From 240aa29a39aafd686cad35da9046df90d2def1e1 Mon Sep 17 00:00:00 2001 From: Jane Xing Date: Tue, 21 Sep 2021 13:35:57 -0500 Subject: [PATCH 001/205] sql: add telemetry for ON UPDATE This commit is to add telemetry for a column created / altered with `ON UPDATE` syntax. Release Note: None --- pkg/sql/create_table.go | 3 +++ pkg/sql/testdata/telemetry/schema | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index 0355a10b0256..bbec941f61c6 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -2842,6 +2842,9 @@ func incTelemetryForNewColumn(def *tree.ColumnTableDef, desc *descpb.ColumnDescr telemetry.Inc(sqltelemetry.SchemaNewColumnTypeQualificationCounter("unique")) } } + if desc.HasOnUpdate() { + telemetry.Inc(sqltelemetry.SchemaNewColumnTypeQualificationCounter("on_update")) + } } func regionalByRowRegionDefaultExpr(oid oid.Oid, region tree.Name) tree.Expr { diff --git a/pkg/sql/testdata/telemetry/schema b/pkg/sql/testdata/telemetry/schema index 3563a15fb29d..df981b056d09 100644 --- a/pkg/sql/testdata/telemetry/schema +++ b/pkg/sql/testdata/telemetry/schema @@ -94,3 +94,30 @@ feature-usage CREATE OR REPLACE VIEW cor_view AS SELECT 1 ---- sql.schema.create_or_replace_view + +feature-allowlist +sql.schema.* +---- + +feature-usage +CREATE TABLE on_update_t (a INT PRIMARY KEY, b INT8 DEFAULT 1 ON UPDATE 2) +---- +sql.schema.create_table +sql.schema.new_column.qualification.default_expr +sql.schema.new_column.qualification.on_update +sql.schema.new_column_type.int8 + +feature-usage +ALTER TABLE on_update_t ADD COLUMN c INT DEFAULT 1 ON UPDATE 2; +---- +sql.schema.alter_table +sql.schema.alter_table.add_column +sql.schema.new_column.qualification.default_expr +sql.schema.new_column.qualification.on_update +sql.schema.new_column_type.int8 + +feature-usage +ALTER TABLE on_update_t ALTER COLUMN b SET ON UPDATE 3 +---- +sql.schema.alter_table +sql.schema.alter_table.set_on_update From d23ccaa795fb965e3c774e76a21c65a1a8e1c1eb Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Thu, 14 Oct 2021 07:57:17 +1100 Subject: [PATCH 002/205] roachtest: disable prometheus for scrub I'm unable to reproduce the error running roachtest myself, and it's the only set of tests affected. Release note: None --- pkg/cmd/roachtest/tests/scrub.go | 5 +++-- pkg/cmd/roachtest/tests/tpcc.go | 23 ++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/roachtest/tests/scrub.go b/pkg/cmd/roachtest/tests/scrub.go index 10798b1643ff..31d562fa3880 100644 --- a/pkg/cmd/roachtest/tests/scrub.go +++ b/pkg/cmd/roachtest/tests/scrub.go @@ -88,8 +88,9 @@ func makeScrubTPCCTest( } return nil }, - Duration: length, - SetupType: usingImport, + DisablePrometheus: true, + Duration: length, + SetupType: usingImport, }) }, } diff --git a/pkg/cmd/roachtest/tests/tpcc.go b/pkg/cmd/roachtest/tests/tpcc.go index 13309959e68a..5ffc88fe7e4e 100644 --- a/pkg/cmd/roachtest/tests/tpcc.go +++ b/pkg/cmd/roachtest/tests/tpcc.go @@ -50,14 +50,17 @@ const ( ) type tpccOptions struct { - Warehouses int - ExtraRunArgs string - ExtraSetupArgs string - Chaos func() Chaos // for late binding of stopper - During func(context.Context) error // for running a function during the test - Duration time.Duration // if zero, TPCC is not invoked - SetupType tpccSetupType + Warehouses int + ExtraRunArgs string + ExtraSetupArgs string + Chaos func() Chaos // for late binding of stopper + During func(context.Context) error // for running a function during the test + Duration time.Duration // if zero, TPCC is not invoked + SetupType tpccSetupType + // PrometheusConfig, if set, overwrites the default prometheus config settings. PrometheusConfig *prometheus.Config + // DisablePrometheus will force prometheus to not start up. + DisablePrometheus bool // WorkloadInstances contains a list of instances for // workloads to run against. // If unset, it will run one workload which talks to @@ -1435,6 +1438,9 @@ func setupPrometheus( if c.IsLocal() { return nil, func() {} } + if opts.DisablePrometheus { + return nil, func() {} + } workloadNode := c.Node(c.Spec().NodeCount) cfg = &prometheus.Config{ PrometheusNode: workloadNode, @@ -1448,6 +1454,9 @@ func setupPrometheus( }, } } + if opts.DisablePrometheus { + t.Fatal("test has PrometheusConfig but DisablePrometheus was on") + } if c.IsLocal() { t.Skip("skipping test as prometheus is needed, but prometheus does not yet work locally") return nil, func() {} From f2b1ca4b37f5301bb68c460a5644b5907e67a0c3 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Wed, 20 Oct 2021 14:33:03 +0100 Subject: [PATCH 003/205] backupccl: remove some used code The baseURI variable was used by a function call that was removed in 28e4cea7ed704acfc2aab2640bcd5dea1dcdbd9f. The TODO still seems relevant. Release note: None --- pkg/ccl/backupccl/backup_planning.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/ccl/backupccl/backup_planning.go b/pkg/ccl/backupccl/backup_planning.go index cf5b83a6521a..65495f6382fe 100644 --- a/pkg/ccl/backupccl/backup_planning.go +++ b/pkg/ccl/backupccl/backup_planning.go @@ -1161,12 +1161,6 @@ func backupPlanHook( if err := checkForPreviousBackup(ctx, defaultStore, defaultURI); err != nil { return err } - // TODO (pbardea): For partitioned backups, also add verification for other - // stores we are writing to in addition to the default. - baseURI := collectionURI - if baseURI == "" { - baseURI = defaultURI - } // Write backup manifest into a temporary checkpoint file. // This accomplishes 2 purposes: @@ -1174,6 +1168,9 @@ func backupPlanHook( // 2. Verifies we can write to destination location. // This temporary checkpoint file gets renamed to real checkpoint // file when the backup jobs starts execution. + // + // TODO (pbardea): For partitioned backups, also add verification for other + // stores we are writing to in addition to the default. doWriteBackupManifestCheckpoint := func(ctx context.Context, jobID jobspb.JobID) error { if err := writeBackupManifest( ctx, p.ExecCfg().Settings, defaultStore, tempCheckpointFileNameForJob(jobID), From 455cdddc6d75c03645f486b22970e5c6198a8d56 Mon Sep 17 00:00:00 2001 From: Alex Lunev Date: Fri, 22 Oct 2021 13:02:40 -0700 Subject: [PATCH 004/205] cli: deflake TestRemoveDeadReplicas Fixes #71789 In 69501 we introduced retry logic to the creation of liveness records. This logic only retries certain errors and was not covering the condition when we encounter OnePCNotAllowedError. This PR moves OnePCNotAllowedError to the kv package to avoid duplicate loops ands adds it to the list of retryable errors for liveness creation and updates. Release note: None --- pkg/cli/debug_test.go | 1 - pkg/kv/kvserver/liveness/liveness.go | 2 + pkg/kv/kvserver/node_liveness_test.go | 111 ++++++++++++++------------ pkg/kv/kvserver/replica_write.go | 13 +-- pkg/kv/txn.go | 10 +++ 5 files changed, 74 insertions(+), 63 deletions(-) diff --git a/pkg/cli/debug_test.go b/pkg/cli/debug_test.go index 5402ac1e41ea..f6f3f255f0c0 100644 --- a/pkg/cli/debug_test.go +++ b/pkg/cli/debug_test.go @@ -132,7 +132,6 @@ func TestOpenReadOnlyStore(t *testing.T) { func TestRemoveDeadReplicas(t *testing.T) { defer leaktest.AfterTest(t)() - skip.WithIssue(t, 71789, "flaky test") defer log.Scope(t).Close(t) // This test is pretty slow under race (200+ cpu-seconds) because it diff --git a/pkg/kv/kvserver/liveness/liveness.go b/pkg/kv/kvserver/liveness/liveness.go index 0d4f9bd0f011..02301a4c74f3 100644 --- a/pkg/kv/kvserver/liveness/liveness.go +++ b/pkg/kv/kvserver/liveness/liveness.go @@ -88,6 +88,8 @@ func isErrRetryLiveness(ctx context.Context, err error) bool { // returned an AmbiguousResultError. // TODO(andrei): Remove this in 22.2. return true + } else if errors.Is(err, kv.OnePCNotAllowedError{}) { + return true } return false } diff --git a/pkg/kv/kvserver/node_liveness_test.go b/pkg/kv/kvserver/node_liveness_test.go index ea674c464815..529410a96fde 100644 --- a/pkg/kv/kvserver/node_liveness_test.go +++ b/pkg/kv/kvserver/node_liveness_test.go @@ -22,6 +22,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/gossip" "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/kv/kvserver" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/liveness" @@ -1059,67 +1060,75 @@ func TestNodeLivenessRetryAmbiguousResultOnCreateError(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) - var injectError atomic.Value - type injectErrorData struct { - shouldError bool - count int32 + errorsToTest := []error{ + roachpb.NewAmbiguousResultError("test"), + roachpb.NewTransactionStatusError(roachpb.TransactionStatusError_REASON_UNKNOWN, "foo"), + kv.OnePCNotAllowedError{}, } - injectError.Store(map[roachpb.NodeID]injectErrorData{ - roachpb.NodeID(1): {true, 0}, - roachpb.NodeID(2): {true, 0}, - roachpb.NodeID(3): {true, 0}, - }) - testingEvalFilter := func(args kvserverbase.FilterArgs) *roachpb.Error { - if req, ok := args.Req.(*roachpb.ConditionalPutRequest); ok { - if val := injectError.Load(); val != nil { - var liveness livenesspb.Liveness - if err := req.Value.GetProto(&liveness); err != nil { - return nil - } - injectErrorMap := val.(map[roachpb.NodeID]injectErrorData) - if injectErrorMap[liveness.NodeID].shouldError { - if liveness.NodeID != 1 { - // We expect this to come from the create code path on all nodes - // except the first. Make sure that is actually true. - assert.Equal(t, liveness.Epoch, int64(0)) + for _, errorToTest := range errorsToTest { + var injectError atomic.Value + type injectErrorData struct { + shouldError bool + count int32 + } + + injectError.Store(map[roachpb.NodeID]injectErrorData{ + roachpb.NodeID(1): {true, 0}, + roachpb.NodeID(2): {true, 0}, + roachpb.NodeID(3): {true, 0}, + }) + testingEvalFilter := func(args kvserverbase.FilterArgs) *roachpb.Error { + if req, ok := args.Req.(*roachpb.ConditionalPutRequest); ok { + if val := injectError.Load(); val != nil { + var liveness livenesspb.Liveness + if err := req.Value.GetProto(&liveness); err != nil { + return nil + } + injectErrorMap := val.(map[roachpb.NodeID]injectErrorData) + if injectErrorMap[liveness.NodeID].shouldError { + if liveness.NodeID != 1 { + // We expect this to come from the create code path on all nodes + // except the first. Make sure that is actually true. + assert.Equal(t, liveness.Epoch, int64(0)) + } + injectErrorMap[liveness.NodeID] = injectErrorData{false, injectErrorMap[liveness.NodeID].count + 1} + injectError.Store(injectErrorMap) + return roachpb.NewError(errorToTest) } - injectErrorMap[liveness.NodeID] = injectErrorData{false, injectErrorMap[liveness.NodeID].count + 1} - injectError.Store(injectErrorMap) - return roachpb.NewError(roachpb.NewAmbiguousResultError("test")) } } + return nil } - return nil - } - ctx := context.Background() - tc := testcluster.StartTestCluster(t, 3, - base.TestClusterArgs{ - ReplicationMode: base.ReplicationManual, - ServerArgs: base.TestServerArgs{ - Knobs: base.TestingKnobs{ - Store: &kvserver.StoreTestingKnobs{ - EvalKnobs: kvserverbase.BatchEvalTestingKnobs{ - TestingEvalFilter: testingEvalFilter, + ctx := context.Background() + tc := testcluster.StartTestCluster(t, 3, + base.TestClusterArgs{ + ReplicationMode: base.ReplicationManual, + ServerArgs: base.TestServerArgs{ + Knobs: base.TestingKnobs{ + Store: &kvserver.StoreTestingKnobs{ + EvalKnobs: kvserverbase.BatchEvalTestingKnobs{ + TestingEvalFilter: testingEvalFilter, + }, }, }, }, - }, - }) - defer tc.Stopper().Stop(ctx) - - for _, s := range tc.Servers { - // Verify retry of the ambiguous result for heartbeat loop. - testutils.SucceedsSoon(t, func() error { - return verifyLivenessServer(s, 3) - }) - nl := s.NodeLiveness().(*liveness.NodeLiveness) - _, ok := nl.Self() - assert.True(t, ok) + }) + defer tc.Stopper().Stop(ctx) - injectErrorMap := injectError.Load().(map[roachpb.NodeID]injectErrorData) - assert.NotNil(t, injectErrorMap) - assert.Equal(t, int32(1), injectErrorMap[s.NodeID()].count) + for _, s := range tc.Servers { + // Verify retry of the ambiguous result for heartbeat loop. + testutils.SucceedsSoon(t, func() error { + return verifyLivenessServer(s, 3) + }) + nl := s.NodeLiveness().(*liveness.NodeLiveness) + _, ok := nl.Self() + assert.True(t, ok) + + injectErrorMap := injectError.Load().(map[roachpb.NodeID]injectErrorData) + assert.NotNil(t, injectErrorMap) + assert.Equal(t, int32(1), injectErrorMap[s.NodeID()].count) + } } } diff --git a/pkg/kv/kvserver/replica_write.go b/pkg/kv/kvserver/replica_write.go index 1e7bbdfa66c1..a2cd4836fad9 100644 --- a/pkg/kv/kvserver/replica_write.go +++ b/pkg/kv/kvserver/replica_write.go @@ -15,6 +15,7 @@ import ( "time" "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency" @@ -400,16 +401,6 @@ func (r *Replica) canAttempt1PCEvaluation( return true } -// OnePCNotAllowedError signifies that a request had the Require1PC flag set, -// but 1PC evaluation was not possible for one reason or another. -type OnePCNotAllowedError struct{} - -var _ error = OnePCNotAllowedError{} - -func (OnePCNotAllowedError) Error() string { - return "could not commit in one phase as requested" -} - // evaluateWriteBatch evaluates the supplied batch. // // If the batch is transactional and has all the hallmarks of a 1PC commit (i.e. @@ -455,7 +446,7 @@ func (r *Replica) evaluateWriteBatch( // terminate this request early. arg, ok := ba.GetArg(roachpb.EndTxn) if ok && arg.(*roachpb.EndTxnRequest).Require1PC { - return nil, enginepb.MVCCStats{}, nil, result.Result{}, roachpb.NewError(OnePCNotAllowedError{}) + return nil, enginepb.MVCCStats{}, nil, result.Result{}, roachpb.NewError(kv.OnePCNotAllowedError{}) } } diff --git a/pkg/kv/txn.go b/pkg/kv/txn.go index 0b9df90f2290..8721aa3d22e5 100644 --- a/pkg/kv/txn.go +++ b/pkg/kv/txn.go @@ -1637,3 +1637,13 @@ func (txn *Txn) AdmissionHeader() roachpb.AdmissionHeader { } return h } + +// OnePCNotAllowedError signifies that a request had the Require1PC flag set, +// but 1PC evaluation was not possible for one reason or another. +type OnePCNotAllowedError struct{} + +var _ error = OnePCNotAllowedError{} + +func (OnePCNotAllowedError) Error() string { + return "could not commit in one phase as requested" +} From 99f4983bde54ddf08ba985227ba2f74add73b25e Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Thu, 21 Oct 2021 14:37:22 -0700 Subject: [PATCH 005/205] sql: ignore last noop processor on gateway when moving single flow When figuring out whether a physical plan consists of flows on multiple nodes, we want to ignore the noop processor that might be planned on the gateway for a sole purpose of propagating the results back to the client (in another words, when we have a flow on the gateway consisting only of a noop processor). Previously, we forgot to do that. Release note: None --- pkg/sql/distsql_physical_planner.go | 11 +++++++++-- pkg/sql/opt/exec/execbuilder/testdata/lookup_join | 8 ++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index 9d4619cadaf9..62614b0f3fad 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -4085,8 +4085,15 @@ func maybeMoveSingleFlowToGateway(planCtx *PlanningCtx, plan *PhysicalPlan, rowC nodeID := plan.Processors[0].Node for _, p := range plan.Processors[1:] { if p.Node != nodeID { - singleFlow = false - break + if p.Node != plan.GatewayNodeID || p.Spec.Core.Noop == nil { + // We want to ignore the noop processors planned on the + // gateway because their job is to simply communicate the + // results back to the client. If, however, there is another + // non-noop processor on the gateway, then we'll correctly + // treat the plan as having multiple flows. + singleFlow = false + break + } } core := p.Spec.Core if core.JoinReader != nil || core.MergeJoiner != nil || core.HashJoiner != nil || diff --git a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join index 50a77d7e56f3..1a8829ca22e5 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join +++ b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join @@ -388,7 +388,7 @@ Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyck1Fv2jAQx9_3K query T EXPLAIN (VERBOSE) SELECT a.name FROM authors AS a JOIN books2 AS b2 ON a.book = b2.title ORDER BY a.name ---- -distribution: full +distribution: local vectorized: true · • project @@ -419,7 +419,7 @@ vectorized: true query T EXPLAIN (VERBOSE) SELECT * FROM books CROSS JOIN books2 ---- -distribution: full +distribution: local vectorized: true · • cross join (inner) @@ -441,7 +441,7 @@ vectorized: true query T EXPLAIN (DISTSQL) SELECT * FROM authors INNER JOIN books2 ON books2.edition = 1 WHERE books2.title = authors.book ---- -distribution: full +distribution: local vectorized: true · • lookup join @@ -457,7 +457,7 @@ vectorized: true table: authors@authors_pkey spans: FULL SCAN · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyUkd-L00AQx9_9K4Z5Upm7a1JPZOFgRSPmqMmZFhSkD3vd4VyvtxN3N6CU_u-S5Iqt0KpvmR-fbz7JbDB-X6PC4vPN7HVZwdO35Xwx_zh7BvNiVrxZwHN419QfwHTpq4QIZVUVDVzXZQW3Ivcxh3r3dM7WJSceriCDT--LptgNkktrhqtdyHnfRkIvlivzwBHVF8yQ8BKXhG2QFccooW9vhqXS_kA1IXS-7VLfXhKuJDCqDQ7ZqLCSM2kvciS0nIxbD2tbQunSbygmc8eoplvaC85OBy_M7ZobNpbDxeQgHh-_R7fBPZjwEwnnrfFRwRkSNuwtBwWZUqqsFq8IdEagczxmlf2P1bU4_yiVHUqN_3zPaSZy37XwTZwH8Qr0lHTP1F1SoHPq6xekL0m_POqWH7j95RQNx1Z85H-6xWS7JGR7x-O5o3RhxTdBVsNrxrIeuKFhOaZxOh2L0o-jXnAfzk7C-Wk4PwlP_oCX2ye_AgAA__98nw4O +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyUkVFr1EAQx9_9FMM8qUzbS05FFgorGjHlTGruQEHysL0d6trrTtzdgHLcd5ckPWyFHr23zH_y-8-PZIvx1wYVFt8uF-_KCp5_KJer5ZfFC1gWi-L9Cl7Cx6b-DKZPPyREKKuqaOCiLiu4ErmJOdT7p1O2LjnxcA4ZfP1UNMV-kVzaMJzvS06HGAm9WK7MLUdU3zHDlrALsuYYJQzRdnyhtL9RzQid7_o0xC3hWgKj2uLYiwpX5mrDDRvL4WyGhJaTcZux9u6k7oK7NeEPEi4746OCEyRs2FsOCjKlVFmt3hLojEDn2O4IpU__LsZkrhlVtqOnW12I83dS2UOp6bPcc1qI3PQd_BTnQbwCPSc9MHWfFOichvkV6dek3zzqlh_jVsmJdGf5A63HiufHFDccO_GRn9Q827WEbK95-t1R-rDmyyDr8cw01iM3BpZjmrbZNJR-Wg2C9-HsIJwfhvOD8Pw_uN09-xsAAP__HEsNaA== #################################### # LOOKUP JOIN ON SECONDARY INDEX # From 25bb2e45b713ded4aabaae56cd6e1a70db5efe0a Mon Sep 17 00:00:00 2001 From: Arjun Nair Date: Tue, 14 Sep 2021 16:56:58 -0400 Subject: [PATCH 006/205] server: share the table cache among stores Possible benefits of sharing the table cache are outlined here: https://github.com/cockroachdb/pebble/issues/1178. This PR creates a table cache which can be shared among many stores. Release note: None --- pkg/server/config.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/server/config.go b/pkg/server/config.go index 0a7ecd62a955..241cfe64cea1 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -15,6 +15,7 @@ import ( "context" "fmt" "net" + "runtime" "strings" "text/tabwriter" "time" @@ -493,6 +494,13 @@ func (cfg *Config) CreateEngines(ctx context.Context) (Engines, error) { log.Event(ctx, "initializing engines") + var tableCache *pebble.TableCache + if physicalStores > 0 { + perStoreLimit := pebble.TableCacheSize(int(openFileLimitPerStore)) + totalFileLimit := perStoreLimit * physicalStores + tableCache = pebble.NewTableCache(pebbleCache, runtime.GOMAXPROCS(0), totalFileLimit) + } + skipSizeCheck := cfg.TestingKnobs.Store != nil && cfg.TestingKnobs.Store.(*kvserver.StoreTestingKnobs).SkipMinSizeCheck disableSeparatedIntents := cfg.TestingKnobs.Store != nil && @@ -579,6 +587,7 @@ func (cfg *Config) CreateEngines(ctx context.Context) (Engines, error) { Opts: storage.DefaultPebbleOptions(), } pebbleConfig.Opts.Cache = pebbleCache + pebbleConfig.Opts.TableCache = tableCache pebbleConfig.Opts.MaxOpenFiles = int(openFileLimitPerStore) // If the spec contains Pebble options, set those too. if len(spec.PebbleOptions) > 0 { @@ -598,6 +607,13 @@ func (cfg *Config) CreateEngines(ctx context.Context) (Engines, error) { } } + if tableCache != nil { + // Unref the table cache now that the engines hold references to it. + if err := tableCache.Unref(); err != nil { + return nil, err + } + } + log.Infof(ctx, "%d storage engine%s initialized", len(engines), util.Pluralize(int64(len(engines)))) for _, s := range details { From e6b5115e483cc0bb7bc674ec044a2346f9178cc9 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Mon, 25 Oct 2021 08:56:42 +1100 Subject: [PATCH 007/205] tree: fix ambiguity with enum overloads Release note (sql change): Previously, certain enum builtins or operators required an explicit enum cast. This has been reduced in some cases. --- pkg/sql/logictest/testdata/logic_test/enums | 31 +++++++++++ pkg/sql/logictest/testdata/logic_test/views | 4 +- pkg/sql/sem/tree/constant.go | 1 + pkg/sql/sem/tree/overload.go | 61 +++++++++++++++++++++ pkg/sql/types/types.go | 4 ++ 5 files changed, 99 insertions(+), 2 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/enums b/pkg/sql/logictest/testdata/logic_test/enums index ed47beb2333e..37ee6f6f7936 100644 --- a/pkg/sql/logictest/testdata/logic_test/enums +++ b/pkg/sql/logictest/testdata/logic_test/enums @@ -1522,9 +1522,32 @@ subtest regression_71388 statement ok CREATE TYPE enum_test AS ENUM ('a', 'b'); +CREATE TABLE enum_table (id SERIAL PRIMARY KEY, elem enum_test); +INSERT INTO enum_table (elem) VALUES ('a'), ('b'); CREATE TABLE enum_array_table (id SERIAL PRIMARY KEY, elems enum_test[]); INSERT INTO enum_array_table (elems) VALUES (array['a']), (array['b']), (array['a', 'b']) +query TB +SELECT + elem, + elem = 'a' +FROM enum_table +ORDER BY id +---- +a true +b false + +query TB +SELECT + elems, + elems = '{a}' +FROM enum_array_table +ORDER BY id +---- +{a} true +{b} false +{a,b} false + query TTBBBB SELECT a.elems, @@ -1546,6 +1569,14 @@ ORDER BY a.id, b.id {a,b} {b} false true true false {a,b} {a,b} true false true true +query B +SELECT '{a,b}'::enum_test[] = '{a,b}' +---- +true + +statement ok +DROP TABLE enum_table + # Make sure that adding a new enum value works well with PREPARE/EXECUTE. subtest regression_70378 diff --git a/pkg/sql/logictest/testdata/logic_test/views b/pkg/sql/logictest/testdata/logic_test/views index 068bc4b211f1..f635c7bb23f9 100644 --- a/pkg/sql/logictest/testdata/logic_test/views +++ b/pkg/sql/logictest/testdata/logic_test/views @@ -1188,7 +1188,7 @@ SELECT * FROM v14 {a,b} statement ok -CREATE VIEW v15 AS (SELECT ('{a, b}'::view_type_new[])[2]) +CREATE VIEW v15 AS (SELECT ('{a, b}'::view_type_new[])[2] AS view_type_new) statement ok ALTER TYPE view_type_new RENAME TO view_type @@ -1196,7 +1196,7 @@ ALTER TYPE view_type_new RENAME TO view_type query TT SHOW CREATE VIEW v15 ---- -v15 CREATE VIEW public.v15 (view_type_new) AS (SELECT ('{a, b}':::STRING::public.view_type[])[2:::INT8]) +v15 CREATE VIEW public.v15 (view_type_new) AS (SELECT ARRAY['a':::public.view_type, 'b':::public.view_type][2:::INT8] AS view_type_new) query T SELECT * FROM v15 diff --git a/pkg/sql/sem/tree/constant.go b/pkg/sql/sem/tree/constant.go index 20e7717726a5..724961b27c22 100644 --- a/pkg/sql/sem/tree/constant.go +++ b/pkg/sql/sem/tree/constant.go @@ -492,6 +492,7 @@ var ( types.Jsonb, types.VarBit, types.AnyEnum, + types.AnyEnumArray, types.INetArray, types.VarBitArray, } diff --git a/pkg/sql/sem/tree/overload.go b/pkg/sql/sem/tree/overload.go index 3e17e4d20f37..56e05d91f7c0 100644 --- a/pkg/sql/sem/tree/overload.go +++ b/pkg/sql/sem/tree/overload.go @@ -801,6 +801,67 @@ func typeCheckOverloadedExprs( } } + // This is a total hack for AnyEnum whilst we don't have postgres type resolution. + // This enables AnyEnum array ops to not need a cast, e.g. array['a']::enum[] = '{a}'. + // If we have one remaining candidate containing AnyEnum, cast all remaining + // arguments to a known enum and check that the rest match. This is a poor man's + // implicit cast / postgres "same argument" resolution clone. + if len(s.overloadIdxs) == 1 { + params := s.overloads[s.overloadIdxs[0]].params() + var knownEnum *types.T + + // Check we have all "AnyEnum" (or "AnyEnum" array) arguments and that + // one argument is typed with an enum. + attemptAnyEnumCast := func() bool { + for i := 0; i < params.Length(); i++ { + typ := params.GetAt(i) + // Note we are deliberately looking at whether the built-in takes in + // AnyEnum as an argument, not the exprs given to the overload itself. + if !(typ.Identical(types.AnyEnum) || typ.Identical(types.MakeArray(types.AnyEnum))) { + return false + } + if s.typedExprs[i] != nil { + // Assign the known enum if it was previously unassigned. + // Otherwise, double check it matches a previously defined enum. + posEnum := s.typedExprs[i].ResolvedType() + if !posEnum.UserDefined() { + return false + } + if posEnum.Family() == types.ArrayFamily { + posEnum = posEnum.ArrayContents() + } + if knownEnum == nil { + knownEnum = posEnum + } else if !posEnum.Identical(knownEnum) { + return false + } + } + } + return knownEnum != nil + }() + + // If we have all arguments as AnyEnum, and we know at least one of the + // enum's actual type, try type cast the rest. + if attemptAnyEnumCast { + // Copy exprs to prevent any overwrites of underlying s.exprs array later. + sCopy := s + sCopy.exprs = make([]Expr, len(s.exprs)) + copy(sCopy.exprs, s.exprs) + if ok, typedExprs, fns, err := filterAttempt(ctx, semaCtx, &sCopy, func() { + for _, idx := range append(s.constIdxs, s.placeholderIdxs...) { + p := params.GetAt(idx) + typCast := knownEnum + if p.Family() == types.ArrayFamily { + typCast = types.MakeArray(knownEnum) + } + sCopy.exprs[idx] = &CastExpr{Expr: sCopy.exprs[idx], Type: typCast, SyntaxMode: CastShort} + } + }); ok { + return typedExprs, fns, err + } + } + } + // In a binary expression, in the case of one of the arguments being untyped NULL, // we prefer overloads where we infer the type of the NULL to be the same as the // other argument. This is used to differentiate the behavior of diff --git a/pkg/sql/types/types.go b/pkg/sql/types/types.go index e7b6f27c37d7..92942a69e8d6 100644 --- a/pkg/sql/types/types.go +++ b/pkg/sql/types/types.go @@ -579,6 +579,10 @@ var ( VarBitArray = &T{InternalType: InternalType{ Family: ArrayFamily, ArrayContents: VarBit, Oid: oid.T__varbit, Locale: &emptyLocale}} + // AnyEnumArray is the type of an array value having AnyEnum-typed elements. + AnyEnumArray = &T{InternalType: InternalType{ + Family: ArrayFamily, ArrayContents: AnyEnum, Oid: oid.T_anyarray, Locale: &emptyLocale}} + // Int2Vector is a type-alias for an array of Int2 values with a different // OID (T_int2vector instead of T__int2). It is a special VECTOR type used // by Postgres in system tables. Int2vectors are 0-indexed, unlike normal arrays. From 85ed80540952c5ba1064ef4a09bcfdfc007f8a2b Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Mon, 25 Oct 2021 00:58:57 -0400 Subject: [PATCH 008/205] roachtest: pass CRDB version to sequelize test The sequelize test suite needs to be told which version it's running against. Release note: None --- pkg/cmd/roachtest/tests/sequelize.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/roachtest/tests/sequelize.go b/pkg/cmd/roachtest/tests/sequelize.go index 15fa9301ccc4..3ffa7a9fd131 100644 --- a/pkg/cmd/roachtest/tests/sequelize.go +++ b/pkg/cmd/roachtest/tests/sequelize.go @@ -12,6 +12,7 @@ package tests import ( "context" + "fmt" "regexp" "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/cluster" @@ -140,7 +141,7 @@ func registerSequelize(r registry.Registry) { // Version telemetry is already disabled in the sequelize-cockroachdb test suite. t.Status("running Sequelize test suite") rawResults, err := c.RunWithBuffer(ctx, t.L(), node, - `cd /mnt/data1/sequelize/ && npm test`, + fmt.Sprintf(`cd /mnt/data1/sequelize/ && CRDB_VERSION=%s npm test`, version), ) rawResultsStr := string(rawResults) t.L().Printf("Test Results: %s", rawResultsStr) From c3e8d8568467809c50a8eb8911fd120fe22661bb Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Sat, 16 Oct 2021 21:18:52 -0400 Subject: [PATCH 009/205] misc: correct spelling mistakes Release note: None --- docs/generated/http/full.md | 12 ++++++------ docs/generated/http/hotranges-request.md | 2 +- docs/generated/swagger/spec.json | 6 +++--- pkg/acceptance/prepare.sh | 2 +- pkg/base/config.go | 2 +- pkg/base/node_id.go | 2 +- pkg/blobs/local_storage.go | 2 +- pkg/cli/cert.go | 2 +- pkg/cli/clierror/check.go | 2 +- pkg/cli/cliflags/flags.go | 2 +- pkg/cli/clisqlcfg/context.go | 2 +- pkg/cli/clisqlclient/conn.go | 2 +- pkg/cli/clisqlexec/format_table.go | 2 +- pkg/cli/clisqlexec/table_display_format.go | 2 +- pkg/cli/clisqlshell/sql.go | 2 +- pkg/cli/democluster/api/api.go | 2 +- pkg/cli/interactive_tests/test_txn_prompt.tcl | 2 +- pkg/cli/node.go | 2 +- pkg/cli/start.go | 4 ++-- pkg/cloud/amazon/s3_storage_test.go | 2 +- pkg/cloud/external_storage.go | 4 ++-- pkg/cloud/impl_registry.go | 2 +- .../userfile/filetable/file_table_read_writer.go | 2 +- pkg/cmd/bazci/watch.go | 2 +- pkg/cmd/cr2pg/main.go | 2 +- pkg/cmd/github-post/main.go | 2 +- pkg/cmd/internal/issues/issues.go | 10 +++++----- pkg/cmd/internal/issues/issues_test.go | 2 +- pkg/cmd/internal/issues/render.go | 4 ++-- pkg/cmd/roachprod/install/cluster_synced.go | 4 ++-- pkg/cmd/roachprod/main.go | 2 +- pkg/cmd/roachprod/vm/gce/gcloud.go | 2 +- pkg/cmd/roachprod/vm/gce/utils.go | 2 +- pkg/cmd/roachprod/vm/vm.go | 2 +- pkg/cmd/roachtest/cluster.go | 2 +- pkg/cmd/roachtest/test_impl.go | 4 ++-- pkg/cmd/roachtest/test_runner.go | 2 +- pkg/cmd/roachtest/tests/cdc.go | 2 +- pkg/cmd/roachtest/tests/rebalance_load.go | 2 +- pkg/cmd/roachtest/tests/restore.go | 4 ++-- pkg/cmd/skip-test/main.go | 2 +- pkg/col/colserde/arrowserde/file.fbs | 2 +- pkg/col/colserde/arrowserde/file_generated.go | 4 ++-- pkg/col/colserde/arrowserde/tensor.fbs | 4 ++-- pkg/col/colserde/arrowserde/tensor_generated.go | 6 +++--- pkg/config/zonepb/zone.pb.go | 2 +- pkg/config/zonepb/zone.proto | 2 +- pkg/geo/bbox.go | 2 +- pkg/geo/geo.go | 2 +- pkg/geo/geogfn/best_projection.go | 2 +- pkg/geo/geogfn/unary_operators.go | 2 +- pkg/geo/geoindex/geoindex.go | 2 +- pkg/geo/geomfn/line_crossing_direction.go | 4 ++-- pkg/geo/geomfn/linestring.go | 2 +- pkg/geo/geomfn/node.go | 2 +- pkg/geo/geosegmentize/geosegmentize.go | 2 +- pkg/geo/hilbert.go | 2 +- pkg/internal/rsg/rsg.go | 2 +- pkg/internal/sqlsmith/alter.go | 2 +- pkg/internal/team/team.go | 2 +- pkg/jobs/jobs.go | 2 +- pkg/jobs/jobs_test.go | 4 ++-- pkg/jobs/jobspb/jobs.pb.go | 8 ++++---- pkg/jobs/jobspb/jobs.proto | 8 ++++---- pkg/jobs/registry.go | 8 ++++---- pkg/keys/keys_test.go | 2 +- pkg/multitenant/tenantcostmodel/model.go | 2 +- pkg/roachpb/api.go | 4 ++-- pkg/roachpb/api.pb.go | 4 ++-- pkg/roachpb/api.proto | 4 ++-- pkg/roachpb/data.pb.go | 6 +++--- pkg/roachpb/data.proto | 6 +++--- pkg/roachpb/errors.pb.go | 2 +- pkg/roachpb/errors.proto | 2 +- pkg/roachpb/errors_test.go | 2 +- pkg/roachpb/io-formats.pb.go | 4 ++-- pkg/roachpb/io-formats.proto | 4 ++-- pkg/roachpb/metadata.go | 2 +- pkg/roachpb/metadata.pb.go | 2 +- pkg/roachpb/metadata.proto | 2 +- pkg/rpc/auth_tenant.go | 2 +- pkg/rpc/context_test.go | 2 +- pkg/rpc/metrics.go | 2 +- pkg/rpc/nodedialer/nodedialer.go | 2 +- pkg/security/auto_tls_init.go | 2 +- pkg/security/certificate_loader.go | 2 +- pkg/security/certificate_manager.go | 4 ++-- pkg/security/tls.go | 2 +- pkg/security/username.go | 2 +- pkg/server/admin.go | 2 +- pkg/server/api_v2_auth.go | 2 +- pkg/server/auto_tls_init.go | 4 ++-- pkg/server/config.go | 2 +- pkg/server/heapprofiler/profilestore_test.go | 2 +- pkg/server/serverpb/admin.pb.go | 6 +++--- pkg/server/serverpb/admin.proto | 6 +++--- pkg/server/serverpb/status.pb.go | 4 ++-- pkg/server/serverpb/status.proto | 4 ++-- pkg/server/stats_test.go | 2 +- pkg/server/status.go | 2 +- pkg/server/telemetry/features.go | 2 +- pkg/server/testserver.go | 2 +- pkg/settings/settings_test.go | 8 ++++---- pkg/storage/engine.go | 4 ++-- pkg/storage/enginepb/mvcc3.pb.go | 2 +- pkg/storage/enginepb/mvcc3.proto | 2 +- pkg/storage/fs/temp_dir.go | 2 +- pkg/storage/mvcc.go | 15 +++++++-------- pkg/storage/mvcc_incremental_iterator.go | 2 +- pkg/storage/mvcc_incremental_iterator_test.go | 2 +- pkg/storage/mvcc_test.go | 4 ++-- pkg/storage/open.go | 2 +- pkg/storage/pebble.go | 2 +- pkg/storage/pebble_iterator.go | 2 +- pkg/testutils/floatcmp/floatcmp.go | 2 +- pkg/testutils/jobutils/jobs_verification.go | 2 +- .../lint/passes/forbiddenmethod/analyzers.go | 2 +- pkg/testutils/lint/passes/nilness/nilness.go | 2 +- .../lint/passes/passesutil/passes_util.go | 2 +- pkg/testutils/net.go | 2 +- pkg/testutils/pgtest/datadriven.go | 2 +- pkg/ts/catalog/catalog_generator.go | 2 +- pkg/ts/catalog/chart_catalog.pb.go | 2 +- pkg/ts/catalog/chart_catalog.proto | 2 +- pkg/ts/db.go | 2 +- pkg/ts/db_test.go | 4 ++-- pkg/ts/iterator_test.go | 10 +++++----- pkg/ts/keys.go | 2 +- pkg/ts/query.go | 4 ++-- pkg/workload/csv.go | 2 +- pkg/workload/tpcc/checks.go | 2 +- pkg/workload/tpcc/tpcc.go | 2 +- pkg/workload/tpccchecks/checks_generator.go | 2 +- pkg/workload/ycsb/acknowledged_counter.go | 2 +- pkg/workload/ycsb/ycsb.go | 2 +- 135 files changed, 201 insertions(+), 202 deletions(-) diff --git a/docs/generated/http/full.md b/docs/generated/http/full.md index 010d06152fe2..bec08f3d289e 100644 --- a/docs/generated/http/full.md +++ b/docs/generated/http/full.md @@ -2389,7 +2389,7 @@ Support status: [reserved](#support-status) | ----- | ---- | ----- | ----------- | -------------- | | node_id | [string](#cockroach.server.serverpb.CancelSessionRequest-string) | | TODO(abhimadan): use [(gogoproto.customname) = "NodeID"] below. Need to figure out how to teach grpc-gateway about custom names.

node_id is a string so that "local" can be used to specify that no forwarding is necessary. | [reserved](#support-status) | | session_id | [bytes](#cockroach.server.serverpb.CancelSessionRequest-bytes) | | | [reserved](#support-status) | -| username | [string](#cockroach.server.serverpb.CancelSessionRequest-string) | | Username of the user making this cancellation request. This may be omitted if the user is the same as the one issuing the CancelSessionRequest. The caller is responsiblef or case-folding and NFC normalization. | [reserved](#support-status) | +| username | [string](#cockroach.server.serverpb.CancelSessionRequest-string) | | Username of the user making this cancellation request. This may be omitted if the user is the same as the one issuing the CancelSessionRequest. The caller is responsible for case-folding and NFC normalization. | [reserved](#support-status) | @@ -2436,7 +2436,7 @@ Support status: [reserved](#support-status) | ----- | ---- | ----- | ----------- | -------------- | | node_id | [string](#cockroach.server.serverpb.CancelSessionRequest-string) | | TODO(abhimadan): use [(gogoproto.customname) = "NodeID"] below. Need to figure out how to teach grpc-gateway about custom names.

node_id is a string so that "local" can be used to specify that no forwarding is necessary. | [reserved](#support-status) | | session_id | [bytes](#cockroach.server.serverpb.CancelSessionRequest-bytes) | | | [reserved](#support-status) | -| username | [string](#cockroach.server.serverpb.CancelSessionRequest-string) | | Username of the user making this cancellation request. This may be omitted if the user is the same as the one issuing the CancelSessionRequest. The caller is responsiblef or case-folding and NFC normalization. | [reserved](#support-status) | +| username | [string](#cockroach.server.serverpb.CancelSessionRequest-string) | | Username of the user making this cancellation request. This may be omitted if the user is the same as the one issuing the CancelSessionRequest. The caller is responsible for case-folding and NFC normalization. | [reserved](#support-status) | @@ -2956,7 +2956,7 @@ of ranges currently considered “hot” by the node(s). | Field | Type | Label | Description | Support status | | ----- | ---- | ----- | ----------- | -------------- | -| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is posssible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.

If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) | +| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is possible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.

If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) | @@ -3986,7 +3986,7 @@ sharedSecret. -CertBundleResponse contains a copy of all CAs needed to intialize TLS for +CertBundleResponse contains a copy of all CAs needed to initialize TLS for a new node. @@ -4231,7 +4231,7 @@ a table. | grants | [TableDetailsResponse.Grant](#cockroach.server.serverpb.TableDetailsResponse-cockroach.server.serverpb.TableDetailsResponse.Grant) | repeated | | [reserved](#support-status) | | columns | [TableDetailsResponse.Column](#cockroach.server.serverpb.TableDetailsResponse-cockroach.server.serverpb.TableDetailsResponse.Column) | repeated | | [reserved](#support-status) | | indexes | [TableDetailsResponse.Index](#cockroach.server.serverpb.TableDetailsResponse-cockroach.server.serverpb.TableDetailsResponse.Index) | repeated | | [reserved](#support-status) | -| range_count | [int64](#cockroach.server.serverpb.TableDetailsResponse-int64) | | range_count is the size of the table in ranges. This provides a rough estimate of the storage requirements for the table. TODO(mrtracy): The TableStats method also returns a range_count field which is more accurate than this one; TableDetails calculates this number using a potentially faster method that is subject to cache staleness. We should consider removing or renaming this field to reflect that difference. See Github issue #5435 for more information. | [reserved](#support-status) | +| range_count | [int64](#cockroach.server.serverpb.TableDetailsResponse-int64) | | range_count is the size of the table in ranges. This provides a rough estimate of the storage requirements for the table. TODO(mrtracy): The TableStats method also returns a range_count field which is more accurate than this one; TableDetails calculates this number using a potentially faster method that is subject to cache staleness. We should consider removing or renaming this field to reflect that difference. See GitHub issue #5435 for more information. | [reserved](#support-status) | | create_table_statement | [string](#cockroach.server.serverpb.TableDetailsResponse-string) | | create_table_statement is the output of "SHOW CREATE" for this table; it is a SQL statement that would re-create the table's current schema if executed. | [reserved](#support-status) | | zone_config | [cockroach.config.zonepb.ZoneConfig](#cockroach.server.serverpb.TableDetailsResponse-cockroach.config.zonepb.ZoneConfig) | | The zone configuration in effect for this table. | [reserved](#support-status) | | zone_config_level | [ZoneConfigurationLevel](#cockroach.server.serverpb.TableDetailsResponse-cockroach.server.serverpb.ZoneConfigurationLevel) | | The level at which this object's zone configuration is set. | [reserved](#support-status) | @@ -5577,7 +5577,7 @@ MetricMetadataRequest requests metadata for all metrics. -MetricMetadataResponse contains the metadata for all metics. +MetricMetadataResponse contains the metadata for all metrics. | Field | Type | Label | Description | Support status | diff --git a/docs/generated/http/hotranges-request.md b/docs/generated/http/hotranges-request.md index 7ed2e9c8fde8..6c542161d115 100644 --- a/docs/generated/http/hotranges-request.md +++ b/docs/generated/http/hotranges-request.md @@ -11,6 +11,6 @@ Support status: [alpha](#support-status) | Field | Type | Label | Description | Support status | | ----- | ---- | ----- | ----------- | -------------- | -| node_id | [string](#string) | | NodeID indicates which node to query for a hot range report. It is posssible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.

If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) | +| node_id | [string](#string) | | NodeID indicates which node to query for a hot range report. It is possible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.

If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) | diff --git a/docs/generated/swagger/spec.json b/docs/generated/swagger/spec.json index 24881f59d395..5d624f088aed 100644 --- a/docs/generated/swagger/spec.json +++ b/docs/generated/swagger/spec.json @@ -1202,7 +1202,7 @@ "x-go-name": "Indexes" }, "range_count": { - "description": "range_count is the size of the table in ranges. This provides a rough\nestimate of the storage requirements for the table.\nTODO(mrtracy): The TableStats method also returns a range_count field which\nis more accurate than this one; TableDetails calculates this number using\na potentially faster method that is subject to cache staleness. We should\nconsider removing or renaming this field to reflect that difference. See\nGithub issue #5435 for more information.", + "description": "range_count is the size of the table in ranges. This provides a rough\nestimate of the storage requirements for the table.\nTODO(mrtracy): The TableStats method also returns a range_count field which\nis more accurate than this one; TableDetails calculates this number using\na potentially faster method that is subject to cache staleness. We should\nconsider removing or renaming this field to reflect that difference. See\nGitHub issue #5435 for more information.", "type": "integer", "format": "int64", "x-go-name": "RangeCount" @@ -1529,7 +1529,7 @@ "x-go-name": "GlobalReads" }, "inherited_constraints": { - "description": "InheritedContraints specifies if the value in the Constraints field was\ninherited from the zone's parent or specified explicitly by the user.\n\nNB: We need this extra field because the non-nullable nature of\n`constraints` means that there is no other way to disambiguate between an\nunset `constraints` attribute and an empty one.", + "description": "InheritedConstraints specifies if the value in the Constraints field was\ninherited from the zone's parent or specified explicitly by the user.\n\nNB: We need this extra field because the non-nullable nature of\n`constraints` means that there is no other way to disambiguate between an\nunset `constraints` attribute and an empty one.", "type": "boolean", "x-go-name": "InheritedConstraints" }, @@ -1775,7 +1775,7 @@ "type": "object", "properties": { "logged_out": { - "description": "Indicates whether logout was succeessful.", + "description": "Indicates whether logout was successful.", "type": "boolean", "x-go-name": "LoggedOut" } diff --git a/pkg/acceptance/prepare.sh b/pkg/acceptance/prepare.sh index 3f3855327cb6..60b40bedaaa7 100755 --- a/pkg/acceptance/prepare.sh +++ b/pkg/acceptance/prepare.sh @@ -6,6 +6,6 @@ set -euxo pipefail rm -f cockroach-linux-2.6.32-gnu-amd64 pkg/acceptance/acceptance.test # We must make a release build here because the binary needs to work in both -# the builder image and the postgres-test image, which have different libstc++ +# the builder image and the postgres-test image, which have different libstdc++ # versions. build/builder.sh mkrelease linux-gnu diff --git a/pkg/base/config.go b/pkg/base/config.go index 861e1ed955c3..77804983fd70 100644 --- a/pkg/base/config.go +++ b/pkg/base/config.go @@ -309,7 +309,7 @@ type RaftConfig struct { // duration should be of the range lease active time. For example, with a // value of 0.2 and a lease duration of 10 seconds, leases would be eagerly // renewed 8 seconds into each lease. A value of zero means use the default - // and a value of -1 means never pre-emptively renew the lease. A value of 1 + // and a value of -1 means never preemptively renew the lease. A value of 1 // means always renew. RangeLeaseRenewalFraction float64 diff --git a/pkg/base/node_id.go b/pkg/base/node_id.go index 8a619596a98a..a9fbf0757733 100644 --- a/pkg/base/node_id.go +++ b/pkg/base/node_id.go @@ -210,7 +210,7 @@ func (c *SQLIDContainer) OptionalNodeID() (roachpb.NodeID, bool) { } // OptionalNodeIDErr is like OptionalNodeID, but returns an error (referring to -// the optionally supplied Github issues) if the ID is not present. +// the optionally supplied GitHub issues) if the ID is not present. func (c *SQLIDContainer) OptionalNodeIDErr(issue int) (roachpb.NodeID, error) { v, err := c.w.OptionalErr(issue) if err != nil { diff --git a/pkg/blobs/local_storage.go b/pkg/blobs/local_storage.go index b2d04c78c464..286564300115 100644 --- a/pkg/blobs/local_storage.go +++ b/pkg/blobs/local_storage.go @@ -33,7 +33,7 @@ type LocalStorage struct { // an error when we cannot take the absolute path of `externalIODir`. func NewLocalStorage(externalIODir string) (*LocalStorage, error) { // An empty externalIODir indicates external IO is completely disabled. - // Returning a nil *LocalStorage in this case and then hanldling `nil` in the + // Returning a nil *LocalStorage in this case and then handling `nil` in the // prependExternalIODir helper ensures that that is respected throughout the // implementation (as a failure to do so would likely fail loudly with a // nil-pointer dereference). diff --git a/pkg/cli/cert.go b/pkg/cli/cert.go index 5d7de5e7b4c8..9639c40271b2 100644 --- a/pkg/cli/cert.go +++ b/pkg/cli/cert.go @@ -126,7 +126,7 @@ Creation fails if the CA expiration time is before the desired certificate expir // runCreateNodeCert generates key pair and CA certificate and writes them // to their corresponding files. // TODO(marc): there is currently no way to specify which CA cert to use if more -// than one is present. We shoult try to load each certificate along with the key +// than one is present. We should try to load each certificate along with the key // and pick the one that works. That way, the key specifies the certificate. func runCreateNodeCert(cmd *cobra.Command, args []string) error { return errors.Wrap( diff --git a/pkg/cli/clierror/check.go b/pkg/cli/clierror/check.go index 2adb4fd46d3b..798f5d464e0e 100644 --- a/pkg/cli/clierror/check.go +++ b/pkg/cli/clierror/check.go @@ -17,7 +17,7 @@ import ( "github.com/cockroachdb/errors" ) -// CheckAndMaybeLog reports the error, if non-nil, to the givven +// CheckAndMaybeLog reports the error, if non-nil, to the given // logger. func CheckAndMaybeLog( err error, logger func(context.Context, logpb.Severity, string, ...interface{}), diff --git a/pkg/cli/cliflags/flags.go b/pkg/cli/cliflags/flags.go index 3110fea5281e..0d238f8e1338 100644 --- a/pkg/cli/cliflags/flags.go +++ b/pkg/cli/cliflags/flags.go @@ -582,7 +582,7 @@ apply. This flag is experimental. Name: "locality-advertise-addr", Description: ` List of ports to advertise to other CockroachDB nodes for intra-cluster -communication for some locality. This should be specified as a commma +communication for some locality. This should be specified as a comma separated list of locality@address. Addresses can also include ports. For example:
diff --git a/pkg/cli/clisqlcfg/context.go b/pkg/cli/clisqlcfg/context.go
index c1d0b21ec3f3..ca2d0e54efac 100644
--- a/pkg/cli/clisqlcfg/context.go
+++ b/pkg/cli/clisqlcfg/context.go
@@ -137,7 +137,7 @@ func (c *Context) getInputFile(defaultIn *os.File) (cmdIn *os.File, closeFn func
 // checkInteractive sets the isInteractive parameter depending on the
 // execution environment and the presence of -e flags.
 func (c *Context) checkInteractive() {
-	// We don't consider sessions interactives unless we have a
+	// We don't consider sessions interactive unless we have a
 	// serious hunch they are. For now, only `cockroach sql` *without*
 	// `-e` has the ability to input from a (presumably) human user,
 	// and we'll also assume that there is no human if the standard
diff --git a/pkg/cli/clisqlclient/conn.go b/pkg/cli/clisqlclient/conn.go
index db93a3fde58b..efd4ab2aae94 100644
--- a/pkg/cli/clisqlclient/conn.go
+++ b/pkg/cli/clisqlclient/conn.go
@@ -155,7 +155,7 @@ func (c *sqlConn) EnsureConn() error {
 		// connections only once instead. The context is only used for dialing.
 		conn, err := connector.Connect(context.TODO())
 		if err != nil {
-			// Connection failed: if the failure is due to a mispresented
+			// Connection failed: if the failure is due to a missing
 			// password, we're going to fill the password here.
 			//
 			// TODO(knz): CockroachDB servers do not properly fill SQLSTATE
diff --git a/pkg/cli/clisqlexec/format_table.go b/pkg/cli/clisqlexec/format_table.go
index 466228be1c54..c7a9f8a2f474 100644
--- a/pkg/cli/clisqlexec/format_table.go
+++ b/pkg/cli/clisqlexec/format_table.go
@@ -189,7 +189,7 @@ func render(
 			err = errors.WithSecondaryError(err, r.describe(w, cols))
 		}
 
-		// completedHook, if provided, is called unconditonally of error.
+		// completedHook, if provided, is called unconditionally of error.
 		if completedHook != nil {
 			completedHook()
 		}
diff --git a/pkg/cli/clisqlexec/table_display_format.go b/pkg/cli/clisqlexec/table_display_format.go
index 4917d17ce6f8..aac5ae5f88c1 100644
--- a/pkg/cli/clisqlexec/table_display_format.go
+++ b/pkg/cli/clisqlexec/table_display_format.go
@@ -39,7 +39,7 @@ const (
 	// the creation of a SQL table containing the result values.
 	TableDisplaySQL
 	// TableDisplayHTML reports the results using a HTML table.  HTML
-	// special characters inside the values are escapde.
+	// special characters inside the values are escaped.
 	TableDisplayHTML
 	// TableDisplayRawHTML is a variant of the HTML output format
 	// supported specifically to generate CockroachDB's documentation.
diff --git a/pkg/cli/clisqlshell/sql.go b/pkg/cli/clisqlshell/sql.go
index 733701f4f0b6..6e05585f9b5f 100644
--- a/pkg/cli/clisqlshell/sql.go
+++ b/pkg/cli/clisqlshell/sql.go
@@ -1937,7 +1937,7 @@ func (c *cliState) serverSideParse(sql string) (helpText string, err error) {
 		// the constant string parser.helpHintPrefix.
 		//
 		// However, we cannot include the 'parser' package here because it
-		// would incur a hughe dependency overhead.
+		// would incur a huge dependency overhead.
 		if strings.HasPrefix(message, "help token in input") && strings.HasPrefix(hint, "help:") {
 			// Yes: return it.
 			helpText = hint[6:]
diff --git a/pkg/cli/democluster/api/api.go b/pkg/cli/democluster/api/api.go
index 6d79816b2243..b8acc51d819c 100644
--- a/pkg/cli/democluster/api/api.go
+++ b/pkg/cli/democluster/api/api.go
@@ -22,7 +22,7 @@ import (
 type DemoCluster interface {
 	// ListDemoNodes produces a listing of servers on the specified
 	// writer. If justOne is specified, only the first node is listed.
-	// Listing is printed to 'w'. Errors/wranings are printed to 'ew'.
+	// Listing is printed to 'w'. Errors/warnings are printed to 'ew'.
 	ListDemoNodes(w, ew io.Writer, justOne bool)
 
 	// AddNode creates a new node with the given locality string.
diff --git a/pkg/cli/interactive_tests/test_txn_prompt.tcl b/pkg/cli/interactive_tests/test_txn_prompt.tcl
index af0382c94bac..8a6763779628 100644
--- a/pkg/cli/interactive_tests/test_txn_prompt.tcl
+++ b/pkg/cli/interactive_tests/test_txn_prompt.tcl
@@ -41,7 +41,7 @@ eexpect abcrootdef
 send "\\set prompt1 abc%/def\r"
 eexpect abcdefaultdbdef
 
-# Check promptw with no formatting code.
+# Check prompt with no formatting code.
 send "\\set prompt1 woo \r"
 eexpect woo
 
diff --git a/pkg/cli/node.go b/pkg/cli/node.go
index 9669fd32afa0..736d3da51f99 100644
--- a/pkg/cli/node.go
+++ b/pkg/cli/node.go
@@ -346,7 +346,7 @@ func runDecommissionNode(cmd *cobra.Command, args []string) error {
 	if err := runDecommissionNodeImpl(ctx, c, nodeCtx.nodeDecommissionWait, nodeIDs); err != nil {
 		cause := errors.UnwrapAll(err)
 		if s, ok := status.FromError(cause); ok && s.Code() == codes.NotFound {
-			// Are we trying to decommision a node that does not
+			// Are we trying to decommission a node that does not
 			// exist? See Server.Decommission for where this specific grpc error
 			// code is generated.
 			return errors.New("node does not exist")
diff --git a/pkg/cli/start.go b/pkg/cli/start.go
index 4235f7dd0cba..6d1e1eacdfc9 100644
--- a/pkg/cli/start.go
+++ b/pkg/cli/start.go
@@ -447,7 +447,7 @@ func runStart(cmd *cobra.Command, args []string, startSingleNode bool) (returnEr
 
 	// Tweak GOMAXPROCS if we're in a cgroup / container that has cpu limits set.
 	// The GO default for GOMAXPROCS is NumCPU(), however this is less
-	// than ideal if the cgruop is limited to a number lower than that.
+	// than ideal if the cgroup is limited to a number lower than that.
 	//
 	// TODO(bilal): various global settings have already been initialized based on
 	// GOMAXPROCS(0) by now.
@@ -556,7 +556,7 @@ func runStart(cmd *cobra.Command, args []string, startSingleNode bool) (returnEr
 		}
 	}
 
-	// DelayedBoostrapFn will be called if the boostrap process is
+	// DelayedBootstrapFn will be called if the bootstrap process is
 	// taking a bit long.
 	serverCfg.DelayedBootstrapFn = func() {
 		const msg = `The server appears to be unable to contact the other nodes in the cluster. Please try:
diff --git a/pkg/cloud/amazon/s3_storage_test.go b/pkg/cloud/amazon/s3_storage_test.go
index 03f25526dac3..98d167055b7c 100644
--- a/pkg/cloud/amazon/s3_storage_test.go
+++ b/pkg/cloud/amazon/s3_storage_test.go
@@ -303,7 +303,7 @@ func TestS3BucketDoesNotExist(t *testing.T) {
 func TestAntagonisticS3Read(t *testing.T) {
 	defer leaktest.AfterTest(t)()
 
-	// Check if we can create aws session with implicit crendentials.
+	// Check if we can create aws session with implicit credentials.
 	_, err := session.NewSession()
 	if err != nil {
 		skip.IgnoreLint(t, "No AWS credentials")
diff --git a/pkg/cloud/external_storage.go b/pkg/cloud/external_storage.go
index afc70ace60ac..d90057dc74cc 100644
--- a/pkg/cloud/external_storage.go
+++ b/pkg/cloud/external_storage.go
@@ -70,7 +70,7 @@ type ExternalStorage interface {
 	// A Writer *must* be closed via either Close, and if closing returns a
 	// non-nil error, that error should be handled or reported to the user -- an
 	// implementation may buffer written data until Close and only then return
-	// an error, or Write may retrun an opaque io.EOF with the underlying cause
+	// an error, or Write may return an opaque io.EOF with the underlying cause
 	// returned by the subsequent Close().
 	Writer(ctx context.Context, basename string) (io.WriteCloser, error)
 
@@ -148,7 +148,7 @@ type ExternalStorageContext struct {
 }
 
 // ExternalStorageConstructor is a function registered to create instances
-// of a given external storage implamentation.
+// of a given external storage implementation.
 type ExternalStorageConstructor func(
 	context.Context, ExternalStorageContext, roachpb.ExternalStorage,
 ) (ExternalStorage, error)
diff --git a/pkg/cloud/impl_registry.go b/pkg/cloud/impl_registry.go
index dbaead5728fd..95c5610d8433 100644
--- a/pkg/cloud/impl_registry.go
+++ b/pkg/cloud/impl_registry.go
@@ -100,7 +100,7 @@ func ExternalStorageFromURI(
 // protection from shoulder-surfing. The param is still present -- just
 // redacted -- to make it clearer that that value is indeed persisted interally.
 // extraParams which should be scrubbed -- for params beyond those that the
-// various clound-storage URIs supported by this package know about -- can be
+// various cloud-storage URIs supported by this package know about -- can be
 // passed allowing this function to be used to scrub other URIs too (such as
 // non-cloudstorage changefeed sinks).
 func SanitizeExternalStorageURI(path string, extraParams []string) (string, error) {
diff --git a/pkg/cloud/userfile/filetable/file_table_read_writer.go b/pkg/cloud/userfile/filetable/file_table_read_writer.go
index 89a4d16fc572..9bc0356afb6d 100644
--- a/pkg/cloud/userfile/filetable/file_table_read_writer.go
+++ b/pkg/cloud/userfile/filetable/file_table_read_writer.go
@@ -242,7 +242,7 @@ func NewFileToTableSystem(
 	username security.SQLUsername,
 ) (*FileToTableSystem, error) {
 	// Check the qualifiedTableName is parseable, so that we can return a useful
-	// error pre-emptively.
+	// error preemptively.
 	_, err := parser.ParseQualifiedTableName(qualifiedTableName)
 	if err != nil {
 		return nil, errors.Wrapf(err, "unable to parse qualified table name %s supplied to userfile",
diff --git a/pkg/cmd/bazci/watch.go b/pkg/cmd/bazci/watch.go
index dd8633326a0d..a11736f6402c 100644
--- a/pkg/cmd/bazci/watch.go
+++ b/pkg/cmd/bazci/watch.go
@@ -352,7 +352,7 @@ func (w *cancelableWriter) Close() error {
 // we're not finalizing, especially if we read an artifact while it's in the
 // process of being written.)
 //
-// In the intialCachingPhase, NO artifacts will be staged, but
+// In the initialCachingPhase, NO artifacts will be staged, but
 // maybeStageArtifact will still stat the source file and cache its metadata.
 // This is important because Bazel aggressively caches build and test artifacts,
 // so just because a file exists, doesn't necessarily mean that it should be
diff --git a/pkg/cmd/cr2pg/main.go b/pkg/cmd/cr2pg/main.go
index fa95e9940f2a..cdf40d60c6de 100644
--- a/pkg/cmd/cr2pg/main.go
+++ b/pkg/cmd/cr2pg/main.go
@@ -89,7 +89,7 @@ func main() {
 								def.Columns[i].Direction = tree.DefaultDirection
 							}
 							// Unset Name here because
-							// constaint names cannot
+							// constraint names cannot
 							// be shared among tables,
 							// so multiple PK constraints
 							// named "primary" is an error.
diff --git a/pkg/cmd/github-post/main.go b/pkg/cmd/github-post/main.go
index 4dc69281a1eb..dcfeedf0c1fc 100644
--- a/pkg/cmd/github-post/main.go
+++ b/pkg/cmd/github-post/main.go
@@ -154,7 +154,7 @@ func listFailures(
 ) error {
 	// Tests that took less than this are not even considered for slow test
 	// reporting. This is so that we protect against large number of
-	// programatically-generated subtests.
+	// programmatically-generated subtests.
 	const shortTestFilterSecs float64 = 0.5
 	var timeoutMsg = "panic: test timed out after"
 
diff --git a/pkg/cmd/internal/issues/issues.go b/pkg/cmd/internal/issues/issues.go
index f065e2d2ff5d..b5c5560d4c2f 100644
--- a/pkg/cmd/internal/issues/issues.go
+++ b/pkg/cmd/internal/issues/issues.go
@@ -191,7 +191,7 @@ func newPoster(client *github.Client, opts *Options) *poster {
 
 // Options configures the issue poster.
 type Options struct {
-	Token        string // Github API token
+	Token        string // GitHub API token
 	Org          string
 	Repo         string
 	SHA          string
@@ -272,7 +272,7 @@ type TemplateData struct {
 	CondensedMessage CondensedMessage
 	// The commit SHA.
 	Commit string
-	// Link to the commit on Github.
+	// Link to the commit on GitHub.
 	CommitURL string
 	// The branch.
 	Branch string
@@ -475,11 +475,11 @@ type PostRequest struct {
 	// A path to the test artifacts relative to the artifacts root. If nonempty,
 	// allows the poster formatter to construct a direct URL to this directory.
 	Artifacts string
-	// The email of the author. It will be translated into a Github handle and
+	// The email of the author. It will be translated into a GitHub handle and
 	// appended to the Mention slice below. This increases the chances of the
 	// "right person" seeing the failure early.
 	AuthorEmail string
-	// Mention is a slice of Github handles (@foo, @cockroachdb/some-team, etc)
+	// Mention is a slice of GitHub handles (@foo, @cockroachdb/some-team, etc)
 	// that should be mentioned in the message.
 	Mention []string
 	// The instructions to reproduce the failure.
@@ -495,7 +495,7 @@ type PostRequest struct {
 }
 
 // Post either creates a new issue for a failed test, or posts a comment to an
-// existing open issue. GITHUB_API_TOKEN must be set to a valid Github token
+// existing open issue. GITHUB_API_TOKEN must be set to a valid GitHub token
 // that has permissions to search and create issues and comments or an error
 // will be returned.
 func Post(ctx context.Context, formatter IssueFormatter, req PostRequest) error {
diff --git a/pkg/cmd/internal/issues/issues_test.go b/pkg/cmd/internal/issues/issues_test.go
index f220e9825c52..a62d86599427 100644
--- a/pkg/cmd/internal/issues/issues_test.go
+++ b/pkg/cmd/internal/issues/issues_test.go
@@ -28,7 +28,7 @@ import (
 
 func TestPost(t *testing.T) {
 	const (
-		assignee    = "hodor" // fake Github handle we're returning as assignee
+		assignee    = "hodor" // fake GitHub handle we're returning as assignee
 		milestone   = 2       // fake milestone we're using here
 		issueID     = 1337    // issue ID returned in select test cases
 		issueNumber = 30      // issue # returned in select test cases
diff --git a/pkg/cmd/internal/issues/render.go b/pkg/cmd/internal/issues/render.go
index ba20ba6a12d6..7be1e12eaccb 100644
--- a/pkg/cmd/internal/issues/render.go
+++ b/pkg/cmd/internal/issues/render.go
@@ -18,7 +18,7 @@ import (
 )
 
 // An IssueFormatter turns TemplateData for a test failure into markdown
-// that can form a Github issue comment.
+// that can form a GitHub issue comment.
 type IssueFormatter struct {
 	Title func(TemplateData) string
 	Body  func(*Renderer, TemplateData) error
@@ -77,7 +77,7 @@ func (r *Renderer) CodeBlock(typ string, txt string) {
 	r.nl()
 	// NB: the leading newline may be spurious, but quotes
 	// always need to be preceded by a blank line, or at
-	// least Github doesn't interpret the ``` right. The
+	// least GitHub doesn't interpret the ``` right. The
 	// below will misbehave, we need a blank line after `

`. // //

foo diff --git a/pkg/cmd/roachprod/install/cluster_synced.go b/pkg/cmd/roachprod/install/cluster_synced.go index 8bbc5ad7ae1f..a260097a927e 100644 --- a/pkg/cmd/roachprod/install/cluster_synced.go +++ b/pkg/cmd/roachprod/install/cluster_synced.go @@ -598,7 +598,7 @@ func (c *SyncedCluster) Wait() error { // SetupSSH configures the cluster for use with SSH. This is generally run after // the cloud.Cluster has been synced which resets the SSH credentials on the // machines and sets them up for the current user. This method enables the -// hosts to talk to eachother and optionally confiures additional keys to be +// hosts to talk to eachother and optionally configures additional keys to be // added to the hosts via the c.AuthorizedKeys field. It does so in the following // steps: // @@ -1580,7 +1580,7 @@ func (c *SyncedCluster) pghosts(nodes []int) map[int]string { // SSH TODO(peter): document func (c *SyncedCluster) SSH(sshArgs, args []string) error { if len(c.Nodes) != 1 && len(args) == 0 { - // If trying to ssh to more than 1 node and the ssh session is interative, + // If trying to ssh to more than 1 node and the ssh session is interactive, // try sshing with an iTerm2 split screen configuration. sshed, err := maybeSplitScreenSSHITerm2(c) if sshed { diff --git a/pkg/cmd/roachprod/main.go b/pkg/cmd/roachprod/main.go index 0c5ccad93ec1..a0bc1d0a70f3 100644 --- a/pkg/cmd/roachprod/main.go +++ b/pkg/cmd/roachprod/main.go @@ -640,7 +640,7 @@ var cachedHostsCmd = &cobra.Command{ continue } fmt.Print(c.Name) - // when invokved by bash-completion, cachedHostsCluster is what the user + // when invoked by bash-completion, cachedHostsCluster is what the user // has currently typed -- if this cluster matches that, expand its hosts. if strings.HasPrefix(cachedHostsCluster, c.Name) { for i := range c.VMs { diff --git a/pkg/cmd/roachprod/vm/gce/gcloud.go b/pkg/cmd/roachprod/vm/gce/gcloud.go index e7a0c48e84c3..e65ad9fc4c05 100644 --- a/pkg/cmd/roachprod/vm/gce/gcloud.go +++ b/pkg/cmd/roachprod/vm/gce/gcloud.go @@ -188,7 +188,7 @@ type providerOpts struct { // useSharedUser indicates that the shared user rather than the personal // user should be used to ssh into the remote machines. useSharedUser bool - // use preemptible insances + // use preemptible instances preemptible bool } diff --git a/pkg/cmd/roachprod/vm/gce/utils.go b/pkg/cmd/roachprod/vm/gce/utils.go index 5159c5551143..44e8fc596181 100644 --- a/pkg/cmd/roachprod/vm/gce/utils.go +++ b/pkg/cmd/roachprod/vm/gce/utils.go @@ -249,7 +249,7 @@ func SyncDNS(vms vm.List) error { return errors.Wrapf(err, "Command: %s\nOutput: %s\nZone file contents:\n%s", cmd, output, zoneBuilder.String()) } -// GetUserAuthorizedKeys retreives reads a list of user public keys from the +// GetUserAuthorizedKeys retrieves reads a list of user public keys from the // gcloud cockroach-ephemeral project and returns them formatted for use in // an authorized_keys file. func GetUserAuthorizedKeys() (authorizedKeys []byte, err error) { diff --git a/pkg/cmd/roachprod/vm/vm.go b/pkg/cmd/roachprod/vm/vm.go index be440088ab25..c71c2e15ddbc 100644 --- a/pkg/cmd/roachprod/vm/vm.go +++ b/pkg/cmd/roachprod/vm/vm.go @@ -197,7 +197,7 @@ type Provider interface { // Active returns true if the provider is properly installed and capable of // operating, false if it's just a stub. This allows one to test whether a - // particular provider is functioning properly by doin, for example, + // particular provider is functioning properly by calling, for example, // Providers[gce.ProviderName].Active. Note that just looking at // Providers[gce.ProviderName] != nil doesn't work because // Providers[gce.ProviderName] can be a stub. diff --git a/pkg/cmd/roachtest/cluster.go b/pkg/cmd/roachtest/cluster.go index 8a622c69cb22..f4e519c2bd59 100644 --- a/pkg/cmd/roachtest/cluster.go +++ b/pkg/cmd/roachtest/cluster.go @@ -937,7 +937,7 @@ func attachToExistingCluster( expiration: exp, encryptDefault: encrypt.asBool(), destroyState: destroyState{ - // If we're attaching to an existing cluster, we're not going to destoy it. + // If we're attaching to an existing cluster, we're not going to destroy it. owned: false, }, r: r, diff --git a/pkg/cmd/roachtest/test_impl.go b/pkg/cmd/roachtest/test_impl.go index 5943a4b8f462..61b229bef22a 100644 --- a/pkg/cmd/roachtest/test_impl.go +++ b/pkg/cmd/roachtest/test_impl.go @@ -213,7 +213,7 @@ func (t *testImpl) progress(id int64, frac float64) { } // Progress sets the progress (a fraction in the range [0,1]) associated with -// the main test status messasge. When called from the main test goroutine +// the main test status message. When called from the main test goroutine // (i.e. the goroutine on which TestSpec.Run is invoked), this is equivalent to // calling WorkerProgress. func (t *testImpl) Progress(frac float64) { @@ -221,7 +221,7 @@ func (t *testImpl) Progress(frac float64) { } // WorkerProgress sets the progress (a fraction in the range [0,1]) associated -// with the a worker status messasge. +// with the a worker status message. func (t *testImpl) WorkerProgress(frac float64) { t.progress(goid.Get(), frac) } diff --git a/pkg/cmd/roachtest/test_runner.go b/pkg/cmd/roachtest/test_runner.go index dbd3e7babee2..24ea3365f33a 100644 --- a/pkg/cmd/roachtest/test_runner.go +++ b/pkg/cmd/roachtest/test_runner.go @@ -1109,7 +1109,7 @@ func (r *testRunner) addWorker(ctx context.Context, name string) *workerStatus { return w } -// removeWorker deletes the bookkepping for a worker that has finished running. +// removeWorker deletes the bookkeeping for a worker that has finished running. func (r *testRunner) removeWorker(ctx context.Context, name string) { r.workersMu.Lock() delete(r.workersMu.workers, name) diff --git a/pkg/cmd/roachtest/tests/cdc.go b/pkg/cmd/roachtest/tests/cdc.go index 9f9e89e9ba58..8caebe8f331e 100644 --- a/pkg/cmd/roachtest/tests/cdc.go +++ b/pkg/cmd/roachtest/tests/cdc.go @@ -1110,7 +1110,7 @@ fi `, confluentDownloadURL, confluentSHA256, confluentInstallBase, confluentCLIVersion, confluentCLIDownloadURLBase) const ( - // kafkaJAASConfig is a JAAS configuration file that creats a + // kafkaJAASConfig is a JAAS configuration file that creates a // user called "plain" with password "plain-secret" that can // authenticate via SASL/PLAIN. // diff --git a/pkg/cmd/roachtest/tests/rebalance_load.go b/pkg/cmd/roachtest/tests/rebalance_load.go index 549c731c9414..222e3d5622ef 100644 --- a/pkg/cmd/roachtest/tests/rebalance_load.go +++ b/pkg/cmd/roachtest/tests/rebalance_load.go @@ -38,7 +38,7 @@ func registerRebalanceLoad(r registry.Registry) { // kv.allocator.stat_based_rebalancing.enabled is set to true, we'd expect // load-based rebalancing to distribute the load evenly across the nodes in // the cluster. Without that setting, the fact that the kv table has so few - // ranges means that they probablistically won't have their leases evenly + // ranges means that they probabilistically won't have their leases evenly // spread across all the nodes (they'll often just end up staying on n1). // // In other words, this test should always pass with diff --git a/pkg/cmd/roachtest/tests/restore.go b/pkg/cmd/roachtest/tests/restore.go index 9bc47e8c1e4b..b92121276fdf 100644 --- a/pkg/cmd/roachtest/tests/restore.go +++ b/pkg/cmd/roachtest/tests/restore.go @@ -59,7 +59,7 @@ func NewHealthChecker(t test.Test, c cluster.Cluster, nodes option.NodeListOptio } } -// Done signals the HeatlthChecker's Runner to shut down. +// Done signals the HealthChecker's Runner to shut down. func (hc *HealthChecker) Done() { close(hc.doneCh) } @@ -425,7 +425,7 @@ func registerRestore(r registry.Registry) { // If the nodes are large enough (specifically, if they // have enough memory we can increase the parallelism // of restore). Machines with 16 vCPUs typically have - // enough memory to supoprt 3 concurrent workers. + // enough memory to support 3 concurrent workers. c.Run(ctx, c.Node(1), `./cockroach sql --insecure -e "SET CLUSTER SETTING kv.bulk_io_write.restore_node_concurrency = 5"`) c.Run(ctx, c.Node(1), diff --git a/pkg/cmd/skip-test/main.go b/pkg/cmd/skip-test/main.go index bb3ec8f6fd8a..28cc64156bb1 100644 --- a/pkg/cmd/skip-test/main.go +++ b/pkg/cmd/skip-test/main.go @@ -182,7 +182,7 @@ Release note: None // The shorthand for opening a web browser with Python, `python -m // webbrowser URL`, does not set the status code appropriately. - // TODO(mgartner): A Github username should be used instead of remote in the + // TODO(mgartner): A GitHub username should be used instead of remote in the // URL. A remote might not be the same as the username, in which case an // incorrect URL is opened. For example, the remote might be named "origin" // but the username is "mgartner". diff --git a/pkg/col/colserde/arrowserde/file.fbs b/pkg/col/colserde/arrowserde/file.fbs index 2fb3f5d3ec33..81cdb1b6d81b 100644 --- a/pkg/col/colserde/arrowserde/file.fbs +++ b/pkg/col/colserde/arrowserde/file.fbs @@ -42,7 +42,7 @@ struct Block { metaDataLength: int; /// Length of the data (this is aligned so there can be a gap between this and - /// the metatdata). + /// the metadata). bodyLength: long; } diff --git a/pkg/col/colserde/arrowserde/file_generated.go b/pkg/col/colserde/arrowserde/file_generated.go index c4195461c44f..4cb12084d5cb 100644 --- a/pkg/col/colserde/arrowserde/file_generated.go +++ b/pkg/col/colserde/arrowserde/file_generated.go @@ -153,13 +153,13 @@ func (rcv *Block) MutateMetaDataLength(n int32) bool { } /// Length of the data (this is aligned so there can be a gap between this and -/// the metatdata). +/// the metadata). func (rcv *Block) BodyLength() int64 { return rcv._tab.GetInt64(rcv._tab.Pos + flatbuffers.UOffsetT(16)) } /// Length of the data (this is aligned so there can be a gap between this and -/// the metatdata). +/// the metadata). func (rcv *Block) MutateBodyLength(n int64) bool { return rcv._tab.MutateInt64(rcv._tab.Pos+flatbuffers.UOffsetT(16), n) } diff --git a/pkg/col/colserde/arrowserde/tensor.fbs b/pkg/col/colserde/arrowserde/tensor.fbs index e67214839c84..532126705c80 100644 --- a/pkg/col/colserde/arrowserde/tensor.fbs +++ b/pkg/col/colserde/arrowserde/tensor.fbs @@ -55,7 +55,7 @@ root_type Tensor; /// ---------------------------------------------------------------------- /// EXPERIMENTAL: Data structures for sparse tensors -/// Coodinate format of sparse tensor index. +/// Coordinate format of sparse tensor index. table SparseTensorIndexCOO { /// COO's index list are represented as a NxM matrix, /// where N is the number of non-zero values, @@ -79,7 +79,7 @@ table SparseTensorIndexCOO { /// [2, 2, 3, 1, 2, 0], /// [0, 1, 0, 0, 3, 4]] /// - /// Note that the indices are sorted in lexcographical order. + /// Note that the indices are sorted in lexicographical order. indicesBuffer: Buffer; } diff --git a/pkg/col/colserde/arrowserde/tensor_generated.go b/pkg/col/colserde/arrowserde/tensor_generated.go index 8590acb1bcb5..0d30ab7dd410 100644 --- a/pkg/col/colserde/arrowserde/tensor_generated.go +++ b/pkg/col/colserde/arrowserde/tensor_generated.go @@ -209,7 +209,7 @@ func TensorEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { /// ---------------------------------------------------------------------- /// EXPERIMENTAL: Data structures for sparse tensors -/// Coodinate format of sparse tensor index. +/// Coordinate format of sparse tensor index. type SparseTensorIndexCOO struct { _tab flatbuffers.Table } @@ -252,7 +252,7 @@ func (rcv *SparseTensorIndexCOO) Table() flatbuffers.Table { /// [2, 2, 3, 1, 2, 0], /// [0, 1, 0, 0, 3, 4]] /// -/// Note that the indices are sorted in lexcographical order. +/// Note that the indices are sorted in lexicographical order. func (rcv *SparseTensorIndexCOO) IndicesBuffer(obj *Buffer) *Buffer { o := flatbuffers.UOffsetT(rcv._tab.Offset(4)) if o != 0 { @@ -288,7 +288,7 @@ func (rcv *SparseTensorIndexCOO) IndicesBuffer(obj *Buffer) *Buffer { /// [2, 2, 3, 1, 2, 0], /// [0, 1, 0, 0, 3, 4]] /// -/// Note that the indices are sorted in lexcographical order. +/// Note that the indices are sorted in lexicographical order. func SparseTensorIndexCOOStart(builder *flatbuffers.Builder) { builder.StartObject(1) } diff --git a/pkg/config/zonepb/zone.pb.go b/pkg/config/zonepb/zone.pb.go index 5971b322739d..9049c3e86fdf 100644 --- a/pkg/config/zonepb/zone.pb.go +++ b/pkg/config/zonepb/zone.pb.go @@ -269,7 +269,7 @@ type ZoneConfig struct { // compatible if none of the "prohibitive" constraints in `Constraints` are // "required" constraints in `VoterConstraints`. VoterConstraints []ConstraintsConjunction `protobuf:"bytes,14,rep,name=voter_constraints,json=voterConstraints" json:"voter_constraints" yaml:"voter_constraints,flow"` - // InheritedContraints specifies if the value in the Constraints field was + // InheritedConstraints specifies if the value in the Constraints field was // inherited from the zone's parent or specified explicitly by the user. // // NB: We need this extra field because the non-nullable nature of diff --git a/pkg/config/zonepb/zone.proto b/pkg/config/zonepb/zone.proto index 04292e84ca51..59f5be1341a9 100644 --- a/pkg/config/zonepb/zone.proto +++ b/pkg/config/zonepb/zone.proto @@ -132,7 +132,7 @@ message ZoneConfig { // "required" constraints in `VoterConstraints`. repeated ConstraintsConjunction voter_constraints = 14 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"voter_constraints,flow\""]; - // InheritedContraints specifies if the value in the Constraints field was + // InheritedConstraints specifies if the value in the Constraints field was // inherited from the zone's parent or specified explicitly by the user. // // NB: We need this extra field because the non-nullable nature of diff --git a/pkg/geo/bbox.go b/pkg/geo/bbox.go index fdbb07f300ef..c465a767c75c 100644 --- a/pkg/geo/bbox.go +++ b/pkg/geo/bbox.go @@ -29,7 +29,7 @@ type CartesianBoundingBox struct { } // NewCartesianBoundingBox returns a properly initialized empty bounding box -// for carestian plane types. +// for cartesian plane types. func NewCartesianBoundingBox() *CartesianBoundingBox { return nil } diff --git a/pkg/geo/geo.go b/pkg/geo/geo.go index 3d865a96f426..bcf255d9182f 100644 --- a/pkg/geo/geo.go +++ b/pkg/geo/geo.go @@ -842,7 +842,7 @@ func normalizeLngLat(lng float64, lat float64) (float64, float64) { } // normalizeGeographyGeomT limits geography coordinates to spherical coordinates -// by converting geom.T cordinates inplace +// by converting geom.T coordinates inplace func normalizeGeographyGeomT(t geom.T) { switch repr := t.(type) { case *geom.GeometryCollection: diff --git a/pkg/geo/geogfn/best_projection.go b/pkg/geo/geogfn/best_projection.go index e4d3f42ea786..07f29e285d00 100644 --- a/pkg/geo/geogfn/best_projection.go +++ b/pkg/geo/geogfn/best_projection.go @@ -55,7 +55,7 @@ func BestGeomProjection(boundingRect s2.Rect) (geoprojbase.Proj4Text, error) { if lngWidth.Degrees() < 6 { // Determine the offset of the projection. // Offset longitude -180 to 0 and divide by 6 to get the zone. - // Note that we treat 180 degree longtitudes as offset 59. + // Note that we treat 180 degree longitudes as offset 59. // TODO(#geo): do we care about https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system#Exceptions? // PostGIS's _ST_BestSRID function doesn't seem to care: . sridOffset := geopb.SRID(math.Min(math.Floor((center.Lng.Degrees()+180)/6), 59)) diff --git a/pkg/geo/geogfn/unary_operators.go b/pkg/geo/geogfn/unary_operators.go index c7f1759f6d49..db1f2901fd0c 100644 --- a/pkg/geo/geogfn/unary_operators.go +++ b/pkg/geo/geogfn/unary_operators.go @@ -159,7 +159,7 @@ func Project(g geo.Geography, distance float64, azimuth s1.Angle) (geo.Geography return geo.MakeGeographyFromGeomT(ret) } -// length returns the sum of the lengtsh and perimeters in the shapes of the Geography. +// length returns the sum of the lengths and perimeters in the shapes of the Geography. // In OGC parlance, length returns both LineString lengths _and_ Polygon perimeters. func length( regions []s2.Region, spheroid *geographiclib.Spheroid, useSphereOrSpheroid UseSphereOrSpheroid, diff --git a/pkg/geo/geoindex/geoindex.go b/pkg/geo/geoindex/geoindex.go index 65dfe32429ed..fa58a6d72116 100644 --- a/pkg/geo/geoindex/geoindex.go +++ b/pkg/geo/geoindex/geoindex.go @@ -94,7 +94,7 @@ var CommuteRelationshipMap = map[RelationshipType]RelationshipType{ // rooted at the covering of g, (b) all the parent nodes of the covering // of g. The individual entries in (b) are representable as ranges of // length 1. All of this is represented as UnionKeySpans. Covers, which -// is the shape that g covers, currently delegates to Interects so +// is the shape that g covers, currently delegates to Intersects so // returns the same. // // - CoveredBy, which are the shapes that g is covered-by, needs to compute diff --git a/pkg/geo/geomfn/line_crossing_direction.go b/pkg/geo/geomfn/line_crossing_direction.go index 2065025b78fa..d6eec9ae8a78 100644 --- a/pkg/geo/geomfn/line_crossing_direction.go +++ b/pkg/geo/geomfn/line_crossing_direction.go @@ -69,7 +69,7 @@ func getSegCrossDirection(fromSeg1, toSeg1, fromSeg2, toSeg2 geom.Coord) lineCro posT2FromSeg1 := getPosition(fromSeg1, toSeg1, toSeg2) // If both point of any segment is on same side of other segment - // or second point of any segment is colliniear with other segment + // or second point of any segment is collinear with other segment // then return the value as noCrossOrCollinear else return the direction of segment2 over segment1. // To avoid double counting, collinearity condition is used with second points only. if posF1FromSeg2 == posT1FromSeg2 || posF2FromSeg1 == posT2FromSeg1 || @@ -79,7 +79,7 @@ func getSegCrossDirection(fromSeg1, toSeg1, fromSeg2, toSeg2 geom.Coord) lineCro return posT2FromSeg1 } -// LineCrossingDirection takes two lines and returns an interger value defining behavior of crossing of lines: +// LineCrossingDirection takes two lines and returns an integer value defining behavior of crossing of lines: // 0: lines do not cross, // -1: line2 crosses line1 from right to left, // 1: line2 crosses line1 from left to right, diff --git a/pkg/geo/geomfn/linestring.go b/pkg/geo/geomfn/linestring.go index 8d19b2040d96..57b374085ab9 100644 --- a/pkg/geo/geomfn/linestring.go +++ b/pkg/geo/geomfn/linestring.go @@ -281,7 +281,7 @@ func LineSubstring(g geo.Geometry, start, end float64) (geo.Geometry, error) { } lsLength := lineString.Length() - // A LineString which entirely consistents of the same point has length 0 + // A LineString which entirely consistent of the same point has length 0 // and should return the single point that represents it. if lsLength == 0 { return geo.MakeGeometryFromGeomT( diff --git a/pkg/geo/geomfn/node.go b/pkg/geo/geomfn/node.go index 3d815cb044d3..9aca4ba6871e 100644 --- a/pkg/geo/geomfn/node.go +++ b/pkg/geo/geomfn/node.go @@ -183,7 +183,7 @@ func collectEndpoints(ls *geom.LineString) []*geom.Point { // splitLineByPoint splits the line using the Point provided. // Returns a bool representing whether the line was splitted or not, and a slice of output LineStrings. -// The Point must be on provided line and not an endpoint, otherwise false is returned along with unsplitted line. +// The Point must be on provided line and not an endpoint, otherwise false is returned along with unsplit line. func splitLineByPoint(l *geom.LineString, p geom.Coord) (bool, []*geom.LineString, error) { // Do not split if Point is not on line. if !xy.IsOnLine(l.Layout(), p, l.FlatCoords()) { diff --git a/pkg/geo/geosegmentize/geosegmentize.go b/pkg/geo/geosegmentize/geosegmentize.go index ff3dc7c68b49..22c68e20906a 100644 --- a/pkg/geo/geosegmentize/geosegmentize.go +++ b/pkg/geo/geosegmentize/geosegmentize.go @@ -28,7 +28,7 @@ import ( // represents maximum segment distance. // segmentizeCoords represents the function's definition which allows // us to segmentize given two-points. We have to specify segmentizeCoords -// explicitly, as the algorithm for segmentization is significantly +// explicitly, as the algorithm for segmentation is significantly // different for geometry and geography. func Segmentize( geometry geom.T, diff --git a/pkg/geo/hilbert.go b/pkg/geo/hilbert.go index a0a9c2ffb621..1c4d0158200b 100644 --- a/pkg/geo/hilbert.go +++ b/pkg/geo/hilbert.go @@ -29,7 +29,7 @@ func hilbertInverse(n, x, y uint64) uint64 { return d } -// hilberRoate rotates/flips a quadrant appropriately. +// hilbertRotate rotates/flips a quadrant appropriately. // Adapted from `rot` in https://en.wikipedia.org/wiki/Hilbert_curve#Applications_and_mapping_algorithms. func hilbertRotate(n, x, y, rx, ry uint64) (uint64, uint64) { if ry == 0 { diff --git a/pkg/internal/rsg/rsg.go b/pkg/internal/rsg/rsg.go index 8660f00ab902..5a8bd105a743 100644 --- a/pkg/internal/rsg/rsg.go +++ b/pkg/internal/rsg/rsg.go @@ -54,7 +54,7 @@ func NewRSG(seed int64, y string, allowDuplicates bool) (*RSG, error) { // Generate generates a unique random syntax from the root node. At most depth // levels of token expansion are performed. An empty string is returned on -// error or if depth is exceeded. Generate is safe to call from mulitipule +// error or if depth is exceeded. Generate is safe to call from multiple // goroutines. If Generate is called more times than it can generate unique // output, it will block forever. func (r *RSG) Generate(root string, depth int) string { diff --git a/pkg/internal/sqlsmith/alter.go b/pkg/internal/sqlsmith/alter.go index 259959c45f44..81eb28cacebe 100644 --- a/pkg/internal/sqlsmith/alter.go +++ b/pkg/internal/sqlsmith/alter.go @@ -301,7 +301,7 @@ func makeCreateIndex(s *Smither) (tree.Statement, bool) { continue } seen[col.Name] = true - // If this is the first column and it's invertable (i.e., JSONB), make an inverted index. + // If this is the first column and it's invertible (i.e., JSONB), make an inverted index. if len(cols) == 0 && colinfo.ColumnTypeIsInvertedIndexable(tree.MustBeStaticallyKnownType(col.Type)) { inverted = true diff --git a/pkg/internal/team/team.go b/pkg/internal/team/team.go index e5f6fb421730..7e096f137b4f 100644 --- a/pkg/internal/team/team.go +++ b/pkg/internal/team/team.go @@ -37,7 +37,7 @@ type Team struct { // CODEOWNERS, code reviews for the @cockroachdb/bulk-io team). This map // does not contain TeamName. Aliases map[Alias]Purpose `yaml:"aliases"` - // TriageColumnID is the Github Column ID to assign issues to. + // TriageColumnID is the GitHub Column ID to assign issues to. TriageColumnID int `yaml:"triage_column_id"` // Email is the email address for this team. // diff --git a/pkg/jobs/jobs.go b/pkg/jobs/jobs.go index 2cc2f0988ca2..9fd0934096f7 100644 --- a/pkg/jobs/jobs.go +++ b/pkg/jobs/jobs.go @@ -212,7 +212,7 @@ var ( ) // HasErrJobCanceled returns true if the error contains the error set as the -// job's FinalResumError when it has been canceled. +// job's FinalResumeError when it has been canceled. func HasErrJobCanceled(err error) bool { return errors.Is(err, errJobCanceled) } diff --git a/pkg/jobs/jobs_test.go b/pkg/jobs/jobs_test.go index e2a0d29f08bd..0cdf5aedceed 100644 --- a/pkg/jobs/jobs_test.go +++ b/pkg/jobs/jobs_test.go @@ -809,7 +809,7 @@ func TestRegistryLifecycle(t *testing.T) { rts.setUp(t) defer rts.tearDown() - // Inject an error in the update to move the job to "succeeeded" one time. + // Inject an error in the update to move the job to "succeeded" one time. var failed atomic.Value failed.Store(false) rts.beforeUpdate = func(orig, updated jobs.JobMetadata) error { @@ -2797,7 +2797,7 @@ func TestUnmigratedSchemaChangeJobs(t *testing.T) { case <-time.After(100 * time.Millisecond): // With an adopt interval of 10 ms, within 100ms we can be reasonably sure // that the job was not adopted. At the very least, the test would be - // flakey. + // flaky. } }) diff --git a/pkg/jobs/jobspb/jobs.pb.go b/pkg/jobs/jobspb/jobs.pb.go index 6f62a589ac95..c7d227d38d49 100644 --- a/pkg/jobs/jobspb/jobs.pb.go +++ b/pkg/jobs/jobspb/jobs.pb.go @@ -1942,12 +1942,12 @@ var xxx_messageInfo_ChangefeedTarget proto.InternalMessageInfo type ChangefeedDetails struct { // Targets contains the user-specified tables and databases to watch, mapping // the descriptor id to the name at the time of changefeed creating. There is - // a 1:1 correspondance between unique targets in the original sql query and + // a 1:1 correspondence between unique targets in the original sql query and // entries in this map. // // - A watched table is stored here under its table id // - TODO(dan): A watched database is stored here under its database id - // - TODO(dan): A db.* expansion is treated identicially to watching the + // - TODO(dan): A db.* expansion is treated identically to watching the // database // // Note that the TODOs mean this field currently is guaranteed to only hold @@ -2066,7 +2066,7 @@ type ChangefeedProgress struct { // corresponding to this job. While the job ought to clean up the record // when it enters a terminal state, there may be cases where it cannot or // does not run the code to do so. To deal with this there is a background - // reconcilliation loop to ensure that protected timestamps are cleaned up. + // reconciliation loop to ensure that protected timestamps are cleaned up. // // A record is created with the job if the job requires an initial backfill. // Furthermore, once subsequent backfills begin, record will be created and @@ -2105,7 +2105,7 @@ var xxx_messageInfo_ChangefeedProgress proto.InternalMessageInfo // Checkpoint describes changefeed checkpoint. // Checkpoints are needed when performing certain operations, such as backfill. -// When changefeed restarts from previouis high water mark timestamp, all +// When changefeed restarts from previous high water mark timestamp, all // spans in checkpoint fill be filtered out during initial scan. // That is: this checkpoint describes spans that have already advanced to the high water mark. type ChangefeedProgress_Checkpoint struct { diff --git a/pkg/jobs/jobspb/jobs.proto b/pkg/jobs/jobspb/jobs.proto index 1db5dbe01668..9b85e8b904fa 100644 --- a/pkg/jobs/jobspb/jobs.proto +++ b/pkg/jobs/jobspb/jobs.proto @@ -593,12 +593,12 @@ message ChangefeedTarget { message ChangefeedDetails { // Targets contains the user-specified tables and databases to watch, mapping // the descriptor id to the name at the time of changefeed creating. There is - // a 1:1 correspondance between unique targets in the original sql query and + // a 1:1 correspondence between unique targets in the original sql query and // entries in this map. // // - A watched table is stored here under its table id // - TODO(dan): A watched database is stored here under its database id - // - TODO(dan): A db.* expansion is treated identicially to watching the + // - TODO(dan): A db.* expansion is treated identically to watching the // database // // Note that the TODOs mean this field currently is guaranteed to only hold @@ -658,7 +658,7 @@ message ChangefeedProgress { // Checkpoint describes changefeed checkpoint. // Checkpoints are needed when performing certain operations, such as backfill. - // When changefeed restarts from previouis high water mark timestamp, all + // When changefeed restarts from previous high water mark timestamp, all // spans in checkpoint fill be filtered out during initial scan. // That is: this checkpoint describes spans that have already advanced to the high water mark. message Checkpoint { @@ -672,7 +672,7 @@ message ChangefeedProgress { // corresponding to this job. While the job ought to clean up the record // when it enters a terminal state, there may be cases where it cannot or // does not run the code to do so. To deal with this there is a background - // reconcilliation loop to ensure that protected timestamps are cleaned up. + // reconciliation loop to ensure that protected timestamps are cleaned up. // // A record is created with the job if the job requires an initial backfill. // Furthermore, once subsequent backfills begin, record will be created and diff --git a/pkg/jobs/registry.go b/pkg/jobs/registry.go index d0d8adff72f4..474828fcf54f 100644 --- a/pkg/jobs/registry.go +++ b/pkg/jobs/registry.go @@ -134,7 +134,7 @@ type Registry struct { syncutil.Mutex // adoptedJobs holds a map from job id to its context cancel func and epoch. - // It contains the that are adopted and rpobably being run. One exception is + // It contains the jobs that are adopted and probably being run. One exception is // jobs scheduled inside a transaction, they will show in this map but will // only be run when the transaction commits. adoptedJobs map[jobspb.JobID]*adoptedJob @@ -149,7 +149,7 @@ type Registry struct { // function that must be called once the caller is done with the planner. // // TODO(mjibson): Can we do something to avoid passing an interface{} here -// that must be type casted in a Resumer? It cannot be done here because +// that must be cast in a Resumer? It cannot be done here because // JobExecContext lives in the sql package, which would create a dependency // cycle if listed here. Furthermore, moving JobExecContext into a common // subpackage like sqlbase is difficult because of the amount of sql-only @@ -232,7 +232,7 @@ func (r *Registry) CurrentlyRunningJobs() []jobspb.JobID { return jobs } -// ID returns a unique during the lifetume of the registry id that is +// ID returns a unique during the lifetime of the registry id that is // used for keying sqlliveness claims held by the registry. func (r *Registry) ID() base.SQLInstanceID { return r.nodeID.SQLInstanceID() @@ -1400,7 +1400,7 @@ func (r *Registry) RetryMaxDelay() float64 { return retryMaxDelaySetting.Get(&r.settings.SV).Seconds() } -// maybeRecordRetriableExeuctionFailure will record a +// maybeRecordExecutionFailure will record a // RetriableExecutionFailureError into the job payload. func (r *Registry) maybeRecordExecutionFailure(ctx context.Context, err error, j *Job) { var efe *retriableExecutionError diff --git a/pkg/keys/keys_test.go b/pkg/keys/keys_test.go index f92d803dcc84..cfce37bfb3b8 100644 --- a/pkg/keys/keys_test.go +++ b/pkg/keys/keys_test.go @@ -684,7 +684,7 @@ func TestEnsureSafeSplitKey(t *testing.T) { {es(1, 200)[:2], "insufficient bytes to decode uvarint value"}, // The column ID suffix is invalid. {es(1, 2, 200)[:3], "insufficient bytes to decode uvarint value"}, - // Exercises a former overflow bug. We decode a uint(18446744073709551610) which, if casted + // Exercises a former overflow bug. We decode a uint(18446744073709551610) which, if cast // to int carelessly, results in -6. {encoding.EncodeVarintAscending(tenSysCodec.TablePrefix(999), 322434), "malformed table key"}, // Same test cases, but for tenant 5. diff --git a/pkg/multitenant/tenantcostmodel/model.go b/pkg/multitenant/tenantcostmodel/model.go index 5f0990071c0a..3c302808d615 100644 --- a/pkg/multitenant/tenantcostmodel/model.go +++ b/pkg/multitenant/tenantcostmodel/model.go @@ -33,7 +33,7 @@ type RU float64 // have a base cost and a per-byte cost. Specifically, the cost of a read is: // RUs = ReadRequest + * ReadByte // The cost of a write is: -// RUs = WriteRequst + * WriteByte +// RUs = WriteRequest + * WriteByte // // - CPU usage on the tenant's SQL pods. // diff --git a/pkg/roachpb/api.go b/pkg/roachpb/api.go index 7e0518a6c113..12765c3f5806 100644 --- a/pkg/roachpb/api.go +++ b/pkg/roachpb/api.go @@ -83,7 +83,7 @@ const ( isAlone // requests which must be alone in a batch isPrefix // requests which should be grouped with the next request in a batch isUnsplittable // range command that must not be split during sending - skipLeaseCheck // commands which skip the check that the evaluting replica has a valid lease + skipLeaseCheck // commands which skip the check that the evaluating replica has a valid lease updatesTSCache // commands which update the timestamp cache updatesTSCacheOnErr // commands which make read data available on errors needsRefresh // commands which require refreshes to avoid serializable retries @@ -539,7 +539,7 @@ func (rh ResponseHeader) Header() ResponseHeader { return rh } -// Verify implements the Response interface for ResopnseHeader with a +// Verify implements the Response interface for ResponseHeader with a // default noop. Individual response types should override this method // if they contain checksummed data which can be verified. func (rh *ResponseHeader) Verify(req Request) error { diff --git a/pkg/roachpb/api.pb.go b/pkg/roachpb/api.pb.go index e13f4041dd0c..5b24fd402757 100644 --- a/pkg/roachpb/api.pb.go +++ b/pkg/roachpb/api.pb.go @@ -2902,7 +2902,7 @@ type QueryIntentRequest struct { // transaction could have performed overlapping writes, in which case only the // latest sequence number will remain. We assume that if a transaction has // successfully written an intent at a larger sequence number then it must - // have succeeeded in writing an intent at the smaller sequence number as + // have succeeded in writing an intent at the smaller sequence number as // well. // // QueryIntentRequests may be issued in non-transactional BatchRequests or in @@ -3966,7 +3966,7 @@ type ExportRequest struct { // is used as a lower bound and header Timestamp as higher bound or exported // entries. MVCCFilter MVCCFilter `protobuf:"varint,4,opt,name=mvcc_filter,json=mvccFilter,proto3,enum=cockroach.roachpb.MVCCFilter" json:"mvcc_filter,omitempty"` - // StartTime is only used whe MVCCFilter is set to All. + // StartTime is only used when MVCCFilter is set to All. StartTime hlc.Timestamp `protobuf:"bytes,3,opt,name=start_time,json=startTime,proto3" json:"start_time"` // Split large rows in the middle of key sequence. This option will allow // large history being broken up into target_file_size chunks and prevent diff --git a/pkg/roachpb/api.proto b/pkg/roachpb/api.proto index 248e4ce8cb1b..293cb3c881d6 100644 --- a/pkg/roachpb/api.proto +++ b/pkg/roachpb/api.proto @@ -1047,7 +1047,7 @@ message QueryIntentRequest { // transaction could have performed overlapping writes, in which case only the // latest sequence number will remain. We assume that if a transaction has // successfully written an intent at a larger sequence number then it must - // have succeeeded in writing an intent at the smaller sequence number as + // have succeeded in writing an intent at the smaller sequence number as // well. // // QueryIntentRequests may be issued in non-transactional BatchRequests or in @@ -1394,7 +1394,7 @@ message ExportRequest { // is used as a lower bound and header Timestamp as higher bound or exported // entries. MVCCFilter mvcc_filter = 4 [(gogoproto.customname) = "MVCCFilter"]; - // StartTime is only used whe MVCCFilter is set to All. + // StartTime is only used when MVCCFilter is set to All. util.hlc.Timestamp start_time = 3 [(gogoproto.nullable) = false]; // Split large rows in the middle of key sequence. This option will allow diff --git a/pkg/roachpb/data.pb.go b/pkg/roachpb/data.pb.go index e1ee9e84f021..c360a5a322ce 100644 --- a/pkg/roachpb/data.pb.go +++ b/pkg/roachpb/data.pb.go @@ -524,7 +524,7 @@ type MergeTrigger struct { // RaftCommand.WriteBatch. The persisted summary may be identical to the // summary in this field, but it does not have to be. Notably, we intended for // the summary included in the ReplicatedEvalResult.Merge.Trigger to - // eventually be a much higher-resolution version of the ReadSummmary than the + // eventually be a much higher-resolution version of the ReadSummary than the // version persisted. This scheme of persisting a compressed ReadSummary // indefinitely and including a higher-resolution ReadSummary on the // RaftCommand allows us to optimize for the common case where the merge is @@ -1095,7 +1095,7 @@ func (m *Intent) XXX_DiscardUnknown() { var xxx_messageInfo_Intent proto.InternalMessageInfo -// SingleKeySpan preseves wire compatibility with an earlier version of this +// SingleKeySpan preserves wire compatibility with an earlier version of this // proto which used a Span. An Intent never spans keys, so there was no need // for this to contain an EndKey. type Intent_SingleKeySpan struct { @@ -1133,7 +1133,7 @@ func (m *Intent_SingleKeySpan) XXX_DiscardUnknown() { var xxx_messageInfo_Intent_SingleKeySpan proto.InternalMessageInfo // A LockAcquisition represents the action of a Transaction acquiring a lock -// with a specified durbility level over a Span of keys. +// with a specified durability level over a Span of keys. type LockAcquisition struct { Span `protobuf:"bytes,1,opt,name=span,proto3,embedded=span" json:"span"` Txn enginepb.TxnMeta `protobuf:"bytes,2,opt,name=txn,proto3" json:"txn"` diff --git a/pkg/roachpb/data.proto b/pkg/roachpb/data.proto index 195760c5f676..2668caa914fd 100644 --- a/pkg/roachpb/data.proto +++ b/pkg/roachpb/data.proto @@ -188,7 +188,7 @@ message MergeTrigger { // RaftCommand.WriteBatch. The persisted summary may be identical to the // summary in this field, but it does not have to be. Notably, we intended for // the summary included in the ReplicatedEvalResult.Merge.Trigger to - // eventually be a much higher-resolution version of the ReadSummmary than the + // eventually be a much higher-resolution version of the ReadSummary than the // version persisted. This scheme of persisting a compressed ReadSummary // indefinitely and including a higher-resolution ReadSummary on the // RaftCommand allows us to optimize for the common case where the merge is @@ -524,7 +524,7 @@ message TransactionRecord { // // Note: avoid constructing Intent directly; consider using MakeIntent() instead. message Intent { - // SingleKeySpan preseves wire compatibility with an earlier version of this + // SingleKeySpan preserves wire compatibility with an earlier version of this // proto which used a Span. An Intent never spans keys, so there was no need // for this to contain an EndKey. message SingleKeySpan { @@ -537,7 +537,7 @@ message Intent { } // A LockAcquisition represents the action of a Transaction acquiring a lock -// with a specified durbility level over a Span of keys. +// with a specified durability level over a Span of keys. message LockAcquisition { Span span = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true]; storage.enginepb.TxnMeta txn = 2 [(gogoproto.nullable) = false]; diff --git a/pkg/roachpb/errors.pb.go b/pkg/roachpb/errors.pb.go index bd30c95b2208..ddc554fd3390 100644 --- a/pkg/roachpb/errors.pb.go +++ b/pkg/roachpb/errors.pb.go @@ -1375,7 +1375,7 @@ func (m *TxnAlreadyEncounteredErrorError) XXX_DiscardUnknown() { var xxx_messageInfo_TxnAlreadyEncounteredErrorError proto.InternalMessageInfo // An IntegerOverflowError indicates that an operation was aborted because -// it would have caused an integeter overflow. +// it would have caused an integer overflow. type IntegerOverflowError struct { Key Key `protobuf:"bytes,1,opt,name=key,casttype=Key" json:"key,omitempty"` CurrentValue int64 `protobuf:"varint,2,opt,name=current_value,json=currentValue" json:"current_value"` diff --git a/pkg/roachpb/errors.proto b/pkg/roachpb/errors.proto index 59c757875ebe..5a06dc54ff62 100644 --- a/pkg/roachpb/errors.proto +++ b/pkg/roachpb/errors.proto @@ -459,7 +459,7 @@ message TxnAlreadyEncounteredErrorError{ } // An IntegerOverflowError indicates that an operation was aborted because -// it would have caused an integeter overflow. +// it would have caused an integer overflow. message IntegerOverflowError { optional bytes key = 1 [(gogoproto.casttype) = "Key"]; optional int64 current_value = 2 [(gogoproto.nullable) = false]; diff --git a/pkg/roachpb/errors_test.go b/pkg/roachpb/errors_test.go index 6f4d6a57919a..697a90bc0ba5 100644 --- a/pkg/roachpb/errors_test.go +++ b/pkg/roachpb/errors_test.go @@ -46,7 +46,7 @@ func TestNewErrorNil(t *testing.T) { } } -// TestSetTxn vefifies that SetTxn updates the error message. +// TestSetTxn verifies that SetTxn updates the error message. func TestSetTxn(t *testing.T) { e := NewError(NewTransactionAbortedError(ABORT_REASON_ABORTED_RECORD_FOUND)) txn := MakeTransaction("test", Key("a"), 1, hlc.Timestamp{}, 0) diff --git a/pkg/roachpb/io-formats.pb.go b/pkg/roachpb/io-formats.pb.go index 42bb73232538..14e890cf6786 100644 --- a/pkg/roachpb/io-formats.pb.go +++ b/pkg/roachpb/io-formats.pb.go @@ -310,7 +310,7 @@ type MySQLOutfileOptions struct { FieldSeparator int32 `protobuf:"varint,2,opt,name=field_separator,json=fieldSeparator" json:"field_separator"` // enclose is the enclosing (quoting) behavior (i.e. if specified and if optional). Enclose MySQLOutfileOptions_Enclose `protobuf:"varint,3,opt,name=enclose,enum=cockroach.roachpb.MySQLOutfileOptions_Enclose" json:"enclose"` - // encloser is the character used to enclose (qupte) fields (--fields-enclosed-by) + // encloser is the character used to enclose (quote) fields (--fields-enclosed-by) Encloser int32 `protobuf:"varint,4,opt,name=encloser" json:"encloser"` // has_escape indicates that an escape character is set (mysql's default is not). HasEscape bool `protobuf:"varint,5,opt,name=has_escape,json=hasEscape" json:"has_escape"` @@ -355,7 +355,7 @@ var xxx_messageInfo_MySQLOutfileOptions proto.InternalMessageInfo // PgCopyOptions describe the format of postgresql's COPY TO STDOUT. type PgCopyOptions struct { - // delimiter is the delimitor between columns (DELIMITER) + // delimiter is the delimiter between columns (DELIMITER) Delimiter int32 `protobuf:"varint,1,opt,name=delimiter" json:"delimiter"` // null is the NULL value (NULL) Null string `protobuf:"bytes,2,opt,name=null" json:"null"` diff --git a/pkg/roachpb/io-formats.proto b/pkg/roachpb/io-formats.proto index e7291619c67b..6be67b21add0 100644 --- a/pkg/roachpb/io-formats.proto +++ b/pkg/roachpb/io-formats.proto @@ -78,7 +78,7 @@ message MySQLOutfileOptions { optional int32 field_separator = 2 [(gogoproto.nullable) = false]; // enclose is the enclosing (quoting) behavior (i.e. if specified and if optional). optional Enclose enclose = 3 [(gogoproto.nullable) = false]; - // encloser is the character used to enclose (qupte) fields (--fields-enclosed-by) + // encloser is the character used to enclose (quote) fields (--fields-enclosed-by) optional int32 encloser = 4 [(gogoproto.nullable) = false]; // has_escape indicates that an escape character is set (mysql's default is not). optional bool has_escape = 5 [(gogoproto.nullable) = false]; @@ -95,7 +95,7 @@ message MySQLOutfileOptions { // PgCopyOptions describe the format of postgresql's COPY TO STDOUT. message PgCopyOptions { - // delimiter is the delimitor between columns (DELIMITER) + // delimiter is the delimiter between columns (DELIMITER) optional int32 delimiter = 1 [(gogoproto.nullable) = false]; // null is the NULL value (NULL) optional string null = 2 [(gogoproto.nullable) = false]; diff --git a/pkg/roachpb/metadata.go b/pkg/roachpb/metadata.go index 5c6b42dd113e..4bff2db29088 100644 --- a/pkg/roachpb/metadata.go +++ b/pkg/roachpb/metadata.go @@ -552,7 +552,7 @@ func (sc StoreCapacity) FractionUsed() float64 { // cost, not truly part of the disk's capacity. This means that the disk's // capacity is really just the available space plus cockroach's usage. // - // Fall back to a more pessimistic calcuation of disk usage if we don't know + // Fall back to a more pessimistic calculation of disk usage if we don't know // how much space the store's data is taking up. if sc.Used == 0 { return float64(sc.Capacity-sc.Available) / float64(sc.Capacity) diff --git a/pkg/roachpb/metadata.pb.go b/pkg/roachpb/metadata.pb.go index 85b38c305b6d..75525a52231c 100644 --- a/pkg/roachpb/metadata.pb.go +++ b/pkg/roachpb/metadata.pb.go @@ -415,7 +415,7 @@ type RangeDescriptor struct { // merged by the merge queue. Previous, we threw an error when a user // attempted to execute ALTER TABLE/INDEX ... SPLIT AT ... when the merge // queue is enabled. With sticky_bit, users can manually split ranges without - // diabling the merge queue. + // disabling the merge queue. StickyBit *hlc.Timestamp `protobuf:"bytes,7,opt,name=sticky_bit,json=stickyBit" json:"sticky_bit,omitempty"` } diff --git a/pkg/roachpb/metadata.proto b/pkg/roachpb/metadata.proto index 20a32cc6215e..0372b07de05e 100644 --- a/pkg/roachpb/metadata.proto +++ b/pkg/roachpb/metadata.proto @@ -281,7 +281,7 @@ message RangeDescriptor { // merged by the merge queue. Previous, we threw an error when a user // attempted to execute ALTER TABLE/INDEX ... SPLIT AT ... when the merge // queue is enabled. With sticky_bit, users can manually split ranges without - // diabling the merge queue. + // disabling the merge queue. optional util.hlc.Timestamp sticky_bit = 7; } diff --git a/pkg/rpc/auth_tenant.go b/pkg/rpc/auth_tenant.go index 18807e61e1fc..061d5990d0f1 100644 --- a/pkg/rpc/auth_tenant.go +++ b/pkg/rpc/auth_tenant.go @@ -304,7 +304,7 @@ func tenantPrefix(tenID roachpb.TenantID) roachpb.RSpan { // wrappedServerStream is a thin wrapper around grpc.ServerStream that allows // modifying its context and overriding its RecvMsg method. It can be used to -// intercept each messsage and inject custom validation logic. +// intercept each message and inject custom validation logic. type wrappedServerStream struct { grpc.ServerStream ctx context.Context diff --git a/pkg/rpc/context_test.go b/pkg/rpc/context_test.go index 77b42830b747..a2cc242a9654 100644 --- a/pkg/rpc/context_test.go +++ b/pkg/rpc/context_test.go @@ -1017,7 +1017,7 @@ func TestRemoteOffsetUnhealthy(t *testing.T) { // that's what we're testing here. Likewise, serverside keepalive ensures that // if a ping is not seen within a timeout, the transport will also be closed. // -// In this test we use a TestingHeartbeatStreamService as oppposed to a standard +// In this test we use a TestingHeartbeatStreamService as opposed to a standard // HeartbeatService. This is important to test scenarios where the // client->server connection is partitioned but the server->client connection is // healthy, because a TestingHeartbeatStreamService will continue to respond on diff --git a/pkg/rpc/metrics.go b/pkg/rpc/metrics.go index 61a236702440..5cf86e5a337d 100644 --- a/pkg/rpc/metrics.go +++ b/pkg/rpc/metrics.go @@ -21,7 +21,7 @@ import "github.com/cockroachdb/cockroach/pkg/util/metric" var ( // The below gauges store the current state of running heartbeat loops. - // Gauges are useful for examing the current state of a system but can hide + // Gauges are useful for examining the current state of a system but can hide // information is the face of rapidly changing values. The context // additionally keeps counters for the number of heartbeat loops started // and completed as well as a counter for the number of heartbeat failures. diff --git a/pkg/rpc/nodedialer/nodedialer.go b/pkg/rpc/nodedialer/nodedialer.go index 4d782ececc90..e2ba4847b203 100644 --- a/pkg/rpc/nodedialer/nodedialer.go +++ b/pkg/rpc/nodedialer/nodedialer.go @@ -51,7 +51,7 @@ type Dialer struct { breakers [rpc.NumConnectionClasses]syncutil.IntMap // map[roachpb.NodeID]*wrappedBreaker } -// DialerOpt contains ocnfiguration options for a Dialer. +// DialerOpt contains configuration options for a Dialer. type DialerOpt struct { // TestingKnobs contains testing utilities. TestingKnobs DialerTestingKnobs diff --git a/pkg/security/auto_tls_init.go b/pkg/security/auto_tls_init.go index 2988003daa1d..82f804015580 100644 --- a/pkg/security/auto_tls_init.go +++ b/pkg/security/auto_tls_init.go @@ -178,7 +178,7 @@ func CreateServiceCertAndKey( return nil, nil, err } - // Bulid service certificate template; template will be used for all + // Build service certificate template; template will be used for all // autogenerated service certificates. // TODO(aaron-crl): This should match the implementation in // pkg/security/x509.go until we can consolidate them. diff --git a/pkg/security/certificate_loader.go b/pkg/security/certificate_loader.go index 893ec956f09c..ee51b2d06723 100644 --- a/pkg/security/certificate_loader.go +++ b/pkg/security/certificate_loader.go @@ -102,7 +102,7 @@ const ( // Maximum allowable permissions if file is owned by root. maxGroupKeyPermissions os.FileMode = 0740 - // Filename extenstions. + // Filename extensions. certExtension = `.crt` keyExtension = `.key` // Certificate directory permissions. diff --git a/pkg/security/certificate_manager.go b/pkg/security/certificate_manager.go index e9a4eabbd6fc..482406833664 100644 --- a/pkg/security/certificate_manager.go +++ b/pkg/security/certificate_manager.go @@ -90,7 +90,7 @@ var ( // // The nomenclature for certificates is as follows, all within the certs-dir. // - ca.crt main CA certificate. -// Used to verify everything unless overridden by more specifica CAs. +// Used to verify everything unless overridden by more specific CAs. // - ca-client.crt CA certificate to verify client certificates. If it does not exist, // fall back on 'ca.crt'. // - node.crt node certificate. @@ -119,7 +119,7 @@ type CertificateManager struct { // Set of certs. These are swapped in during Load(), and never mutated afterwards. caCert *CertInfo // default CA certificate clientCACert *CertInfo // optional: certificate to verify client certificates - uiCACert *CertInfo // optional: certificate to verify UI certficates + uiCACert *CertInfo // optional: certificate to verify UI certificates nodeCert *CertInfo // certificate for nodes (always server cert, sometimes client cert) nodeClientCert *CertInfo // optional: client certificate for 'node' user. Also included in 'clientCerts' uiCert *CertInfo // optional: server certificate for the admin UI. diff --git a/pkg/security/tls.go b/pkg/security/tls.go index d31bdae05843..18e8016e4f99 100644 --- a/pkg/security/tls.go +++ b/pkg/security/tls.go @@ -165,7 +165,7 @@ func newBaseTLSConfig(settings TLSSettings, caPEM []byte) (*tls.Config, error) { // Note that some TLS cipher suite guidance (such as Mozilla's[1]) // recommend replacing the CBC_SHA suites below with CBC_SHA384 or // CBC_SHA256 variants. We do not do this because Go does not - // currerntly implement the CBC_SHA384 suites, and its CBC_SHA256 + // currently implement the CBC_SHA384 suites, and its CBC_SHA256 // implementation is vulnerable to the Lucky13 attack and is disabled // by default.[2] // diff --git a/pkg/security/username.go b/pkg/security/username.go index 59ef4902b658..f94441ceacc5 100644 --- a/pkg/security/username.go +++ b/pkg/security/username.go @@ -155,7 +155,7 @@ const ( // UsernameValidation indicates that the SQLUsername is // being input for the purpose of looking up an existing // user, or to compare with an existing username. - // This skips the stuctural restrictions imposed + // This skips the structural restrictions imposed // for the purpose UsernameCreation. UsernameValidation UsernamePurpose = true ) diff --git a/pkg/server/admin.go b/pkg/server/admin.go index b6678ccd3f63..09edc75990b8 100644 --- a/pkg/server/admin.go +++ b/pkg/server/admin.go @@ -1096,7 +1096,7 @@ func (s *adminServer) statsForSpan( // TableStats' meta2 query through the DistSender so that it will share // the advantage of populating the cache (without the disadvantage of // potentially returning stale data). - // See Github #5435 for some discussion. + // See GitHub #5435 for some discussion. RangeCount: int64(len(rangeDescKVs)), } type nodeResponse struct { diff --git a/pkg/server/api_v2_auth.go b/pkg/server/api_v2_auth.go index 4c33b060ba50..f562868d280c 100644 --- a/pkg/server/api_v2_auth.go +++ b/pkg/server/api_v2_auth.go @@ -189,7 +189,7 @@ func (a *authenticationV2Server) login(w http.ResponseWriter, r *http.Request) { // swagger:model logoutResponse type logoutResponse struct { - // Indicates whether logout was succeessful. + // Indicates whether logout was successful. LoggedOut bool `json:"logged_out"` } diff --git a/pkg/server/auto_tls_init.go b/pkg/server/auto_tls_init.go index ff41c8a912d7..7e74a65635f8 100644 --- a/pkg/server/auto_tls_init.go +++ b/pkg/server/auto_tls_init.go @@ -252,7 +252,7 @@ func loadKeyFile(keyPath string) (key []byte, err error) { } // Simple wrapper to make it easier to store certs somewhere else later. -// Unless overwrite is true, this function will error if a file alread exists +// Unless overwrite is true, this function will error if a file already exists // at certFilePath. // TODO(aaron-crl): This was lifted from 'pkg/security' and modified. It might // make sense to refactor these calls back to 'pkg/security' rather than @@ -267,7 +267,7 @@ func writeCertificateFile(certFilePath string, certificatePEM *pem.Block, overwr } // Simple wrapper to make it easier to store certs somewhere else later. -// Unless overwrite is true, this function will error if a file alread exists +// Unless overwrite is true, this function will error if a file already exists // at keyFilePath. // TODO(aaron-crl): This was lifted from 'pkg/security' and modified. It might // make sense to refactor these calls back to 'pkg/security' rather than diff --git a/pkg/server/config.go b/pkg/server/config.go index 0a7ecd62a955..f753eba5bcb9 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -282,7 +282,7 @@ type KVConfig struct { // do nontrivial work. ReadyFn func(waitForInit bool) - // DelayedBootstrapFn is called if the boostrap process does not complete + // DelayedBootstrapFn is called if the bootstrap process does not complete // in a timely fashion, typically 30s after the server starts listening. DelayedBootstrapFn func() diff --git a/pkg/server/heapprofiler/profilestore_test.go b/pkg/server/heapprofiler/profilestore_test.go index d137b334312f..62cfeb7aa3c6 100644 --- a/pkg/server/heapprofiler/profilestore_test.go +++ b/pkg/server/heapprofiler/profilestore_test.go @@ -34,7 +34,7 @@ func TestMakeFileName(t *testing.T) { // Also check when the millisecond part is zero. This verifies that // the .999 format is not used, which would cause the millisecond - // part to be (erronously) omitted. + // part to be (erroneously) omitted. ts = time.Date(2020, 6, 15, 13, 19, 19, 00000000, time.UTC) assert.Equal(t, filepath.Join("mydir", "memprof.2020-06-15T13_19_19.000.123456.test"), diff --git a/pkg/server/serverpb/admin.pb.go b/pkg/server/serverpb/admin.pb.go index a56e0ad31828..8d709ad73496 100644 --- a/pkg/server/serverpb/admin.pb.go +++ b/pkg/server/serverpb/admin.pb.go @@ -411,7 +411,7 @@ type TableDetailsResponse struct { // is more accurate than this one; TableDetails calculates this number using // a potentially faster method that is subject to cache staleness. We should // consider removing or renaming this field to reflect that difference. See - // Github issue #5435 for more information. + // GitHub issue #5435 for more information. RangeCount int64 `protobuf:"varint,4,opt,name=range_count,json=rangeCount,proto3" json:"range_count,omitempty"` // create_table_statement is the output of "SHOW CREATE" for this table; // it is a SQL statement that would re-create the table's current schema if @@ -2512,7 +2512,7 @@ func (m *MetricMetadataRequest) XXX_DiscardUnknown() { var xxx_messageInfo_MetricMetadataRequest proto.InternalMessageInfo -// MetricMetadataResponse contains the metadata for all metics. +// MetricMetadataResponse contains the metadata for all metrics. type MetricMetadataResponse struct { Metadata map[string]metric.Metadata `protobuf:"bytes,1,rep,name=metadata,proto3" json:"metadata" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -2830,7 +2830,7 @@ func (m *CertBundleRequest) XXX_DiscardUnknown() { var xxx_messageInfo_CertBundleRequest proto.InternalMessageInfo -// CertBundleResponse contains a copy of all CAs needed to intialize TLS for +// CertBundleResponse contains a copy of all CAs needed to initialize TLS for // a new node. type CertBundleResponse struct { Bundle []byte `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"` diff --git a/pkg/server/serverpb/admin.proto b/pkg/server/serverpb/admin.proto index 501266eefbd1..5a01ae58c8b7 100644 --- a/pkg/server/serverpb/admin.proto +++ b/pkg/server/serverpb/admin.proto @@ -190,7 +190,7 @@ message TableDetailsResponse { // is more accurate than this one; TableDetails calculates this number using // a potentially faster method that is subject to cache staleness. We should // consider removing or renaming this field to reflect that difference. See - // Github issue #5435 for more information. + // GitHub issue #5435 for more information. int64 range_count = 4; // create_table_statement is the output of "SHOW CREATE" for this table; // it is a SQL statement that would re-create the table's current schema if @@ -711,7 +711,7 @@ message DataDistributionResponse { message MetricMetadataRequest { } -// MetricMetadataResponse contains the metadata for all metics. +// MetricMetadataResponse contains the metadata for all metrics. message MetricMetadataResponse { map metadata = 1 [(gogoproto.nullable) = false]; } @@ -770,7 +770,7 @@ message CertBundleRequest { bytes shared_secret = 2; } -// CertBundleResponse contains a copy of all CAs needed to intialize TLS for +// CertBundleResponse contains a copy of all CAs needed to initialize TLS for // a new node. message CertBundleResponse { bytes bundle = 1; diff --git a/pkg/server/serverpb/status.pb.go b/pkg/server/serverpb/status.pb.go index 9fd7df92d949..f4e1fa074d4a 100644 --- a/pkg/server/serverpb/status.pb.go +++ b/pkg/server/serverpb/status.pb.go @@ -2861,7 +2861,7 @@ type CancelSessionRequest struct { SessionID []byte `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` // Username of the user making this cancellation request. This may be omitted // if the user is the same as the one issuing the CancelSessionRequest. - // The caller is responsiblef or case-folding and NFC normalization. + // The caller is responsible for case-folding and NFC normalization. Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"` } @@ -3377,7 +3377,7 @@ var xxx_messageInfo_ProblemRangesResponse_NodeProblems proto.InternalMessageInfo // API: PUBLIC ALPHA type HotRangesRequest struct { // NodeID indicates which node to query for a hot range report. - // It is posssible to populate any node ID; if the node receiving + // It is possible to populate any node ID; if the node receiving // the request is not the target node, it will forward the // request to the target node. // diff --git a/pkg/server/serverpb/status.proto b/pkg/server/serverpb/status.proto index c71060c8c189..f89ec365adb0 100644 --- a/pkg/server/serverpb/status.proto +++ b/pkg/server/serverpb/status.proto @@ -849,7 +849,7 @@ message CancelSessionRequest { bytes session_id = 2 [(gogoproto.customname) = "SessionID"]; // Username of the user making this cancellation request. This may be omitted // if the user is the same as the one issuing the CancelSessionRequest. - // The caller is responsiblef or case-folding and NFC normalization. + // The caller is responsible for case-folding and NFC normalization. string username = 3; } @@ -1020,7 +1020,7 @@ message ProblemRangesResponse { // API: PUBLIC ALPHA message HotRangesRequest { // NodeID indicates which node to query for a hot range report. - // It is posssible to populate any node ID; if the node receiving + // It is possible to populate any node ID; if the node receiving // the request is not the target node, it will forward the // request to the target node. // diff --git a/pkg/server/stats_test.go b/pkg/server/stats_test.go index d4771747f616..81f4cd991152 100644 --- a/pkg/server/stats_test.go +++ b/pkg/server/stats_test.go @@ -75,7 +75,7 @@ CREATE TABLE t.test (x INT PRIMARY KEY); sqlServer.GetReportedSQLStatsController().ResetLocalSQLStats(ctx) // Run some queries mixed with diagnostics, and ensure that the statistics - // are unnaffected by the calls to report diagnostics. + // are unaffected by the calls to report diagnostics. if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ($1)`, 1); err != nil { t.Fatal(err) } diff --git a/pkg/server/status.go b/pkg/server/status.go index 2fd749aac89a..299cb3cd3d81 100644 --- a/pkg/server/status.go +++ b/pkg/server/status.go @@ -948,7 +948,7 @@ func (s *statusServer) GetFiles( var dir string switch req.Type { //TODO(ridwanmsharif): Serve logfiles so debug-zip can fetch them - // intead of reading indididual entries. + // instead of reading individual entries. case serverpb.FileType_HEAP: // Requesting for saved Heap Profiles. dir = s.admin.server.cfg.HeapProfileDirName case serverpb.FileType_GOROUTINES: // Requesting for saved Goroutine dumps. diff --git a/pkg/server/telemetry/features.go b/pkg/server/telemetry/features.go index edc29054a200..ce704965c902 100644 --- a/pkg/server/telemetry/features.go +++ b/pkg/server/telemetry/features.go @@ -192,7 +192,7 @@ const ( ReadOnly ResetCounters = false ) -// GetRawFeatureCounts returns current raw, un-quanitzed feature counter values. +// GetRawFeatureCounts returns current raw, un-quantized feature counter values. func GetRawFeatureCounts() map[string]int32 { return GetFeatureCounts(Raw, ReadOnly) } diff --git a/pkg/server/testserver.go b/pkg/server/testserver.go index 55154f6a6970..8fe26ad03a89 100644 --- a/pkg/server/testserver.go +++ b/pkg/server/testserver.go @@ -931,7 +931,7 @@ func (ts *TestServer) GetNode() *Node { return ts.node } -// DistSenderI is part of DistSendeInterface. +// DistSenderI is part of DistSenderInterface. func (ts *TestServer) DistSenderI() interface{} { return ts.distSender } diff --git a/pkg/settings/settings_test.go b/pkg/settings/settings_test.go index 44694e544e09..2a9ebfe20a3b 100644 --- a/pkg/settings/settings_test.go +++ b/pkg/settings/settings_test.go @@ -534,7 +534,7 @@ func TestCache(t *testing.T) { t.Fatalf("expected %v, got %v", expected, actual) } // If the updater doesn't have a key, e.g. if the setting has been deleted, - // Doneing it from the cache. + // Resetting it from the cache. settings.NewUpdater(sv).ResetRemaining(ctx) if expected, actual := 2, changes.boolTA; expected != actual { @@ -564,7 +564,7 @@ func TestCache(t *testing.T) { } before := i2A.Get(sv) - // Doneing after attempting to set with wrong type preserves the current + // Resetting after attempting to set with wrong type preserves the current // value. { u := settings.NewUpdater(sv) @@ -580,7 +580,7 @@ func TestCache(t *testing.T) { t.Fatalf("expected %v, got %v", expected, actual) } - // Doneing after attempting to set with the wrong type preserves the + // Resetting after attempting to set with the wrong type preserves the // current value. { u := settings.NewUpdater(sv) @@ -596,7 +596,7 @@ func TestCache(t *testing.T) { t.Fatalf("expected %v, got %v", expected, actual) } - // Doneing after attempting to set with invalid value preserves the + // Resetting after attempting to set with invalid value preserves the // current value. beforestrVal := strVal.Get(sv) { diff --git a/pkg/storage/engine.go b/pkg/storage/engine.go index 6a3436f93e6c..133716f2f8b4 100644 --- a/pkg/storage/engine.go +++ b/pkg/storage/engine.go @@ -388,7 +388,7 @@ type ExportOptions struct { // timestamp in resumeTs so that subsequent operation can pass it to firstKeyTs. StopMidKey bool // ResourceLimiter limits how long iterator could run until it exhausts allocated - // resources. Expot queries limiter in its iteration loop to break out once + // resources. Export queries limiter in its iteration loop to break out once // resources are exhausted. ResourceLimiter ResourceLimiter // If UseTBI is true, the backing MVCCIncrementalIterator will initialize a @@ -1111,7 +1111,7 @@ var ingestDelayTime = settings.RegisterDurationSetting( // number of files in it or if PendingCompactionBytesEstimate is elevated. This // it is intended to be called before ingesting a new SST, since we'd rather // backpressure the bulk operation adding SSTs than slow down the whole RocksDB -// instance and impact all forground traffic by adding too many files to it. +// instance and impact all foreground traffic by adding too many files to it. // After the number of L0 files exceeds the configured limit, it gradually // begins delaying more for each additional file in L0 over the limit until // hitting its configured (via settings) maximum delay. If the pending diff --git a/pkg/storage/enginepb/mvcc3.pb.go b/pkg/storage/enginepb/mvcc3.pb.go index cadd58c1b34a..9b83f26c8c06 100644 --- a/pkg/storage/enginepb/mvcc3.pb.go +++ b/pkg/storage/enginepb/mvcc3.pb.go @@ -77,7 +77,7 @@ type TxnMeta struct { // up to t15. Now the txn commits at ts11 and bumps the intent to ts11. // But the client thinks it has seen all changes up to t15, and so never // sees the intent! We avoid this problem by writing intents at the - // provisional commit timestamp insteadr. In this example, the intent + // provisional commit timestamp instead. In this example, the intent // would instead be written at ts11 and picked up by the client's next // read from (ts10, ts15]. // diff --git a/pkg/storage/enginepb/mvcc3.proto b/pkg/storage/enginepb/mvcc3.proto index c00db32010ce..22ed590a9229 100644 --- a/pkg/storage/enginepb/mvcc3.proto +++ b/pkg/storage/enginepb/mvcc3.proto @@ -72,7 +72,7 @@ message TxnMeta { // up to t15. Now the txn commits at ts11 and bumps the intent to ts11. // But the client thinks it has seen all changes up to t15, and so never // sees the intent! We avoid this problem by writing intents at the - // provisional commit timestamp insteadr. In this example, the intent + // provisional commit timestamp instead. In this example, the intent // would instead be written at ts11 and picked up by the client's next // read from (ts10, ts15]. // diff --git a/pkg/storage/fs/temp_dir.go b/pkg/storage/fs/temp_dir.go index ab4dc17aad81..cc55edff3c0b 100644 --- a/pkg/storage/fs/temp_dir.go +++ b/pkg/storage/fs/temp_dir.go @@ -40,7 +40,7 @@ func lockFile(filename string) (lockStruct, error) { return lockStruct{closer: closer}, nil } -// unlockFile unlocks the file asscoiated with the specified lock and GCs any allocated memory for the lock. +// unlockFile unlocks the file associated with the specified lock and GCs any allocated memory for the lock. func unlockFile(lock lockStruct) error { if lock.closer != nil { return lock.closer.Close() diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 36e12eac3361..a7115e680288 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -47,8 +47,7 @@ const ( // MinimumMaxOpenFiles is the minimum value that RocksDB's max_open_files // option can be set to. While this should be set as high as possible, the // minimum total for a single store node must be under 2048 for Windows - // compatibility. See: - // https://wpdev.uservoice.com/forums/266908-command-prompt-console-bash-on-ubuntu-on-windo/suggestions/17310124-add-ability-to-change-max-number-of-open-files-for + // compatibility. MinimumMaxOpenFiles = 1700 // Default value for maximum number of intents reported by ExportToSST // and Scan operations in WriteIntentError is set to half of the maximum @@ -1208,7 +1207,7 @@ func MVCCPut( // existence in updating the stats. // // Note that, when writing transactionally, the txn's timestamps -// dictate the timestamp of the operation, and the timestamp paramater is +// dictate the timestamp of the operation, and the timestamp parameter is // confusing and redundant. See the comment on mvccPutInternal for details. func MVCCBlindPut( ctx context.Context, @@ -1226,7 +1225,7 @@ func MVCCBlindPut( // future get responses. // // Note that, when writing transactionally, the txn's timestamps -// dictate the timestamp of the operation, and the timestamp paramater is +// dictate the timestamp of the operation, and the timestamp parameter is // confusing and redundant. See the comment on mvccPutInternal for details. func MVCCDelete( ctx context.Context, @@ -1857,7 +1856,7 @@ func mvccPutInternal( // timestamp as we use to write a value. // // Note that, when writing transactionally, the txn's timestamps -// dictate the timestamp of the operation, and the timestamp paramater is +// dictate the timestamp of the operation, and the timestamp parameter is // confusing and redundant. See the comment on mvccPutInternal for details. func MVCCIncrement( ctx context.Context, @@ -1923,7 +1922,7 @@ const ( // timestamp as we use to write a value. // // Note that, when writing transactionally, the txn's timestamps -// dictate the timestamp of the operation, and the timestamp paramater is +// dictate the timestamp of the operation, and the timestamp parameter is // confusing and redundant. See the comment on mvccPutInternal for details. // // An empty expVal means that the key is expected to not exist. If not empty, @@ -1953,7 +1952,7 @@ func MVCCConditionalPut( // currently exist. // // Note that, when writing transactionally, the txn's timestamps -// dictate the timestamp of the operation, and the timestamp paramater is +// dictate the timestamp of the operation, and the timestamp parameter is // confusing and redundant. See the comment on mvccPutInternal for details. func MVCCBlindConditionalPut( ctx context.Context, @@ -2006,7 +2005,7 @@ func mvccConditionalPutUsingIter( // will cause a ConditionFailedError. // // Note that, when writing transactionally, the txn's timestamps -// dictate the timestamp of the operation, and the timestamp paramater is +// dictate the timestamp of the operation, and the timestamp parameter is // confusing and redundant. See the comment on mvccPutInternal for details. func MVCCInitPut( ctx context.Context, diff --git a/pkg/storage/mvcc_incremental_iterator.go b/pkg/storage/mvcc_incremental_iterator.go index 4b1073b6ce31..8dd6091d6033 100644 --- a/pkg/storage/mvcc_incremental_iterator.go +++ b/pkg/storage/mvcc_incremental_iterator.go @@ -99,7 +99,7 @@ type MVCCIncrementalIterator struct { var _ SimpleMVCCIterator = &MVCCIncrementalIterator{} // MVCCIncrementalIterIntentPolicy controls how the -// MVCCIncrementalInterator will handle intents that it encounters +// MVCCIncrementalIterator will handle intents that it encounters // when iterating. type MVCCIncrementalIterIntentPolicy int diff --git a/pkg/storage/mvcc_incremental_iterator_test.go b/pkg/storage/mvcc_incremental_iterator_test.go index d928a31cbefb..b1a397281a24 100644 --- a/pkg/storage/mvcc_incremental_iterator_test.go +++ b/pkg/storage/mvcc_incremental_iterator_test.go @@ -1393,7 +1393,7 @@ func runIncrementalBenchmark( eng, _ := setupMVCCData(context.Background(), b, emk, opts) { // Pull all of the sstables into the cache. This - // probably defeates a lot of the benefits of the + // probably defeats a lot of the benefits of the // time-based optimization. iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: roachpb.KeyMax}) _, _ = iter.ComputeStats(keys.LocalMax, roachpb.KeyMax, 0) diff --git a/pkg/storage/mvcc_test.go b/pkg/storage/mvcc_test.go index ad3b6669cbbd..13086afc0a3f 100644 --- a/pkg/storage/mvcc_test.go +++ b/pkg/storage/mvcc_test.go @@ -2757,7 +2757,7 @@ func TestMVCCReverseScanFirstKeyInFuture(t *testing.T) { defer engine.Close() // The value at key2 will be at a lower timestamp than the ReverseScan, but - // the value at key3 will be at a larger timetamp. The ReverseScan should + // the value at key3 will be at a larger timestamp. The ReverseScan should // see key3 and ignore it because none of it versions are at a low enough // timestamp to read. It should then continue scanning backwards and find a // value at key2. @@ -2952,7 +2952,7 @@ func TestMVCCResolveNewerIntent(t *testing.T) { t.Fatal(err) } // Now, put down an intent which should return a write too old error - // (but will still write the intent at tx1Commit.Timestmap+1. + // (but will still write the intent at tx1Commit.Timestamp+1. err := MVCCPut(ctx, engine, nil, testKey1, txn1.ReadTimestamp, value2, txn1) if !errors.HasType(err, (*roachpb.WriteTooOldError)(nil)) { t.Fatalf("expected write too old error; got %s", err) diff --git a/pkg/storage/open.go b/pkg/storage/open.go index d6340f89ba4a..56ad6272c6f1 100644 --- a/pkg/storage/open.go +++ b/pkg/storage/open.go @@ -168,7 +168,7 @@ func Filesystem(dir string) Location { // fs is left nil intentionally, so that it will be left as the // default of vfs.Default wrapped in vfs.WithDiskHealthChecks // (initialized by DefaultPebbleOptions). - // TODO(jackson): Refactor to make it harder to accidentially remove + // TODO(jackson): Refactor to make it harder to accidentally remove // disk health checks by setting your own VFS in a call to NewPebble. } } diff --git a/pkg/storage/pebble.go b/pkg/storage/pebble.go index 4637875ab974..a32b4857f0ca 100644 --- a/pkg/storage/pebble.go +++ b/pkg/storage/pebble.go @@ -610,7 +610,7 @@ func NewPebble(ctx context.Context, cfg PebbleConfig) (*Pebble, error) { } ballastPath := base.EmergencyBallastFile(cfg.Opts.FS.PathJoin, cfg.Dir) - // For some purposes, we want to always use an unecrypted + // For some purposes, we want to always use an unencrypted // filesystem. The call below to ResolveEncryptedEnvOptions will // replace cfg.Opts.FS with a VFS wrapped with encryption-at-rest if // necessary. Before we do that, save a handle on the unencrypted diff --git a/pkg/storage/pebble_iterator.go b/pkg/storage/pebble_iterator.go index 5e1403c1cf80..93d917ba787b 100644 --- a/pkg/storage/pebble_iterator.go +++ b/pkg/storage/pebble_iterator.go @@ -584,7 +584,7 @@ func isValidSplitKey(key roachpb.Key, noSplitSpans []roachpb.Span) bool { if key.Equal(keys.Meta2KeyMax) { // We do not allow splits at Meta2KeyMax. The reason for this is that range // descriptors are stored at RangeMetaKey(range.EndKey), so the new range - // that ends at Meta2KeyMax would naturally store its decriptor at + // that ends at Meta2KeyMax would naturally store its descriptor at // RangeMetaKey(Meta2KeyMax) = Meta1KeyMax. However, Meta1KeyMax already // serves a different role of holding a second copy of the descriptor for // the range that spans the meta2/userspace boundary (see case 3a in diff --git a/pkg/testutils/floatcmp/floatcmp.go b/pkg/testutils/floatcmp/floatcmp.go index 91e82233a9a5..3b50c4615dad 100644 --- a/pkg/testutils/floatcmp/floatcmp.go +++ b/pkg/testutils/floatcmp/floatcmp.go @@ -41,7 +41,7 @@ const ( // EqualApprox reports whether expected and actual are deeply equal with the // following modifications for float64 and float32 types: // -// • If both expected and actual are not NaN or infinate, they are equal within +// • If both expected and actual are not NaN or infinite, they are equal within // the larger of the relative fraction or absolute margin calculated from the // fraction and margin arguments. // diff --git a/pkg/testutils/jobutils/jobs_verification.go b/pkg/testutils/jobutils/jobs_verification.go index 7766b189e01d..b67e548e3843 100644 --- a/pkg/testutils/jobutils/jobs_verification.go +++ b/pkg/testutils/jobutils/jobs_verification.go @@ -59,7 +59,7 @@ func WaitForJob(t testing.TB, db *sqlutils.SQLRunner, jobID jobspb.JobID) { } } -// RunJob runs the provided job control statement, intializing, notifying and +// RunJob runs the provided job control statement, initializing, notifying and // closing the chan at the passed pointer (see below for why) and returning the // jobID and error result. PAUSE JOB and CANCEL JOB are racy in that it's hard // to guarantee that the job is still running when executing a PAUSE or diff --git a/pkg/testutils/lint/passes/forbiddenmethod/analyzers.go b/pkg/testutils/lint/passes/forbiddenmethod/analyzers.go index efa2b6c686ea..82a2a58c3b33 100644 --- a/pkg/testutils/lint/passes/forbiddenmethod/analyzers.go +++ b/pkg/testutils/lint/passes/forbiddenmethod/analyzers.go @@ -69,7 +69,7 @@ var ErrGroupGoAnalyzer = Analyzer(errGroupGo) // since it does not support Protobufs generated by gogoproto. This is // because it uses an Any field to store details, with a reference to the // type, but gogoproto types are not registered with the standard Protobuf -// type registry and are therefore unknown to the unmarshaller. +// type registry and are therefore unknown to the unmarshaler. // // One could instead use a GoGo-compatible version of Status from // github.com/gogo/status, but we probably want to move away from gogoproto. diff --git a/pkg/testutils/lint/passes/nilness/nilness.go b/pkg/testutils/lint/passes/nilness/nilness.go index 3dafd905489f..7436d2467dd8 100644 --- a/pkg/testutils/lint/passes/nilness/nilness.go +++ b/pkg/testutils/lint/passes/nilness/nilness.go @@ -110,7 +110,7 @@ var defaultConfig = analyzerConfig{ } // CRDBAnalyzer defines a pass that checks for uses of provably nil -// values that were likely in error with custom configuruation +// values that were likely in error with custom configuration // suitable for the CRDB codebase. var CRDBAnalyzer = &analysis.Analyzer{ Name: "nilness", diff --git a/pkg/testutils/lint/passes/passesutil/passes_util.go b/pkg/testutils/lint/passes/passesutil/passes_util.go index ccf031b12377..e652fa6059ae 100644 --- a/pkg/testutils/lint/passes/passesutil/passes_util.go +++ b/pkg/testutils/lint/passes/passesutil/passes_util.go @@ -72,7 +72,7 @@ func HasNolintComment(pass *analysis.Pass, n ast.Node, nolintName string) bool { // which occur in in the block or decl which includes the ast node n for // filtering. We want to filter the comments down to all comments // which are associated with n or any expression up to a statement in the -// closest encloding block or decl. This is to deal with multi-line +// closest enclosing block or decl. This is to deal with multi-line // expressions or with cases where the relevant expression occurs in an // init clause of an if or for and the comment is on the preceding line. // diff --git a/pkg/testutils/net.go b/pkg/testutils/net.go index 123e493f7ba5..c88b95904c59 100644 --- a/pkg/testutils/net.go +++ b/pkg/testutils/net.go @@ -142,7 +142,7 @@ var errEAgain = errors.New("try read again") func (b *buf) readLocked(size int) ([]byte, error) { if len(b.data) == 0 && !b.closed { b.readerWait.Wait() - // We were unblocked either by data arrving, or by a partition, or by + // We were unblocked either by data arriving, or by a partition, or by // another uninteresting reason. Return to the caller, in case it's because // of a partition. return nil, errEAgain diff --git a/pkg/testutils/pgtest/datadriven.go b/pkg/testutils/pgtest/datadriven.go index fca90ccf8c0d..f1a9074201d6 100644 --- a/pkg/testutils/pgtest/datadriven.go +++ b/pkg/testutils/pgtest/datadriven.go @@ -65,7 +65,7 @@ func WalkWithNewServer( // messages. // // If the argument crdb_only is given and the server is non-crdb (e.g. -// posrgres), then the exchange is skipped. With noncrdb_only, the inverse +// postgres), then the exchange is skipped. With noncrdb_only, the inverse // happens. func RunTest(t *testing.T, path, addr, user string) { p, err := NewPGTest(context.Background(), addr, user) diff --git a/pkg/ts/catalog/catalog_generator.go b/pkg/ts/catalog/catalog_generator.go index 7543e889d735..665bada8e1c4 100644 --- a/pkg/ts/catalog/catalog_generator.go +++ b/pkg/ts/catalog/catalog_generator.go @@ -374,7 +374,7 @@ func (ic *IndividualChart) addNames(organization []string) { nondashDelimiters := regexp.MustCompile("( )|/|,") // LongTitles look like "SQL Layer | SQL | Connections". - // CollectionTitless look like "sql-layer-sql-connections". + // CollectionTitles look like "sql-layer-sql-connections". for _, n := range organization { ic.LongTitle += n + string(" | ") ic.CollectionTitle += nondashDelimiters.ReplaceAllString(strings.ToLower(n), "-") + "-" diff --git a/pkg/ts/catalog/chart_catalog.pb.go b/pkg/ts/catalog/chart_catalog.pb.go index 4a10f84478dc..461172a07034 100644 --- a/pkg/ts/catalog/chart_catalog.pb.go +++ b/pkg/ts/catalog/chart_catalog.pb.go @@ -262,7 +262,7 @@ type IndividualChart struct { // percentiles specifies whether the chart should have its metrics broken // out into percentiles; applies only to histograms. Percentiles bool `protobuf:"varint,9,req,name=percentiles" json:"percentiles"` - // metrics specifies the metics the chart should display. + // metrics specifies the metrics the chart should display. Metrics []ChartMetric `protobuf:"bytes,10,rep,name=metrics" json:"metrics"` } diff --git a/pkg/ts/catalog/chart_catalog.proto b/pkg/ts/catalog/chart_catalog.proto index ac9479334471..f887d8a2acaf 100644 --- a/pkg/ts/catalog/chart_catalog.proto +++ b/pkg/ts/catalog/chart_catalog.proto @@ -105,7 +105,7 @@ message IndividualChart { // percentiles specifies whether the chart should have its metrics broken // out into percentiles; applies only to histograms. required bool percentiles = 9 [(gogoproto.nullable) = false]; - // metrics specifies the metics the chart should display. + // metrics specifies the metrics the chart should display. repeated ChartMetric metrics = 10 [(gogoproto.nullable) = false]; } diff --git a/pkg/ts/db.go b/pkg/ts/db.go index 298c66ae507e..4e22beb39e09 100644 --- a/pkg/ts/db.go +++ b/pkg/ts/db.go @@ -118,7 +118,7 @@ func NewDB(db *kv.DB, settings *cluster.Settings) *DB { } } -// A DataSource can be queryied for a slice of time series data. +// A DataSource can be queried for a slice of time series data. type DataSource interface { GetTimeSeriesData() []tspb.TimeSeriesData } diff --git a/pkg/ts/db_test.go b/pkg/ts/db_test.go index 21a23e05304c..7f545748180a 100644 --- a/pkg/ts/db_test.go +++ b/pkg/ts/db_test.go @@ -54,7 +54,7 @@ import ( // are dispatched to both the ts.DB instance and the test model. Queries are // executed against both, and the results should match exactly. // -// In addition, the test model can be used to generate an expecation of the +// In addition, the test model can be used to generate an expectation of the // on-disk layout in the ts.DB instance; the tests should periodically assert // that the expectation matches reality. // @@ -289,7 +289,7 @@ func (tm *testModelRunner) storeTimeSeriesData(r Resolution, data []tspb.TimeSer } } - // store data in the model. Even for rollup resolutoins we store the original + // store data in the model. Even for rollup resolutions we store the original // data points in the model, with the expectation that queries will be // identical to those based on rollups. for _, d := range data { diff --git a/pkg/ts/iterator_test.go b/pkg/ts/iterator_test.go index 4bdee1c8c646..ddef32a14cde 100644 --- a/pkg/ts/iterator_test.go +++ b/pkg/ts/iterator_test.go @@ -29,7 +29,7 @@ import ( // is the start timestamp, the sample duration, and the set of samples. As // opposed to the ToInternal() method, there are two key differences: // -// 1. This method always procueds a single InternalTimeSeriesData object with +// 1. This method always produces a single InternalTimeSeriesData object with // the provided startTimestamp, rather than breaking up the datapoints into // several slabs based on a slab duration. // @@ -90,7 +90,7 @@ func makeInternalRowData( // timestamp, the sample duration, and the set of samples. As opposed to the // ToInternal() method, there are two key differences: // -// 1. This method always procueds a single InternalTimeSeriesData object with +// 1. This method always produces a single InternalTimeSeriesData object with // the provided startTimestamp, rather than breaking up the datapoints into // several slabs based on a slab duration. // @@ -711,7 +711,7 @@ func TestDownsampleSpans(t *testing.T) { }}, }, }, - // AVG downsamper. Should re-use original span data. + // AVG downsampler. Should re-use original span data. { inputDesc: []dataDesc{ {0, 10, []tspb.TimeSeriesDatapoint{ @@ -740,7 +740,7 @@ func TestDownsampleSpans(t *testing.T) { }}, }, }, - // MAX downsamper. Should re-use original span data; note that the sum and + // MAX downsampler. Should re-use original span data; note that the sum and // count values are NOT overwritten. { inputDesc: []dataDesc{ @@ -770,7 +770,7 @@ func TestDownsampleSpans(t *testing.T) { }}, }, }, - // MIN downsamper. Should re-use original span data; note that the sum and + // MIN downsampler. Should re-use original span data; note that the sum and // count values are NOT overwritten. { inputDesc: []dataDesc{ diff --git a/pkg/ts/keys.go b/pkg/ts/keys.go index bf14ae440efb..fffcc7559519 100644 --- a/pkg/ts/keys.go +++ b/pkg/ts/keys.go @@ -23,7 +23,7 @@ import ( ) // Time series keys are carefully constructed to usefully sort the data in the -// key-value store for the purpose of queries. Time series data is queryied by +// key-value store for the purpose of queries. Time series data is queried by // providing a series name and a timespan; we therefore expose the series name // and the collection time prominently on the key. // diff --git a/pkg/ts/query.go b/pkg/ts/query.go index 041f2213042c..f11550e419a4 100644 --- a/pkg/ts/query.go +++ b/pkg/ts/query.go @@ -24,8 +24,8 @@ import ( "github.com/cockroachdb/errors" ) -// timeSeriesSpan represents a queryed time span for a single time series. This -// is reprented as an ordered slice of data slabs, where each slab contains +// timeSeriesSpan represents a queried time span for a single time series. This +// is represented as an ordered slice of data slabs, where each slab contains // samples. type timeSeriesSpan []roachpb.InternalTimeSeriesData diff --git a/pkg/workload/csv.go b/pkg/workload/csv.go index 7f873f1cbd54..74b6282bb333 100644 --- a/pkg/workload/csv.go +++ b/pkg/workload/csv.go @@ -225,7 +225,7 @@ func (w *bytesWrittenWriter) Write(p []byte) (int, error) { return n, err } -// CSVMux returns a mux over http handers for csv data in all tables in the +// CSVMux returns a mux over http handlers for csv data in all tables in the // given generators. func CSVMux(metas []Meta) *http.ServeMux { mux := http.NewServeMux() diff --git a/pkg/workload/tpcc/checks.go b/pkg/workload/tpcc/checks.go index e837190738d3..88edac8d23de 100644 --- a/pkg/workload/tpcc/checks.go +++ b/pkg/workload/tpcc/checks.go @@ -427,7 +427,7 @@ func beginAsOfSystemTime(db *gosql.DB, asOfSystemTime string) (txn *gosql.Tx, er return txn, nil } -// selectTimestamp retreives an unqouted string literal of a decimal value +// selectTimestamp retrieves an unquoted string literal of a decimal value // representing the hlc timestamp of the provided txn. func selectTimestamp(txn *gosql.Tx) (ts string, err error) { err = txn.QueryRow("SELECT cluster_logical_timestamp()::string").Scan(&ts) diff --git a/pkg/workload/tpcc/tpcc.go b/pkg/workload/tpcc/tpcc.go index 900c98dfa9fd..81cd6fbc7b43 100644 --- a/pkg/workload/tpcc/tpcc.go +++ b/pkg/workload/tpcc/tpcc.go @@ -798,7 +798,7 @@ func (w *tpcc) Ops( } var partitionDBs [][]*workload.MultiConnPool if w.clientPartitions > 0 { - // Client partitons simply emulates the behavior of data partitions + // Client partitions simply emulates the behavior of data partitions // w/r/t database connections, though all of the connections will // be for the same partition. partitionDBs = make([][]*workload.MultiConnPool, w.clientPartitions) diff --git a/pkg/workload/tpccchecks/checks_generator.go b/pkg/workload/tpccchecks/checks_generator.go index 0a9c4d1e4ede..68441e505978 100644 --- a/pkg/workload/tpccchecks/checks_generator.go +++ b/pkg/workload/tpccchecks/checks_generator.go @@ -55,7 +55,7 @@ foreground TPC-C workload`, g.flags.StringSliceVar(&g.checks, "checks", checkNames, "Name of checks to be run.") g.connFlags = workload.NewConnFlags(&g.flags) - { // Set the dbOveride to default to "tpcc". + { // Set the dbOverride to default to "tpcc". dbOverrideFlag := g.flags.Lookup(`db`) dbOverrideFlag.DefValue = `tpcc` if err := dbOverrideFlag.Value.Set(`tpcc`); err != nil { diff --git a/pkg/workload/ycsb/acknowledged_counter.go b/pkg/workload/ycsb/acknowledged_counter.go index 810cef050950..d7b391c73280 100644 --- a/pkg/workload/ycsb/acknowledged_counter.go +++ b/pkg/workload/ycsb/acknowledged_counter.go @@ -41,7 +41,7 @@ func NewAcknowledgedCounter(initialCount uint64) *AcknowledgedCounter { return c } -// Last returns the largest value v such that all values in [initialCount, v) are ackowledged. +// Last returns the largest value v such that all values in [initialCount, v) are acknowledged. func (c *AcknowledgedCounter) Last() uint64 { c.mu.Lock() defer c.mu.Unlock() diff --git a/pkg/workload/ycsb/ycsb.go b/pkg/workload/ycsb/ycsb.go index 1d23ea246d92..10e8a08671a3 100644 --- a/pkg/workload/ycsb/ycsb.go +++ b/pkg/workload/ycsb/ycsb.go @@ -643,7 +643,7 @@ func (yw *ycsbWorker) nextInsertKeyIndex() uint64 { var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") -// Gnerate a random string of alphabetic characters. +// Generate a random string of alphabetic characters. func (yw *ycsbWorker) randString(length int) string { str := make([]byte, length) // prepend current timestamp matching the default CRDB UTC time format From dc98a7af09206eb38218a817704bc8565c62cec6 Mon Sep 17 00:00:00 2001 From: Andrei Matei Date: Thu, 21 Oct 2021 16:21:46 -0400 Subject: [PATCH 010/205] kvclient: minor tracing change Before this patch, the TxnCoordSender used to set the txn id as a "baggage" item on the current span - but only if the request was being recorded. This patch switches the baggage item to a simpler span "tag". I don't expect any impact from this change because it only takes effect for recording spans - so very rarely. The theoretical difference is that, as a baggage item, the txn id is present on every derived span. As a tag, it's only present on the current span. The decision to have made this a baggage in the first place I think was pretty arbitrary; I doubt anybody ever got any benefit from it. I'm doing this change because I want to get rid of the baggage maintenance code. It's a good amount of code with no real use. Release note: None --- pkg/kv/kvclient/kvcoord/BUILD.bazel | 1 + pkg/kv/kvclient/kvcoord/txn_coord_sender.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/kv/kvclient/kvcoord/BUILD.bazel b/pkg/kv/kvclient/kvcoord/BUILD.bazel index 663f875306a8..d5ac193d94a2 100644 --- a/pkg/kv/kvclient/kvcoord/BUILD.bazel +++ b/pkg/kv/kvclient/kvcoord/BUILD.bazel @@ -76,6 +76,7 @@ go_library( "@com_github_cockroachdb_redact//:redact", "@com_github_gogo_protobuf//proto", "@com_github_google_btree//:btree", + "@io_opentelemetry_go_otel//attribute", ], ) diff --git a/pkg/kv/kvclient/kvcoord/txn_coord_sender.go b/pkg/kv/kvclient/kvcoord/txn_coord_sender.go index 373d9b3cad49..b201d985cf59 100644 --- a/pkg/kv/kvclient/kvcoord/txn_coord_sender.go +++ b/pkg/kv/kvclient/kvcoord/txn_coord_sender.go @@ -26,6 +26,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/uuid" "github.com/cockroachdb/errors" "github.com/cockroachdb/logtags" + "go.opentelemetry.io/otel/attribute" ) const ( @@ -486,7 +487,7 @@ func (tc *TxnCoordSender) Send( log.Fatalf(ctx, "cannot send transactional request through unbound TxnCoordSender") } if sp.IsVerbose() { - sp.SetBaggageItem("txnID", tc.mu.txn.ID.String()) + sp.SetTag("txnID", attribute.StringValue(tc.mu.txn.ID.String())) ctx = logtags.AddTag(ctx, "txn", uuid.ShortStringer(tc.mu.txn.ID)) if log.V(2) { ctx = logtags.AddTag(ctx, "ts", tc.mu.txn.WriteTimestamp) From 3fbe6b271984860ff984471c6b0408c74b5a9a58 Mon Sep 17 00:00:00 2001 From: Andrei Matei Date: Fri, 22 Oct 2021 10:49:21 -0400 Subject: [PATCH 011/205] util/tracing: don't rely on baggage in trace recording Before this patch, a trace recording was relying on the presence of baggage to decide whether it was a "verbose" span or not. I'm trying to get rid of baggage cause it's a lot of code for very little gain, so this patch removes this use of it by replacing it with an explicit recording flag. Release note: None --- pkg/util/tracing/crdbspan.go | 1 + pkg/util/tracing/recording.go | 24 ++- .../tracing/tracingpb/recorded_span.pb.go | 139 +++++++++++------- .../tracing/tracingpb/recorded_span.proto | 3 + 4 files changed, 103 insertions(+), 64 deletions(-) diff --git a/pkg/util/tracing/crdbspan.go b/pkg/util/tracing/crdbspan.go index a64c39ae50f1..dd4a061e785f 100644 --- a/pkg/util/tracing/crdbspan.go +++ b/pkg/util/tracing/crdbspan.go @@ -538,6 +538,7 @@ func (s *crdbSpan) getRecordingNoChildrenLocked( StartTime: s.startTime, Duration: s.mu.duration, RedactableLogs: true, + Verbose: s.recordingType() == RecordingVerbose, } if rs.Duration == -1 { diff --git a/pkg/util/tracing/recording.go b/pkg/util/tracing/recording.go index e3040da839b9..68b51df4419a 100644 --- a/pkg/util/tracing/recording.go +++ b/pkg/util/tracing/recording.go @@ -217,7 +217,8 @@ func (r Recording) visitSpan(sp tracingpb.RecordedSpan, depth int) []traceLogDat // If the span was verbose at the time when the structured event was recorded, // then the Structured events will also have been stringified and included in - // the Logs above. + // the Logs above. We conservatively serialize the structured events again + // here, for the case when the span had not been verbose at the time. sp.Structured(func(sr *types.Any, t time.Time) { str, err := MessageToJSONString(sr, true /* emitDefaults */) if err != nil { @@ -367,10 +368,14 @@ func (r Recording) ToJaegerJSON(stmt, comment, nodeStr string) (string, error) { s.Logs = append(s.Logs, jl) } - // If the span was verbose then the Structured events would have been - // stringified and included in the Logs above. If the span was not verbose - // we should add the Structured events now. - if !isVerbose(sp) { + // If the span was verbose at the time when each structured event was + // recorded, then the respective events would have been stringified and + // included in the Logs above. If the span was not verbose at the time, we + // need to produce a string now. We don't know whether the span was verbose + // or not at the time each event was recorded, so we make a guess based on + // whether the span was verbose at the moment when the Recording was + // produced. + if !sp.Verbose { sp.Structured(func(sr *types.Any, t time.Time) { jl := jaegerjson.Log{Timestamp: uint64(t.UnixNano() / 1000)} jsonStr, err := MessageToJSONString(sr, true /* emitDefaults */) @@ -410,12 +415,3 @@ type TraceCollection struct { Comment string `json:"_comment"` Data []jaegerjson.Trace `json:"data"` } - -// isVerbose returns true if the RecordedSpan was started is a verbose mode. -func isVerbose(s tracingpb.RecordedSpan) bool { - if s.Baggage == nil { - return false - } - _, isVerbose := s.Baggage[verboseTracingBaggageKey] - return isVerbose -} diff --git a/pkg/util/tracing/tracingpb/recorded_span.pb.go b/pkg/util/tracing/tracingpb/recorded_span.pb.go index 9fcad9d1cb47..bfcf3b5532dd 100644 --- a/pkg/util/tracing/tracingpb/recorded_span.pb.go +++ b/pkg/util/tracing/tracingpb/recorded_span.pb.go @@ -173,6 +173,9 @@ type RecordedSpan struct { RedactableLogs bool `protobuf:"varint,15,opt,name=redactable_logs,json=redactableLogs,proto3" json:"redactable_logs,omitempty"` // Events logged in the span. Logs []LogRecord `protobuf:"bytes,9,rep,name=logs,proto3" json:"logs"` + // verbose indicates whether the span was recording in verbose mode at the + // time the recording was produced. + Verbose bool `protobuf:"varint,16,opt,name=verbose,proto3" json:"verbose,omitempty"` // The ID of the goroutine on which the span was created. GoroutineID uint64 `protobuf:"varint,12,opt,name=goroutine_id,json=goroutineId,proto3" json:"goroutine_id,omitempty"` // True if the span has been Finish()ed, false otherwise. @@ -275,59 +278,60 @@ func init() { } var fileDescriptor_e9b7b35ae7ab4ca8 = []byte{ - // 828 bytes of a gzipped FileDescriptorProto + // 841 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xdb, 0x36, 0x14, 0xb6, 0x6c, 0x25, 0x92, 0x69, 0x2f, 0x71, 0x89, 0x1c, 0x54, 0x63, 0x90, 0x8d, 0x5c, 0x16, 0xac, 0x80, 0xbc, 0x7a, 0xd8, 0x16, 0x64, 0x87, 0x61, 0x5e, 0xba, 0xc1, 0x45, 0x50, 0x14, 0x4a, - 0x76, 0xd9, 0xc5, 0xa0, 0x45, 0x86, 0x11, 0x2a, 0x8b, 0x02, 0x49, 0x0d, 0xf0, 0xb0, 0x1f, 0xd1, - 0x63, 0x8f, 0xfb, 0x17, 0x3b, 0xef, 0x96, 0x63, 0x8f, 0xc5, 0x0e, 0xd9, 0xe6, 0x00, 0xfb, 0x1d, - 0x03, 0x29, 0x4a, 0x6e, 0x9c, 0x02, 0x86, 0xe3, 0x9d, 0x44, 0x3d, 0x7e, 0xdf, 0xc7, 0x47, 0xbe, - 0xef, 0x3d, 0xf0, 0x69, 0x2e, 0xe3, 0x64, 0x20, 0x39, 0x8a, 0xe2, 0x94, 0x96, 0xdf, 0x6c, 0x3a, - 0xe0, 0x24, 0x62, 0x1c, 0x13, 0x3c, 0x11, 0x19, 0x4a, 0x83, 0x8c, 0x33, 0xc9, 0x60, 0x3f, 0x62, - 0xd1, 0x2b, 0xce, 0x50, 0x74, 0x15, 0x28, 0x56, 0x60, 0xd0, 0x41, 0xc5, 0xea, 0x1e, 0x50, 0x46, - 0x99, 0x06, 0x0f, 0xd4, 0xaa, 0xe0, 0x75, 0x1f, 0x53, 0xc6, 0x68, 0x42, 0x06, 0xfa, 0x6f, 0x9a, - 0x5f, 0x0e, 0x50, 0x3a, 0x37, 0x5b, 0xbd, 0xd5, 0x2d, 0x19, 0xcf, 0x88, 0x90, 0x68, 0x96, 0x19, - 0x80, 0xbf, 0x0a, 0xc0, 0x39, 0x47, 0x32, 0x66, 0x26, 0xa7, 0xc3, 0x7f, 0xeb, 0xa0, 0x79, 0xc6, - 0x68, 0xa8, 0xd3, 0x85, 0xc7, 0xc0, 0x56, 0x02, 0x9e, 0xd5, 0xb7, 0x8e, 0x5a, 0xc3, 0x6e, 0x50, - 0x90, 0x83, 0x92, 0x1c, 0x5c, 0x94, 0xea, 0x23, 0xf7, 0xfa, 0xa6, 0x57, 0x7b, 0xfd, 0x57, 0xcf, - 0x0a, 0x35, 0x03, 0x62, 0xf0, 0x08, 0x93, 0x8c, 0x93, 0x08, 0x49, 0x82, 0x27, 0x97, 0x31, 0x49, - 0xb0, 0xf0, 0xea, 0xfd, 0xc6, 0x51, 0x6b, 0xf8, 0x34, 0x58, 0x77, 0xef, 0xa0, 0xca, 0x20, 0xf8, - 0x5e, 0x31, 0x47, 0xb6, 0x52, 0x0f, 0x3b, 0x4b, 0x45, 0x1d, 0x16, 0xf0, 0x25, 0x70, 0x66, 0x44, - 0x08, 0x44, 0x89, 0xd7, 0xe8, 0x5b, 0x47, 0xcd, 0xd1, 0x97, 0x0a, 0xf8, 0xe7, 0x4d, 0x2f, 0xa0, - 0xb1, 0xbc, 0xca, 0xa7, 0x41, 0xc4, 0x66, 0x83, 0xea, 0x34, 0xac, 0xca, 0x80, 0x51, 0x24, 0x83, - 0x50, 0x7f, 0xd0, 0x34, 0x21, 0xe7, 0x92, 0xc7, 0x29, 0x0d, 0x4b, 0x99, 0x2e, 0x05, 0x3b, 0x5a, - 0x1b, 0x76, 0x40, 0xe3, 0x15, 0x99, 0xeb, 0x9b, 0x37, 0x43, 0xb5, 0x84, 0x67, 0x60, 0xe7, 0x67, - 0x94, 0xe4, 0xc4, 0xab, 0x6f, 0x75, 0x54, 0x21, 0x72, 0xf8, 0x2b, 0xe8, 0x9c, 0x4b, 0x9e, 0x47, - 0x32, 0xe7, 0x04, 0x6f, 0xfd, 0xdc, 0x01, 0x70, 0x32, 0x34, 0x4f, 0x18, 0xc2, 0x3a, 0xbb, 0xd6, - 0xf0, 0xe0, 0x1e, 0xf9, 0xdb, 0x74, 0x1e, 0x96, 0xa0, 0xc3, 0xdf, 0x1d, 0xd0, 0x0e, 0x8d, 0x25, - 0xcf, 0x33, 0x94, 0xc2, 0x2f, 0x80, 0xab, 0x9e, 0x9f, 0x4c, 0x62, 0xac, 0x8f, 0xb7, 0x47, 0x5d, - 0x73, 0x3f, 0xe7, 0x42, 0xc5, 0xc7, 0xa7, 0x8b, 0xe5, 0x32, 0x74, 0x34, 0x76, 0x8c, 0xe1, 0x53, - 0xe0, 0x28, 0x43, 0x2b, 0x56, 0x5d, 0xb3, 0x3c, 0xc3, 0xda, 0x55, 0xaa, 0x9a, 0x64, 0x56, 0xe1, - 0xae, 0x02, 0x8e, 0x31, 0x3c, 0x05, 0x7b, 0x19, 0xe2, 0x24, 0x95, 0x93, 0x92, 0xd9, 0xd0, 0x4c, - 0xff, 0x1e, 0xb3, 0xfd, 0x52, 0xe3, 0x0c, 0xbf, 0x9d, 0x2d, 0xff, 0x30, 0xfc, 0x18, 0x34, 0x59, - 0x46, 0x0a, 0xeb, 0x7a, 0xb6, 0x2e, 0xd2, 0x32, 0x00, 0x7f, 0x04, 0xce, 0x14, 0x51, 0xaa, 0x7c, - 0xb1, 0xa3, 0x3d, 0xf7, 0xf5, 0x7a, 0xcf, 0xbd, 0xff, 0x1c, 0xc1, 0xa8, 0x60, 0x3f, 0x4b, 0x25, - 0x9f, 0x87, 0xa5, 0x16, 0x3c, 0x03, 0xb6, 0x44, 0x54, 0x78, 0xbb, 0x5a, 0xf3, 0x78, 0x43, 0xcd, - 0x0b, 0x44, 0x45, 0x21, 0xa8, 0x55, 0xe0, 0x77, 0x00, 0x08, 0x89, 0xb8, 0x9c, 0xe8, 0x9a, 0x3b, - 0x1b, 0xd4, 0xbc, 0xa9, 0x79, 0x6a, 0x07, 0x7e, 0x03, 0xdc, 0xb2, 0x83, 0x3d, 0x57, 0x4b, 0x3c, - 0xbe, 0x27, 0x71, 0x6a, 0x00, 0x85, 0xc2, 0x1b, 0xa5, 0x50, 0x91, 0xe0, 0x27, 0x60, 0x9f, 0x57, - 0x16, 0x9d, 0x24, 0x8c, 0x0a, 0x6f, 0xbf, 0x6f, 0x1d, 0xb9, 0xe1, 0xde, 0x32, 0x7c, 0xc6, 0xa8, - 0x80, 0xcf, 0x80, 0xad, 0x77, 0x9b, 0xfa, 0xf2, 0x4f, 0x36, 0x68, 0x62, 0xd3, 0xbe, 0x9a, 0x0e, - 0x87, 0xa0, 0x4d, 0x19, 0x67, 0xb9, 0x8c, 0x53, 0x6d, 0xb6, 0xb6, 0x2e, 0xfe, 0xfe, 0xe2, 0xa6, - 0xd7, 0xfa, 0xa1, 0x8c, 0x8f, 0x4f, 0xc3, 0x56, 0x05, 0x1a, 0x63, 0xd8, 0x05, 0xee, 0x65, 0x9c, - 0xc6, 0xe2, 0x8a, 0x60, 0xef, 0x23, 0x9d, 0x5c, 0xf5, 0x0f, 0x29, 0x80, 0xa2, 0xea, 0xa3, 0x49, - 0x31, 0x66, 0x85, 0xb7, 0xa7, 0x93, 0x1c, 0xae, 0x4f, 0x72, 0xb5, 0x07, 0x4d, 0xae, 0x8f, 0xc4, - 0x4a, 0x5c, 0x74, 0x4f, 0x40, 0xfb, 0x7d, 0x57, 0x7c, 0x60, 0x40, 0x1c, 0xdc, 0x19, 0x10, 0xa6, - 0xd1, 0x4f, 0xea, 0xc7, 0x56, 0xf7, 0x2b, 0xd0, 0xac, 0xaa, 0xbf, 0x09, 0xf1, 0xc4, 0x7e, 0xf3, - 0x5b, 0xaf, 0xf6, 0xdc, 0x76, 0x41, 0xa7, 0xf5, 0xdc, 0x76, 0x5b, 0x9d, 0xf6, 0xe1, 0x1f, 0x36, - 0xd8, 0x7b, 0xc1, 0xf8, 0x0c, 0x25, 0xf1, 0x2f, 0xa6, 0x77, 0xef, 0xf4, 0x82, 0xb5, 0xda, 0x0b, - 0x2f, 0x8c, 0x69, 0x8b, 0xe1, 0x7b, 0xb2, 0xfe, 0x49, 0xee, 0xaa, 0xaf, 0xb1, 0x6d, 0x63, 0x7b, - 0xdb, 0xda, 0x0f, 0xb1, 0x6d, 0xe9, 0xc6, 0x9d, 0xed, 0xdc, 0xf8, 0x61, 0xf7, 0x38, 0xff, 0xbb, - 0x7b, 0x60, 0x08, 0xdc, 0xe8, 0x2a, 0x4e, 0x30, 0x27, 0xa9, 0x19, 0x1f, 0x9f, 0x6d, 0x5a, 0x09, - 0x23, 0x5e, 0xe9, 0x3c, 0xd8, 0x55, 0xa3, 0x27, 0xd7, 0xff, 0xf8, 0xb5, 0xeb, 0x85, 0x6f, 0xbd, - 0x5d, 0xf8, 0xd6, 0xbb, 0x85, 0x6f, 0xfd, 0xbd, 0xf0, 0xad, 0xd7, 0xb7, 0x7e, 0xed, 0xed, 0xad, - 0x5f, 0x7b, 0x77, 0xeb, 0xd7, 0x7e, 0x6a, 0x56, 0x49, 0x4c, 0x77, 0x75, 0x41, 0x3e, 0xff, 0x2f, - 0x00, 0x00, 0xff, 0xff, 0x2f, 0xec, 0xf7, 0xf7, 0xda, 0x08, 0x00, 0x00, + 0x76, 0xd9, 0xc5, 0xa0, 0x45, 0x86, 0x11, 0x2a, 0x8b, 0x02, 0x49, 0x15, 0xf0, 0xb0, 0x1f, 0xd1, + 0x63, 0x8f, 0xfb, 0x2b, 0xbb, 0xe5, 0xd8, 0xdd, 0x8a, 0x1d, 0xb2, 0xcd, 0x01, 0xf6, 0x3b, 0x06, + 0x52, 0x94, 0xdc, 0x38, 0x05, 0x0c, 0xd7, 0x3d, 0x49, 0x7c, 0xfc, 0xbe, 0x8f, 0x8f, 0x7c, 0xdf, + 0x7b, 0xe0, 0xf3, 0x5c, 0xc6, 0xc9, 0x40, 0x72, 0x14, 0xc5, 0x29, 0x2d, 0xbf, 0xd9, 0x74, 0xc0, + 0x49, 0xc4, 0x38, 0x26, 0x78, 0x22, 0x32, 0x94, 0x06, 0x19, 0x67, 0x92, 0xc1, 0x7e, 0xc4, 0xa2, + 0x17, 0x9c, 0xa1, 0xe8, 0x2a, 0x50, 0xac, 0xc0, 0xa0, 0x83, 0x8a, 0xd5, 0x3d, 0xa0, 0x8c, 0x32, + 0x0d, 0x1e, 0xa8, 0xbf, 0x82, 0xd7, 0x7d, 0x48, 0x19, 0xa3, 0x09, 0x19, 0xe8, 0xd5, 0x34, 0xbf, + 0x1c, 0xa0, 0x74, 0x6e, 0xb6, 0x7a, 0xab, 0x5b, 0x32, 0x9e, 0x11, 0x21, 0xd1, 0x2c, 0x33, 0x00, + 0x7f, 0x15, 0x80, 0x73, 0x8e, 0x64, 0xcc, 0x4c, 0x4e, 0x87, 0xff, 0xd5, 0x41, 0xf3, 0x8c, 0xd1, + 0x50, 0xa7, 0x0b, 0x8f, 0x81, 0xad, 0x04, 0x3c, 0xab, 0x6f, 0x1d, 0xb5, 0x86, 0xdd, 0xa0, 0x20, + 0x07, 0x25, 0x39, 0xb8, 0x28, 0xd5, 0x47, 0xee, 0xf5, 0x4d, 0xaf, 0xf6, 0xea, 0xef, 0x9e, 0x15, + 0x6a, 0x06, 0xc4, 0xe0, 0x01, 0x26, 0x19, 0x27, 0x11, 0x92, 0x04, 0x4f, 0x2e, 0x63, 0x92, 0x60, + 0xe1, 0xd5, 0xfb, 0x8d, 0xa3, 0xd6, 0xf0, 0x71, 0xb0, 0xee, 0xde, 0x41, 0x95, 0x41, 0xf0, 0xa3, + 0x62, 0x8e, 0x6c, 0xa5, 0x1e, 0x76, 0x96, 0x8a, 0x3a, 0x2c, 0xe0, 0x73, 0xe0, 0xcc, 0x88, 0x10, + 0x88, 0x12, 0xaf, 0xd1, 0xb7, 0x8e, 0x9a, 0xa3, 0xaf, 0x15, 0xf0, 0xaf, 0x9b, 0x5e, 0x40, 0x63, + 0x79, 0x95, 0x4f, 0x83, 0x88, 0xcd, 0x06, 0xd5, 0x69, 0x58, 0x95, 0x01, 0xa3, 0x48, 0x06, 0xa1, + 0xfe, 0xa0, 0x69, 0x42, 0xce, 0x25, 0x8f, 0x53, 0x1a, 0x96, 0x32, 0x5d, 0x0a, 0x76, 0xb4, 0x36, + 0xec, 0x80, 0xc6, 0x0b, 0x32, 0xd7, 0x37, 0x6f, 0x86, 0xea, 0x17, 0x9e, 0x81, 0x9d, 0x97, 0x28, + 0xc9, 0x89, 0x57, 0xdf, 0xea, 0xa8, 0x42, 0xe4, 0xf0, 0x37, 0xd0, 0x39, 0x97, 0x3c, 0x8f, 0x64, + 0xce, 0x09, 0xde, 0xfa, 0xb9, 0x03, 0xe0, 0x64, 0x68, 0x9e, 0x30, 0x84, 0x75, 0x76, 0xad, 0xe1, + 0xc1, 0x3d, 0xf2, 0xf7, 0xe9, 0x3c, 0x2c, 0x41, 0x87, 0x7f, 0x3a, 0xa0, 0x1d, 0x1a, 0x4b, 0x9e, + 0x67, 0x28, 0x85, 0x5f, 0x01, 0x57, 0x3d, 0x3f, 0x99, 0xc4, 0x58, 0x1f, 0x6f, 0x8f, 0xba, 0xe6, + 0x7e, 0xce, 0x85, 0x8a, 0x8f, 0x4f, 0x17, 0xcb, 0xdf, 0xd0, 0xd1, 0xd8, 0x31, 0x86, 0x8f, 0x81, + 0xa3, 0x0c, 0xad, 0x58, 0x75, 0xcd, 0xf2, 0x0c, 0x6b, 0x57, 0xa9, 0x6a, 0x92, 0xf9, 0x0b, 0x77, + 0x15, 0x70, 0x8c, 0xe1, 0x29, 0xd8, 0xcb, 0x10, 0x27, 0xa9, 0x9c, 0x94, 0xcc, 0x86, 0x66, 0xfa, + 0xf7, 0x98, 0xed, 0xe7, 0x1a, 0x67, 0xf8, 0xed, 0x6c, 0xb9, 0xc2, 0xf0, 0x53, 0xd0, 0x64, 0x19, + 0x29, 0xac, 0xeb, 0xd9, 0xba, 0x48, 0xcb, 0x00, 0xfc, 0x19, 0x38, 0x53, 0x44, 0xa9, 0xf2, 0xc5, + 0x8e, 0xf6, 0xdc, 0xb7, 0xeb, 0x3d, 0xf7, 0xee, 0x73, 0x04, 0xa3, 0x82, 0xfd, 0x24, 0x95, 0x7c, + 0x1e, 0x96, 0x5a, 0xf0, 0x0c, 0xd8, 0x12, 0x51, 0xe1, 0xed, 0x6a, 0xcd, 0xe3, 0x0d, 0x35, 0x2f, + 0x10, 0x15, 0x85, 0xa0, 0x56, 0x81, 0x3f, 0x00, 0x20, 0x24, 0xe2, 0x72, 0xa2, 0x6b, 0xee, 0x6c, + 0x50, 0xf3, 0xa6, 0xe6, 0xa9, 0x1d, 0xf8, 0x1d, 0x70, 0xcb, 0x0e, 0xf6, 0x5c, 0x2d, 0xf1, 0xf0, + 0x9e, 0xc4, 0xa9, 0x01, 0x14, 0x0a, 0xaf, 0x95, 0x42, 0x45, 0x82, 0x9f, 0x81, 0x7d, 0x5e, 0x59, + 0x74, 0x92, 0x30, 0x2a, 0xbc, 0xfd, 0xbe, 0x75, 0xe4, 0x86, 0x7b, 0xcb, 0xf0, 0x19, 0xa3, 0x02, + 0x3e, 0x01, 0xb6, 0xde, 0x6d, 0xea, 0xcb, 0x3f, 0xda, 0xa0, 0x89, 0x4d, 0xfb, 0x6a, 0x3a, 0xf4, + 0x80, 0xf3, 0x92, 0xf0, 0x29, 0x13, 0xc4, 0xeb, 0xe8, 0x73, 0xca, 0x25, 0x1c, 0x82, 0x36, 0x65, + 0x9c, 0xe5, 0x32, 0x4e, 0xb5, 0x0d, 0xdb, 0xda, 0x16, 0xfb, 0x8b, 0x9b, 0x5e, 0xeb, 0xa7, 0x32, + 0x3e, 0x3e, 0x0d, 0x5b, 0x15, 0x68, 0x8c, 0x61, 0x17, 0xb8, 0x97, 0x71, 0x1a, 0x8b, 0x2b, 0x82, + 0xbd, 0x4f, 0xb4, 0x5c, 0xb5, 0x86, 0x14, 0x40, 0x51, 0x75, 0xd8, 0xa4, 0x18, 0xc0, 0xc2, 0xdb, + 0xd3, 0xe9, 0x0f, 0xd7, 0xa7, 0xbf, 0xda, 0x9d, 0xe6, 0x16, 0x0f, 0xc4, 0x4a, 0x5c, 0x74, 0x4f, + 0x40, 0xfb, 0x5d, 0xbf, 0xbc, 0x67, 0x74, 0x1c, 0xdc, 0x19, 0x1d, 0x66, 0x04, 0x9c, 0xd4, 0x8f, + 0xad, 0xee, 0x37, 0xa0, 0x59, 0xf9, 0x62, 0x13, 0xe2, 0x89, 0xfd, 0xfa, 0xf7, 0x5e, 0xed, 0xa9, + 0xed, 0x82, 0x4e, 0xeb, 0xa9, 0xed, 0xb6, 0x3a, 0xed, 0xc3, 0x3f, 0x6c, 0xb0, 0xf7, 0x8c, 0xf1, + 0x19, 0x4a, 0xe2, 0x5f, 0x4d, 0x57, 0xdf, 0xe9, 0x12, 0x6b, 0xb5, 0x4b, 0x9e, 0x19, 0x3b, 0x17, + 0x63, 0xf9, 0x64, 0xfd, 0x93, 0xdc, 0x55, 0x5f, 0x63, 0xe8, 0xc6, 0xf6, 0x86, 0xb6, 0x3f, 0xc4, + 0xd0, 0xa5, 0x4f, 0x77, 0xb6, 0xf3, 0xe9, 0xfb, 0xdd, 0xe3, 0x7c, 0x74, 0xf7, 0xc0, 0x10, 0xb8, + 0xd1, 0x55, 0x9c, 0x60, 0x4e, 0x52, 0x33, 0x58, 0xbe, 0xd8, 0xb4, 0x12, 0x46, 0xbc, 0xd2, 0xf9, + 0x60, 0x57, 0x8d, 0x1e, 0x5d, 0xff, 0xeb, 0xd7, 0xae, 0x17, 0xbe, 0xf5, 0x66, 0xe1, 0x5b, 0x6f, + 0x17, 0xbe, 0xf5, 0xcf, 0xc2, 0xb7, 0x5e, 0xdd, 0xfa, 0xb5, 0x37, 0xb7, 0x7e, 0xed, 0xed, 0xad, + 0x5f, 0xfb, 0xa5, 0x59, 0x25, 0x31, 0xdd, 0xd5, 0x05, 0xf9, 0xf2, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x86, 0xe1, 0x5b, 0x0a, 0xf4, 0x08, 0x00, 0x00, } func (m *LogRecord) Marshal() (dAtA []byte, err error) { @@ -482,6 +486,18 @@ func (m *RecordedSpan) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Verbose { + i-- + if m.Verbose { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } if m.RedactableLogs { i-- if m.RedactableLogs { @@ -860,6 +876,9 @@ func (m *RecordedSpan) Size() (n int) { if m.RedactableLogs { n += 2 } + if m.Verbose { + n += 3 + } return n } @@ -1859,6 +1878,26 @@ func (m *RecordedSpan) Unmarshal(dAtA []byte) error { } } m.RedactableLogs = bool(v != 0) + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Verbose", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRecordedSpan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Verbose = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRecordedSpan(dAtA[iNdEx:]) diff --git a/pkg/util/tracing/tracingpb/recorded_span.proto b/pkg/util/tracing/tracingpb/recorded_span.proto index fa3ed511150d..62e195a4e942 100644 --- a/pkg/util/tracing/tracingpb/recorded_span.proto +++ b/pkg/util/tracing/tracingpb/recorded_span.proto @@ -80,6 +80,9 @@ message RecordedSpan { bool redactable_logs = 15; // Events logged in the span. repeated LogRecord logs = 9 [(gogoproto.nullable) = false]; + // verbose indicates whether the span was recording in verbose mode at the + // time the recording was produced. + bool verbose = 16; // The ID of the goroutine on which the span was created. uint64 goroutine_id = 12 [(gogoproto.customname) = "GoroutineID"]; From 42bf6ca031a64d4700c467373bc6e00d96a51c3e Mon Sep 17 00:00:00 2001 From: Andrei Matei Date: Fri, 22 Oct 2021 11:14:10 -0400 Subject: [PATCH 012/205] util/tracing: get rid of "baggage tags" Baggage tags are tags that are inherited by all the spans derived from a parent (as opposed to a regular tag that just affects the span on which it is set). We were only using this mechanism for propagating information about verbose recordings across nodes - a verbose span would get a baggage tag that would be serialized on the wire like all the other baggage, and recognized on the other side. This baggage mechanism is too general for this one specific use case; all the code for generically passing baggage back and forth is not worth it. Also the propagation mechanism for the verbosity was confusing; there was a coupling before the baggage state and other state on the span, for no good reason. This patch gets rid of all of it, replacing it with specific code for propagating the verbose recording flag. Release note: None --- pkg/server/node_tenant_test.go | 2 +- pkg/util/log/trace.go | 2 +- pkg/util/tracing/crdbspan.go | 45 +-- pkg/util/tracing/doc.go | 1 - pkg/util/tracing/grpc_interceptor_test.go | 23 +- pkg/util/tracing/span.go | 17 -- pkg/util/tracing/span_inner.go | 29 -- pkg/util/tracing/tracer.go | 69 ++--- pkg/util/tracing/tracer_test.go | 22 +- .../tracing/tracingpb/recorded_span.pb.go | 267 ++++-------------- .../tracing/tracingpb/recorded_span.proto | 5 +- 11 files changed, 79 insertions(+), 403 deletions(-) diff --git a/pkg/server/node_tenant_test.go b/pkg/server/node_tenant_test.go index 8f02f1917cf1..63bdcbfd9ed5 100644 --- a/pkg/server/node_tenant_test.go +++ b/pkg/server/node_tenant_test.go @@ -98,12 +98,12 @@ func TestRedactRecordingForTenant(t *testing.T) { SpanID tracingpb.SpanID ParentSpanID tracingpb.SpanID Operation string - Baggage map[string]string Tags map[string]string StartTime time.Time Duration time.Duration RedactableLogs bool Logs []tracingpb.LogRecord + Verbose bool GoroutineID uint64 Finished bool StructuredRecords []tracingpb.StructuredRecord diff --git a/pkg/util/log/trace.go b/pkg/util/log/trace.go index 915b7b220376..b92f61d701d7 100644 --- a/pkg/util/log/trace.go +++ b/pkg/util/log/trace.go @@ -108,7 +108,7 @@ func eventInternal(sp *tracing.Span, el *ctxEventLog, isErr bool, entry *logEntr // TODO(obs-inf): figure out a way to signal that this is an error. We could // use a different "error" key (provided it shows up in LightStep). Things // like NetTraceIntegrator would need to be modified to understand the - // difference. We could also set a special Tag or Baggage on the span. See + // difference. We could also set a special Tag on the span. See // #8827 for more discussion. _ = isErr return diff --git a/pkg/util/tracing/crdbspan.go b/pkg/util/tracing/crdbspan.go index dd4a061e785f..84847b6d430c 100644 --- a/pkg/util/tracing/crdbspan.go +++ b/pkg/util/tracing/crdbspan.go @@ -98,9 +98,6 @@ type crdbSpanMu struct { finishedChildren []tracingpb.RecordedSpan } - // The Span's associated baggage. - baggage map[string]string - // tags are only captured when recording. These are tags that have been // added to this Span, and will be appended to the tags in logTags when // someone needs to actually observe the total set of tags that is a part of @@ -205,9 +202,6 @@ func (s *crdbSpan) enableRecording(recType RecordingType) { s.mu.Lock() defer s.mu.Unlock() s.mu.recording.recordingType.swap(recType) - if recType == RecordingVerbose { - s.setBaggageItemLocked(verboseTracingBaggageKey, "1") - } } // resetRecording clears any previously recorded info. @@ -232,15 +226,7 @@ func (s *crdbSpan) disableRecording() { } s.mu.Lock() defer s.mu.Unlock() - oldRecType := s.mu.recording.recordingType.swap(RecordingOff) - // We test the duration as a way to check if the Span has been finished. If it - // has, we don't want to do the call below as it might crash (at least if - // there's a netTr). - if (s.mu.duration == -1) && (oldRecType == RecordingVerbose) { - // Clear the verboseTracingBaggageKey baggage item, assuming that it was set by - // enableRecording(). - s.setBaggageItemLocked(verboseTracingBaggageKey, "") - } + s.mu.recording.recordingType.swap(RecordingOff) } // TraceID is part of the RegistrySpan interface. @@ -467,29 +453,6 @@ func (s *crdbSpan) recordInternalLocked(payload memorySizable, buffer *sizeLimit buffer.AddLast(payload) } -func (s *crdbSpan) setBaggageItemAndTag(restrictedKey, value string) { - s.mu.Lock() - defer s.mu.Unlock() - s.setBaggageItemLocked(restrictedKey, value) - // Don't set the tag if this is the special cased baggage item indicating - // span verbosity, as it is named nondescriptly and the recording knows - // how to display its verbosity independently. - if restrictedKey != verboseTracingBaggageKey { - s.setTagLocked(restrictedKey, attribute.StringValue(value)) - } -} - -func (s *crdbSpan) setBaggageItemLocked(restrictedKey, value string) { - if oldVal, ok := s.mu.baggage[restrictedKey]; ok && oldVal == value { - // No-op. - return - } - if s.mu.baggage == nil { - s.mu.baggage = make(map[string]string) - } - s.mu.baggage[restrictedKey] = value -} - // getStructuredEventsRecursively returns the structured events accumulated by // this span and its finished and still-open children. func (s *crdbSpan) getStructuredEventsRecursively( @@ -584,12 +547,6 @@ func (s *crdbSpan) getRecordingNoChildrenLocked( } } - if len(s.mu.baggage) > 0 { - rs.Baggage = make(map[string]string) - for k, v := range s.mu.baggage { - rs.Baggage[k] = v - } - } if wantTags { if s.logTags != nil { setLogTags(s.logTags.Get(), func(remappedKey string, tag *logtags.Tag) { diff --git a/pkg/util/tracing/doc.go b/pkg/util/tracing/doc.go index de2471348a2a..1ec86ecaaaa2 100644 --- a/pkg/util/tracing/doc.go +++ b/pkg/util/tracing/doc.go @@ -59,7 +59,6 @@ // - An operation name // - Timing information (start timestamp, duration) // - A set of zero or more tags (for annotation, visible when rendering spans) -// - A set of zero or more baggage items (data that crosses process boundaries) // - References to other spans (mediated by the relations described above) // - Recording data[7] (structured data/messages visible when rendering spans) // diff --git a/pkg/util/tracing/grpc_interceptor_test.go b/pkg/util/tracing/grpc_interceptor_test.go index 8fd5d7b87857..c604faa81068 100644 --- a/pkg/util/tracing/grpc_interceptor_test.go +++ b/pkg/util/tracing/grpc_interceptor_test.go @@ -54,8 +54,6 @@ func TestGRPCInterceptors(t *testing.T) { defer leaktest.AfterTest(t)() const ( - k = "test-baggage-key" - v = "test-baggage-value" magicValue = "magic-value" ) @@ -64,14 +62,6 @@ func TestGRPCInterceptors(t *testing.T) { if sp == nil { return nil, errors.New("no span in ctx") } - actV, ok := sp.GetRecording()[0].Baggage[k] - if !ok { - return nil, errors.Newf("%s not set in baggage", k) - } - if v != actV { - return nil, errors.Newf("expected %v, got %v instead", v, actV) - } - sp.RecordStructured(newTestStructured(magicValue)) sp.SetVerbose(true) // want the tags recs := sp.GetRecording() @@ -135,13 +125,8 @@ func TestGRPCInterceptors(t *testing.T) { })) conn, err := grpc.DialContext(context.Background(), ln.Addr().String(), grpc.WithInsecure(), - grpc.WithUnaryInterceptor(tracing.ClientInterceptor( - tr, func(sp *tracing.Span) { - sp.SetBaggageItem(k, v) - })), - grpc.WithStreamInterceptor(tracing.StreamClientInterceptor(tr, func(sp *tracing.Span) { - sp.SetBaggageItem(k, v) - })), + grpc.WithUnaryInterceptor(tracing.ClientInterceptor(tr, nil /* init */)), + grpc.WithStreamInterceptor(tracing.StreamClientInterceptor(tr, nil /* init */)), ) require.NoError(t, err) defer func() { @@ -233,9 +218,9 @@ func TestGRPCInterceptors(t *testing.T) { exp := fmt.Sprintf(` span: root span: /cockroach.testutils.grpcutils.GRPCTest/%[1]s - tags: span.kind=client test-baggage-key=test-baggage-value + tags: span.kind=client span: /cockroach.testutils.grpcutils.GRPCTest/%[1]s - tags: span.kind=server test-baggage-key=test-baggage-value + tags: span.kind=server event: structured=magic-value`, tc.name) require.NoError(t, tracing.CheckRecordedSpans(finalRecs, exp)) }) diff --git a/pkg/util/tracing/span.go b/pkg/util/tracing/span.go index 1751c6623c43..f8f959e869a4 100644 --- a/pkg/util/tracing/span.go +++ b/pkg/util/tracing/span.go @@ -201,17 +201,6 @@ func (sp *Span) SetTag(key string, value attribute.Value) { sp.i.SetTag(key, value) } -// SetBaggageItem attaches "baggage" to this span, a key:value pair that's -// propagated to all future descendants of this Span. Any attached baggage -// crosses RPC boundaries, and is copied transitively for every remote -// descendant. -func (sp *Span) SetBaggageItem(restrictedKey, value string) { - if sp.done() { - return - } - sp.i.SetBaggageItem(restrictedKey, value) -} - // TraceID retrieves a span's trace ID. func (sp *Span) TraceID() tracingpb.TraceID { return sp.i.TraceID() @@ -246,14 +235,8 @@ type SpanMeta struct { otelCtx oteltrace.SpanContext // If set, all spans derived from this context are being recorded. - // - // NB: at the time of writing, this is only ever set to RecordingVerbose - // and only if Baggage[verboseTracingBaggageKey] is set. recordingType RecordingType - // The Span's associated baggage. - Baggage map[string]string - // sterile is set if this span does not want to have children spans. In that // case, trying to create a child span will result in the would-be child being // a root span. This is useful for span corresponding to long-running diff --git a/pkg/util/tracing/span_inner.go b/pkg/util/tracing/span_inner.go index 5ee702dd171f..7b5062e4778f 100644 --- a/pkg/util/tracing/span_inner.go +++ b/pkg/util/tracing/span_inner.go @@ -124,21 +124,10 @@ func (s *spanInner) Meta() SpanMeta { var traceID tracingpb.TraceID var spanID tracingpb.SpanID var recordingType RecordingType - var baggage map[string]string var sterile bool if s.crdb != nil { traceID, spanID = s.crdb.traceID, s.crdb.spanID - s.crdb.mu.Lock() - defer s.crdb.mu.Unlock() - n := len(s.crdb.mu.baggage) - // In the common case, we have no baggage, so avoid making an empty map. - if n > 0 { - baggage = make(map[string]string, n) - } - for k, v := range s.crdb.mu.baggage { - baggage[k] = v - } recordingType = s.crdb.mu.recording.recordingType.load() sterile = s.isSterile() } @@ -152,7 +141,6 @@ func (s *spanInner) Meta() SpanMeta { spanID == 0 && !otelCtx.TraceID().IsValid() && recordingType == 0 && - baggage == nil && !sterile { return SpanMeta{} } @@ -161,7 +149,6 @@ func (s *spanInner) Meta() SpanMeta { spanID: spanID, otelCtx: otelCtx, recordingType: recordingType, - Baggage: baggage, sterile: sterile, } } @@ -248,22 +235,6 @@ func (s *spanInner) hasVerboseSink() bool { return true } -func (s *spanInner) SetBaggageItem(restrictedKey, value string) *spanInner { - if s.isNoop() { - return s - } - s.crdb.setBaggageItemAndTag(restrictedKey, value) - if s.otelSpan != nil { - // In OpenTelemetry, baggage is stored directly in the context, separately - // from the span. We don't go through the trouble. We'll set a tag on the - // current span, however. - s.otelSpan.SetAttributes(attribute.String(restrictedKey, value)) - } - // NB: nothing to do for net/trace. - - return s -} - // Tracer exports the tracer this span was created using. func (s *spanInner) Tracer() *Tracer { return s.tracer diff --git a/pkg/util/tracing/tracer.go b/pkg/util/tracing/tracer.go index f420ca58a951..aace2f7b8d9c 100644 --- a/pkg/util/tracing/tracer.go +++ b/pkg/util/tracing/tracer.go @@ -43,14 +43,6 @@ import ( "golang.org/x/net/trace" ) -// verboseTracingBaggageKey is set as Baggage on traces which are used for verbose tracing, -// meaning that a) spans derived from this one will not be no-op spans and b) they will -// start recording. -// -// This is "sb" for historical reasons; this concept used to be called "[S]now[b]all" tracing -// and since this string goes on the wire, it's a hassle to change it now. -const verboseTracingBaggageKey = "sb" - const ( // maxRecordedBytesPerSpan limits the size of logs and structured in a span; // use a comfortable limit. @@ -70,7 +62,6 @@ const ( // information in "carriers" to be transported across RPC boundaries. const ( prefixTracerState = "crdb-tracer-" - prefixBaggage = "crdb-baggage-" fieldNameTraceID = prefixTracerState + "traceid" fieldNameSpanID = prefixTracerState + "spanid" @@ -79,6 +70,13 @@ const ( fieldNameOtelTraceID = prefixTracerState + "otel_traceid" fieldNameOtelSpanID = prefixTracerState + "otel_spanid" + // verboseTracingKey is the carrier key indicating that the trace has verbose + // recording enabled. It means that a) spans derived from this one will not be + // no-op spans and b) they will start recording. + // + // The key is named the way it is for backwards compatibility reasons. + verboseTracingKey = "crdb-baggage-sb" + spanKindTagKey = "span.kind" ) @@ -138,9 +136,7 @@ var ZipkinCollector = settings.RegisterValidatedStringSetting( // // - forwarding events to x/net/trace instances // -// - recording traces. Recording is started automatically for spans that have -// the verboseTracingBaggageKey baggage and can be started explicitly as well. Recorded -// events can be retrieved at any time. +// - recording traces. Recorded events can be retrieved at any time. // // - OpenTelemetry tracing. This is implemented by maintaining a "shadow" // OpenTelemetry Span inside each of our spans. @@ -630,8 +626,8 @@ func (t *Tracer) startSpanGeneric( // First, create any external spans that we may need (OpenTelemetry, net/trace). // We do this early so that they are available when we construct the main Span, - // which makes it easier to avoid one-offs when populating the tags and baggage - // items for the top-level Span. + // which makes it easier to avoid one-offs when populating the tags items for + // the top-level Span. var otelSpan oteltrace.Span if otelTr := t.getOtelTracer(); otelTr != nil { parentSpan, parentContext := opts.otelContext() @@ -727,30 +723,13 @@ func (t *Tracer) startSpanGeneric( s.i.crdb.enableRecording(opts.recordingType()) } - // Copy baggage from parent. This similarly fans out over the various - // spans contained in Span. + // If the span is a local root, put it into the registry of active local root + // spans. Span.Finish will take care of removing it. // - // NB: this could be optimized. // NB: (opts.Parent != nil && opts.Parent.i.crdb == nil) is not possible at // the moment, but let's not rely on that. - if opts.Parent != nil && opts.Parent.i.crdb != nil { - opts.Parent.i.crdb.mu.Lock() - m := opts.Parent.i.crdb.mu.baggage - opts.Parent.i.crdb.mu.Unlock() - - for k, v := range m { - s.SetBaggageItem(k, v) - } - } else { - // Local root span - put it into the registry of active local root - // spans. `Span.Finish` takes care of deleting it again. + if opts.Parent == nil || opts.Parent.i.crdb == nil { t.activeSpansRegistry.addSpan(s.i.crdb) - - if !opts.RemoteParent.Empty() { - for k, v := range opts.RemoteParent.Baggage { - s.SetBaggageItem(k, v) - } - } } return maybeWrapCtx(ctx, &helper.octx, s) @@ -804,8 +783,9 @@ func (t *Tracer) InjectMetaInto(sm SpanMeta, carrier Carrier) error { carrier.Set(fieldNameTraceID, strconv.FormatUint(uint64(sm.traceID), 16)) carrier.Set(fieldNameSpanID, strconv.FormatUint(uint64(sm.spanID), 16)) - for k, v := range sm.Baggage { - carrier.Set(prefixBaggage+k, v) + if sm.recordingType == RecordingVerbose { + // This key is dictated by backwards compatibility. + carrier.Set(verboseTracingKey, "1") } if sm.otelCtx.TraceID().IsValid() { carrier.Set(fieldNameOtelTraceID, sm.otelCtx.TraceID().String()) @@ -825,7 +805,7 @@ func (t *Tracer) ExtractMetaFrom(carrier Carrier) (SpanMeta, error) { var spanID tracingpb.SpanID var otelTraceID oteltrace.TraceID var otelSpanID oteltrace.SpanID - var baggage map[string]string + var recordingType RecordingType iterFn := func(k, v string) error { switch k = strings.ToLower(k); k { @@ -855,13 +835,8 @@ func (t *Tracer) ExtractMetaFrom(carrier Carrier) (SpanMeta, error) { if err != nil { return err } - default: - if strings.HasPrefix(k, prefixBaggage) { - if baggage == nil { - baggage = make(map[string]string) - } - baggage[strings.TrimPrefix(k, prefixBaggage)] = v - } + case verboseTracingKey: + recordingType = RecordingVerbose } return nil } @@ -885,11 +860,6 @@ func (t *Tracer) ExtractMetaFrom(carrier Carrier) (SpanMeta, error) { return noopSpanMeta, nil } - var recordingType RecordingType - if baggage[verboseTracingBaggageKey] != "" { - recordingType = RecordingVerbose - } - var otelCtx oteltrace.SpanContext if otelTraceID.IsValid() && otelSpanID.IsValid() { otelCtx = otelCtx.WithRemote(true).WithTraceID(otelTraceID).WithSpanID(otelSpanID) @@ -900,7 +870,6 @@ func (t *Tracer) ExtractMetaFrom(carrier Carrier) (SpanMeta, error) { spanID: spanID, otelCtx: otelCtx, recordingType: recordingType, - Baggage: baggage, // The sterile field doesn't make it across the wire. The simple fact that // there was any tracing info in the carrier means that the parent span was // not sterile. diff --git a/pkg/util/tracing/tracer_test.go b/pkg/util/tracing/tracer_test.go index 43ee7c4602cd..ffc1474a68cf 100644 --- a/pkg/util/tracing/tracer_test.go +++ b/pkg/util/tracing/tracer_test.go @@ -385,10 +385,6 @@ func TestOtelTracer(t *testing.T) { // Put something in the span. s.Record("hello") - const testBaggageKey = "test-baggage" - const testBaggageVal = "test-val" - s.SetBaggageItem(testBaggageKey, testBaggageVal) - carrier := metadataCarrier{metadata.MD{}} if err := tr.InjectMetaInto(s.Meta(), carrier); err != nil { t.Fatal(err) @@ -400,28 +396,12 @@ func TestOtelTracer(t *testing.T) { t.Fatal(err) } - s2 := tr.StartSpan("child", WithParentAndManualCollection(wireSpanMeta)) - - exp := map[string]string{ - testBaggageKey: testBaggageVal, - } - require.Equal(t, exp, s2.Meta().Baggage) - // Note: we don't propagate baggage to the otel spans very well - we don't use - // the otel baggage features. We do, however, set attributes on the shadow - // spans when we can, which happens to be sufficient for this test. + tr.StartSpan("child", WithParentAndManualCollection(wireSpanMeta)) rs := sr.Started() require.Len(t, rs, 2) require.Len(t, rs[0].Events(), 1) require.Equal(t, "hello", rs[0].Events()[0].Name) - require.Len(t, rs[0].Attributes(), 1) - require.Equal(t, - attribute.KeyValue{Key: testBaggageKey, Value: attribute.StringValue(testBaggageVal)}, - rs[0].Attributes()[0]) - require.Len(t, rs[1].Attributes(), 1) - require.Equal(t, - attribute.KeyValue{Key: testBaggageKey, Value: attribute.StringValue(testBaggageVal)}, - rs[1].Attributes()[0]) require.Equal(t, rs[0].SpanContext().TraceID(), rs[1].Parent().TraceID()) require.Equal(t, rs[0].SpanContext().SpanID(), rs[1].Parent().SpanID()) } diff --git a/pkg/util/tracing/tracingpb/recorded_span.pb.go b/pkg/util/tracing/tracingpb/recorded_span.pb.go index bfcf3b5532dd..d6dc174f25c9 100644 --- a/pkg/util/tracing/tracingpb/recorded_span.pb.go +++ b/pkg/util/tracing/tracingpb/recorded_span.pb.go @@ -153,9 +153,6 @@ type RecordedSpan struct { ParentSpanID SpanID `protobuf:"varint,3,opt,name=parent_span_id,json=parentSpanId,proto3,customtype=SpanID" json:"parent_span_id"` // Operation name. Operation string `protobuf:"bytes,4,opt,name=operation,proto3" json:"operation,omitempty"` - // Baggage items get passed from parent to child spans (even through gRPC). - // Notably, verbose tracing uses a special baggage item. - Baggage map[string]string `protobuf:"bytes,5,rep,name=baggage,proto3" json:"baggage,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Tags associated with the span. Tags map[string]string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Time when the span was started. @@ -267,7 +264,6 @@ func init() { proto.RegisterType((*LogRecord_Field)(nil), "cockroach.util.tracing.tracingpb.LogRecord.Field") proto.RegisterType((*StructuredRecord)(nil), "cockroach.util.tracing.tracingpb.StructuredRecord") proto.RegisterType((*RecordedSpan)(nil), "cockroach.util.tracing.tracingpb.RecordedSpan") - proto.RegisterMapType((map[string]string)(nil), "cockroach.util.tracing.tracingpb.RecordedSpan.BaggageEntry") proto.RegisterMapType((map[string]string)(nil), "cockroach.util.tracing.tracingpb.RecordedSpan.TagsEntry") proto.RegisterType((*NormalizedSpan)(nil), "cockroach.util.tracing.tracingpb.NormalizedSpan") proto.RegisterMapType((map[string]string)(nil), "cockroach.util.tracing.tracingpb.NormalizedSpan.TagsEntry") @@ -278,60 +274,58 @@ func init() { } var fileDescriptor_e9b7b35ae7ab4ca8 = []byte{ - // 841 bytes of a gzipped FileDescriptorProto + // 816 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xdb, 0x36, - 0x14, 0xb6, 0x6c, 0x25, 0x92, 0x69, 0x2f, 0x71, 0x89, 0x1c, 0x54, 0x63, 0x90, 0x8d, 0x5c, 0x16, - 0xac, 0x80, 0xbc, 0x7a, 0xd8, 0x16, 0x64, 0x87, 0x61, 0x5e, 0xba, 0xc1, 0x45, 0x50, 0x14, 0x4a, - 0x76, 0xd9, 0xc5, 0xa0, 0x45, 0x86, 0x11, 0x2a, 0x8b, 0x02, 0x49, 0x15, 0xf0, 0xb0, 0x1f, 0xd1, - 0x63, 0x8f, 0xfb, 0x2b, 0xbb, 0xe5, 0xd8, 0xdd, 0x8a, 0x1d, 0xb2, 0xcd, 0x01, 0xf6, 0x3b, 0x06, - 0x52, 0x94, 0xdc, 0x38, 0x05, 0x0c, 0xd7, 0x3d, 0x49, 0x7c, 0xfc, 0xbe, 0x8f, 0x8f, 0x7c, 0xdf, - 0x7b, 0xe0, 0xf3, 0x5c, 0xc6, 0xc9, 0x40, 0x72, 0x14, 0xc5, 0x29, 0x2d, 0xbf, 0xd9, 0x74, 0xc0, - 0x49, 0xc4, 0x38, 0x26, 0x78, 0x22, 0x32, 0x94, 0x06, 0x19, 0x67, 0x92, 0xc1, 0x7e, 0xc4, 0xa2, - 0x17, 0x9c, 0xa1, 0xe8, 0x2a, 0x50, 0xac, 0xc0, 0xa0, 0x83, 0x8a, 0xd5, 0x3d, 0xa0, 0x8c, 0x32, - 0x0d, 0x1e, 0xa8, 0xbf, 0x82, 0xd7, 0x7d, 0x48, 0x19, 0xa3, 0x09, 0x19, 0xe8, 0xd5, 0x34, 0xbf, - 0x1c, 0xa0, 0x74, 0x6e, 0xb6, 0x7a, 0xab, 0x5b, 0x32, 0x9e, 0x11, 0x21, 0xd1, 0x2c, 0x33, 0x00, - 0x7f, 0x15, 0x80, 0x73, 0x8e, 0x64, 0xcc, 0x4c, 0x4e, 0x87, 0xff, 0xd5, 0x41, 0xf3, 0x8c, 0xd1, - 0x50, 0xa7, 0x0b, 0x8f, 0x81, 0xad, 0x04, 0x3c, 0xab, 0x6f, 0x1d, 0xb5, 0x86, 0xdd, 0xa0, 0x20, - 0x07, 0x25, 0x39, 0xb8, 0x28, 0xd5, 0x47, 0xee, 0xf5, 0x4d, 0xaf, 0xf6, 0xea, 0xef, 0x9e, 0x15, - 0x6a, 0x06, 0xc4, 0xe0, 0x01, 0x26, 0x19, 0x27, 0x11, 0x92, 0x04, 0x4f, 0x2e, 0x63, 0x92, 0x60, - 0xe1, 0xd5, 0xfb, 0x8d, 0xa3, 0xd6, 0xf0, 0x71, 0xb0, 0xee, 0xde, 0x41, 0x95, 0x41, 0xf0, 0xa3, - 0x62, 0x8e, 0x6c, 0xa5, 0x1e, 0x76, 0x96, 0x8a, 0x3a, 0x2c, 0xe0, 0x73, 0xe0, 0xcc, 0x88, 0x10, - 0x88, 0x12, 0xaf, 0xd1, 0xb7, 0x8e, 0x9a, 0xa3, 0xaf, 0x15, 0xf0, 0xaf, 0x9b, 0x5e, 0x40, 0x63, - 0x79, 0x95, 0x4f, 0x83, 0x88, 0xcd, 0x06, 0xd5, 0x69, 0x58, 0x95, 0x01, 0xa3, 0x48, 0x06, 0xa1, - 0xfe, 0xa0, 0x69, 0x42, 0xce, 0x25, 0x8f, 0x53, 0x1a, 0x96, 0x32, 0x5d, 0x0a, 0x76, 0xb4, 0x36, - 0xec, 0x80, 0xc6, 0x0b, 0x32, 0xd7, 0x37, 0x6f, 0x86, 0xea, 0x17, 0x9e, 0x81, 0x9d, 0x97, 0x28, - 0xc9, 0x89, 0x57, 0xdf, 0xea, 0xa8, 0x42, 0xe4, 0xf0, 0x37, 0xd0, 0x39, 0x97, 0x3c, 0x8f, 0x64, - 0xce, 0x09, 0xde, 0xfa, 0xb9, 0x03, 0xe0, 0x64, 0x68, 0x9e, 0x30, 0x84, 0x75, 0x76, 0xad, 0xe1, - 0xc1, 0x3d, 0xf2, 0xf7, 0xe9, 0x3c, 0x2c, 0x41, 0x87, 0x7f, 0x3a, 0xa0, 0x1d, 0x1a, 0x4b, 0x9e, - 0x67, 0x28, 0x85, 0x5f, 0x01, 0x57, 0x3d, 0x3f, 0x99, 0xc4, 0x58, 0x1f, 0x6f, 0x8f, 0xba, 0xe6, - 0x7e, 0xce, 0x85, 0x8a, 0x8f, 0x4f, 0x17, 0xcb, 0xdf, 0xd0, 0xd1, 0xd8, 0x31, 0x86, 0x8f, 0x81, - 0xa3, 0x0c, 0xad, 0x58, 0x75, 0xcd, 0xf2, 0x0c, 0x6b, 0x57, 0xa9, 0x6a, 0x92, 0xf9, 0x0b, 0x77, - 0x15, 0x70, 0x8c, 0xe1, 0x29, 0xd8, 0xcb, 0x10, 0x27, 0xa9, 0x9c, 0x94, 0xcc, 0x86, 0x66, 0xfa, - 0xf7, 0x98, 0xed, 0xe7, 0x1a, 0x67, 0xf8, 0xed, 0x6c, 0xb9, 0xc2, 0xf0, 0x53, 0xd0, 0x64, 0x19, - 0x29, 0xac, 0xeb, 0xd9, 0xba, 0x48, 0xcb, 0x00, 0xfc, 0x19, 0x38, 0x53, 0x44, 0xa9, 0xf2, 0xc5, - 0x8e, 0xf6, 0xdc, 0xb7, 0xeb, 0x3d, 0xf7, 0xee, 0x73, 0x04, 0xa3, 0x82, 0xfd, 0x24, 0x95, 0x7c, - 0x1e, 0x96, 0x5a, 0xf0, 0x0c, 0xd8, 0x12, 0x51, 0xe1, 0xed, 0x6a, 0xcd, 0xe3, 0x0d, 0x35, 0x2f, - 0x10, 0x15, 0x85, 0xa0, 0x56, 0x81, 0x3f, 0x00, 0x20, 0x24, 0xe2, 0x72, 0xa2, 0x6b, 0xee, 0x6c, - 0x50, 0xf3, 0xa6, 0xe6, 0xa9, 0x1d, 0xf8, 0x1d, 0x70, 0xcb, 0x0e, 0xf6, 0x5c, 0x2d, 0xf1, 0xf0, - 0x9e, 0xc4, 0xa9, 0x01, 0x14, 0x0a, 0xaf, 0x95, 0x42, 0x45, 0x82, 0x9f, 0x81, 0x7d, 0x5e, 0x59, - 0x74, 0x92, 0x30, 0x2a, 0xbc, 0xfd, 0xbe, 0x75, 0xe4, 0x86, 0x7b, 0xcb, 0xf0, 0x19, 0xa3, 0x02, - 0x3e, 0x01, 0xb6, 0xde, 0x6d, 0xea, 0xcb, 0x3f, 0xda, 0xa0, 0x89, 0x4d, 0xfb, 0x6a, 0x3a, 0xf4, - 0x80, 0xf3, 0x92, 0xf0, 0x29, 0x13, 0xc4, 0xeb, 0xe8, 0x73, 0xca, 0x25, 0x1c, 0x82, 0x36, 0x65, - 0x9c, 0xe5, 0x32, 0x4e, 0xb5, 0x0d, 0xdb, 0xda, 0x16, 0xfb, 0x8b, 0x9b, 0x5e, 0xeb, 0xa7, 0x32, - 0x3e, 0x3e, 0x0d, 0x5b, 0x15, 0x68, 0x8c, 0x61, 0x17, 0xb8, 0x97, 0x71, 0x1a, 0x8b, 0x2b, 0x82, - 0xbd, 0x4f, 0xb4, 0x5c, 0xb5, 0x86, 0x14, 0x40, 0x51, 0x75, 0xd8, 0xa4, 0x18, 0xc0, 0xc2, 0xdb, - 0xd3, 0xe9, 0x0f, 0xd7, 0xa7, 0xbf, 0xda, 0x9d, 0xe6, 0x16, 0x0f, 0xc4, 0x4a, 0x5c, 0x74, 0x4f, - 0x40, 0xfb, 0x5d, 0xbf, 0xbc, 0x67, 0x74, 0x1c, 0xdc, 0x19, 0x1d, 0x66, 0x04, 0x9c, 0xd4, 0x8f, - 0xad, 0xee, 0x37, 0xa0, 0x59, 0xf9, 0x62, 0x13, 0xe2, 0x89, 0xfd, 0xfa, 0xf7, 0x5e, 0xed, 0xa9, - 0xed, 0x82, 0x4e, 0xeb, 0xa9, 0xed, 0xb6, 0x3a, 0xed, 0xc3, 0x3f, 0x6c, 0xb0, 0xf7, 0x8c, 0xf1, - 0x19, 0x4a, 0xe2, 0x5f, 0x4d, 0x57, 0xdf, 0xe9, 0x12, 0x6b, 0xb5, 0x4b, 0x9e, 0x19, 0x3b, 0x17, - 0x63, 0xf9, 0x64, 0xfd, 0x93, 0xdc, 0x55, 0x5f, 0x63, 0xe8, 0xc6, 0xf6, 0x86, 0xb6, 0x3f, 0xc4, - 0xd0, 0xa5, 0x4f, 0x77, 0xb6, 0xf3, 0xe9, 0xfb, 0xdd, 0xe3, 0x7c, 0x74, 0xf7, 0xc0, 0x10, 0xb8, - 0xd1, 0x55, 0x9c, 0x60, 0x4e, 0x52, 0x33, 0x58, 0xbe, 0xd8, 0xb4, 0x12, 0x46, 0xbc, 0xd2, 0xf9, - 0x60, 0x57, 0x8d, 0x1e, 0x5d, 0xff, 0xeb, 0xd7, 0xae, 0x17, 0xbe, 0xf5, 0x66, 0xe1, 0x5b, 0x6f, - 0x17, 0xbe, 0xf5, 0xcf, 0xc2, 0xb7, 0x5e, 0xdd, 0xfa, 0xb5, 0x37, 0xb7, 0x7e, 0xed, 0xed, 0xad, - 0x5f, 0xfb, 0xa5, 0x59, 0x25, 0x31, 0xdd, 0xd5, 0x05, 0xf9, 0xf2, 0xff, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x86, 0xe1, 0x5b, 0x0a, 0xf4, 0x08, 0x00, 0x00, + 0x14, 0xb6, 0x6c, 0xc5, 0x92, 0x68, 0x2f, 0x71, 0x89, 0x1c, 0x54, 0x63, 0x90, 0x8d, 0x5c, 0x16, + 0xac, 0x80, 0xbc, 0x7a, 0xd8, 0x16, 0xe4, 0x32, 0xcc, 0x4b, 0x37, 0x38, 0x08, 0x8a, 0x42, 0xc9, + 0x69, 0x17, 0x83, 0x16, 0x19, 0x46, 0xa8, 0x2c, 0x0a, 0x24, 0x55, 0xc0, 0xc3, 0x7e, 0x44, 0x8f, + 0x3d, 0xee, 0xaf, 0x0c, 0xd8, 0x21, 0xc7, 0x1e, 0x8b, 0x1d, 0xb2, 0xcd, 0x01, 0xf6, 0x3b, 0x06, + 0xd2, 0x94, 0xbc, 0x38, 0x05, 0x8c, 0xc4, 0x3b, 0x89, 0x7c, 0xfc, 0xbe, 0x8f, 0xef, 0xe9, 0x7d, + 0x8f, 0xe0, 0xf3, 0x42, 0x26, 0xe9, 0x40, 0x72, 0x14, 0x27, 0x19, 0x2d, 0xbf, 0xf9, 0x74, 0xc0, + 0x49, 0xcc, 0x38, 0x26, 0x78, 0x22, 0x72, 0x94, 0x85, 0x39, 0x67, 0x92, 0xc1, 0x7e, 0xcc, 0xe2, + 0xd7, 0x9c, 0xa1, 0xf8, 0x2a, 0x54, 0xac, 0xd0, 0xa0, 0xc3, 0x8a, 0xd5, 0xdd, 0xa7, 0x8c, 0x32, + 0x0d, 0x1e, 0xa8, 0xd5, 0x92, 0xd7, 0x7d, 0x4a, 0x19, 0xa3, 0x29, 0x19, 0xe8, 0xdd, 0xb4, 0xb8, + 0x1c, 0xa0, 0x6c, 0x6e, 0x8e, 0x7a, 0xeb, 0x47, 0x32, 0x99, 0x11, 0x21, 0xd1, 0x2c, 0x37, 0x80, + 0x60, 0x1d, 0x80, 0x0b, 0x8e, 0x64, 0xc2, 0x4c, 0x4e, 0x07, 0xff, 0xd4, 0x81, 0x77, 0xc6, 0x68, + 0xa4, 0xd3, 0x85, 0x47, 0xc0, 0x56, 0x02, 0xbe, 0xd5, 0xb7, 0x0e, 0x5b, 0xc3, 0x6e, 0xb8, 0x24, + 0x87, 0x25, 0x39, 0xbc, 0x28, 0xd5, 0x47, 0xee, 0xf5, 0x4d, 0xaf, 0xf6, 0xf6, 0xcf, 0x9e, 0x15, + 0x69, 0x06, 0xc4, 0xe0, 0x09, 0x26, 0x39, 0x27, 0x31, 0x92, 0x04, 0x4f, 0x2e, 0x13, 0x92, 0x62, + 0xe1, 0xd7, 0xfb, 0x8d, 0xc3, 0xd6, 0xf0, 0x79, 0xb8, 0xa9, 0xee, 0xb0, 0xca, 0x20, 0xfc, 0x41, + 0x31, 0x47, 0xb6, 0x52, 0x8f, 0x3a, 0x2b, 0x45, 0x1d, 0x16, 0xf0, 0x15, 0x70, 0x66, 0x44, 0x08, + 0x44, 0x89, 0xdf, 0xe8, 0x5b, 0x87, 0xde, 0xe8, 0x6b, 0x05, 0xfc, 0xe3, 0xa6, 0x17, 0xd2, 0x44, + 0x5e, 0x15, 0xd3, 0x30, 0x66, 0xb3, 0x41, 0x75, 0x1b, 0x56, 0x6d, 0xc0, 0x28, 0x96, 0x61, 0xa4, + 0x3f, 0x68, 0x9a, 0x92, 0x73, 0xc9, 0x93, 0x8c, 0x46, 0xa5, 0x4c, 0x97, 0x82, 0x1d, 0xad, 0x0d, + 0x3b, 0xa0, 0xf1, 0x9a, 0xcc, 0x75, 0xe5, 0x5e, 0xa4, 0x96, 0xf0, 0x0c, 0xec, 0xbc, 0x41, 0x69, + 0x41, 0xfc, 0xfa, 0x56, 0x57, 0x2d, 0x45, 0x0e, 0x7e, 0x01, 0x9d, 0x73, 0xc9, 0x8b, 0x58, 0x16, + 0x9c, 0xe0, 0xad, 0x7f, 0x77, 0x08, 0x9c, 0x1c, 0xcd, 0x53, 0x86, 0xb0, 0xce, 0xae, 0x35, 0xdc, + 0xbf, 0x47, 0xfe, 0x2e, 0x9b, 0x47, 0x25, 0xe8, 0xe0, 0xf7, 0x26, 0x68, 0x47, 0xc6, 0x92, 0xe7, + 0x39, 0xca, 0xe0, 0x57, 0xc0, 0x55, 0xbf, 0x9f, 0x4c, 0x12, 0xac, 0xaf, 0xb7, 0x47, 0x5d, 0x53, + 0x9f, 0x73, 0xa1, 0xe2, 0xe3, 0x93, 0xc5, 0x6a, 0x19, 0x39, 0x1a, 0x3b, 0xc6, 0xf0, 0x39, 0x70, + 0x94, 0xa1, 0x15, 0xab, 0xae, 0x59, 0xbe, 0x61, 0x35, 0x95, 0xaa, 0x26, 0x99, 0x55, 0xd4, 0x54, + 0xc0, 0x31, 0x86, 0x27, 0x60, 0x37, 0x47, 0x9c, 0x64, 0x72, 0x52, 0x32, 0x1b, 0x9a, 0x19, 0xdc, + 0x63, 0xb6, 0x5f, 0x69, 0x9c, 0xe1, 0xb7, 0xf3, 0xd5, 0x0e, 0xc3, 0x4f, 0x81, 0xc7, 0x72, 0xb2, + 0xb4, 0xae, 0x6f, 0xeb, 0x26, 0xad, 0x02, 0xf0, 0x0c, 0xd8, 0x12, 0x51, 0xe1, 0x37, 0xb5, 0xe1, + 0x8e, 0x36, 0x1b, 0xee, 0xbf, 0xff, 0x22, 0xbc, 0x40, 0x54, 0xbc, 0xc8, 0x24, 0x9f, 0x47, 0x5a, + 0x05, 0x7e, 0x0f, 0x80, 0x90, 0x88, 0xcb, 0x89, 0x6e, 0x8e, 0xf3, 0x80, 0xe6, 0x78, 0x9a, 0xa7, + 0x4e, 0xe0, 0xb7, 0xc0, 0x2d, 0x47, 0xcd, 0x77, 0xb5, 0xc4, 0xd3, 0x7b, 0x12, 0x27, 0x06, 0xb0, + 0x54, 0x78, 0xa7, 0x14, 0x2a, 0x12, 0xfc, 0x0c, 0xec, 0xf1, 0xca, 0x4b, 0x93, 0x94, 0x51, 0xe1, + 0xef, 0xf5, 0xad, 0x43, 0x37, 0xda, 0x5d, 0x85, 0xcf, 0x18, 0x15, 0xf0, 0x05, 0xb0, 0xf5, 0xa9, + 0xa7, 0x8b, 0x7f, 0xf6, 0x80, 0x69, 0x33, 0x73, 0xa6, 0xe9, 0xd0, 0x07, 0xce, 0x1b, 0xc2, 0xa7, + 0x4c, 0x10, 0xbf, 0xa3, 0xef, 0x29, 0xb7, 0x70, 0x08, 0xda, 0x94, 0x71, 0x56, 0xc8, 0x24, 0xd3, + 0x7e, 0x69, 0xeb, 0xfe, 0xed, 0x2d, 0x6e, 0x7a, 0xad, 0x1f, 0xcb, 0xf8, 0xf8, 0x24, 0x6a, 0x55, + 0xa0, 0x31, 0x86, 0x5d, 0xe0, 0x5e, 0x26, 0x59, 0x22, 0xae, 0x08, 0xf6, 0x3f, 0xd1, 0x72, 0xd5, + 0x1e, 0x52, 0x00, 0x45, 0x35, 0x0a, 0x93, 0xe5, 0x4b, 0x29, 0xfc, 0x5d, 0x9d, 0xfe, 0x70, 0x73, + 0xfa, 0xeb, 0x63, 0x64, 0xaa, 0x78, 0x22, 0xd6, 0xe2, 0xa2, 0xfb, 0x0d, 0xf0, 0xaa, 0xde, 0x7e, + 0x64, 0xc0, 0xf7, 0xef, 0x0c, 0xb8, 0x19, 0xd4, 0xe3, 0xfa, 0x91, 0x75, 0x6c, 0xbf, 0xfb, 0xb5, + 0x57, 0x3b, 0xb5, 0xdd, 0x9d, 0x4e, 0xf3, 0xd4, 0x76, 0x41, 0xa7, 0x75, 0x6a, 0xbb, 0xad, 0x4e, + 0xfb, 0xe0, 0x37, 0x1b, 0xec, 0xbe, 0x64, 0x7c, 0x86, 0xd2, 0xe4, 0x67, 0x33, 0x48, 0x77, 0x8c, + 0x69, 0xad, 0x1b, 0xf3, 0xa5, 0x31, 0xe6, 0xf2, 0x25, 0x3c, 0xde, 0x5c, 0xdc, 0x5d, 0xf5, 0x0d, + 0xd6, 0x6c, 0x6c, 0x6f, 0x4d, 0xfb, 0x31, 0xd6, 0x2c, 0x1d, 0xb7, 0xb3, 0x9d, 0xe3, 0x3e, 0xee, + 0x03, 0xe7, 0x7f, 0xf7, 0x01, 0x8c, 0x80, 0x1b, 0x5f, 0x25, 0x29, 0xe6, 0x24, 0x33, 0x4f, 0xc4, + 0x17, 0x0f, 0xed, 0x84, 0x11, 0xaf, 0x74, 0x1e, 0xed, 0xad, 0xd1, 0xb3, 0xeb, 0xbf, 0x83, 0xda, + 0xf5, 0x22, 0xb0, 0xde, 0x2f, 0x02, 0xeb, 0xc3, 0x22, 0xb0, 0xfe, 0x5a, 0x04, 0xd6, 0xdb, 0xdb, + 0xa0, 0xf6, 0xfe, 0x36, 0xa8, 0x7d, 0xb8, 0x0d, 0x6a, 0x3f, 0x79, 0x55, 0x12, 0xd3, 0xa6, 0x6e, + 0xc8, 0x97, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x5d, 0x84, 0xfc, 0x67, 0x08, 0x00, 0x00, } func (m *LogRecord) Marshal() (dAtA []byte, err error) { @@ -591,30 +585,6 @@ func (m *RecordedSpan) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x32 } } - if len(m.Baggage) > 0 { - keysForBaggage := make([]string, 0, len(m.Baggage)) - for k := range m.Baggage { - keysForBaggage = append(keysForBaggage, string(k)) - } - github_com_gogo_protobuf_sortkeys.Strings(keysForBaggage) - for iNdEx := len(keysForBaggage) - 1; iNdEx >= 0; iNdEx-- { - v := m.Baggage[string(keysForBaggage[iNdEx])] - baseI := i - i -= len(v) - copy(dAtA[i:], v) - i = encodeVarintRecordedSpan(dAtA, i, uint64(len(v))) - i-- - dAtA[i] = 0x12 - i -= len(keysForBaggage[iNdEx]) - copy(dAtA[i:], keysForBaggage[iNdEx]) - i = encodeVarintRecordedSpan(dAtA, i, uint64(len(keysForBaggage[iNdEx]))) - i-- - dAtA[i] = 0xa - i = encodeVarintRecordedSpan(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0x2a - } - } if len(m.Operation) > 0 { i -= len(m.Operation) copy(dAtA[i:], m.Operation) @@ -835,14 +805,6 @@ func (m *RecordedSpan) Size() (n int) { if l > 0 { n += 1 + l + sovRecordedSpan(uint64(l)) } - if len(m.Baggage) > 0 { - for k, v := range m.Baggage { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovRecordedSpan(uint64(len(k))) + 1 + len(v) + sovRecordedSpan(uint64(len(v))) - n += mapEntrySize + 1 + sovRecordedSpan(uint64(mapEntrySize)) - } - } if len(m.Tags) > 0 { for k, v := range m.Tags { _ = k @@ -1431,133 +1393,6 @@ func (m *RecordedSpan) Unmarshal(dAtA []byte) error { } m.Operation = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Baggage", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRecordedSpan - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthRecordedSpan - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthRecordedSpan - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Baggage == nil { - m.Baggage = make(map[string]string) - } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRecordedSpan - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRecordedSpan - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthRecordedSpan - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthRecordedSpan - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRecordedSpan - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthRecordedSpan - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue < 0 { - return ErrInvalidLengthRecordedSpan - } - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipRecordedSpan(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthRecordedSpan - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Baggage[mapkey] = mapvalue - iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) diff --git a/pkg/util/tracing/tracingpb/recorded_span.proto b/pkg/util/tracing/tracingpb/recorded_span.proto index 62e195a4e942..23b856b6b4b2 100644 --- a/pkg/util/tracing/tracingpb/recorded_span.proto +++ b/pkg/util/tracing/tracingpb/recorded_span.proto @@ -57,9 +57,6 @@ message RecordedSpan { uint64 parent_span_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "ParentSpanID", (gogoproto.customtype) = "SpanID"]; // Operation name. string operation = 4; - // Baggage items get passed from parent to child spans (even through gRPC). - // Notably, verbose tracing uses a special baggage item. - map baggage = 5; // Tags associated with the span. map tags = 6; // Time when the span was started. @@ -99,7 +96,7 @@ message RecordedSpan { // DeprecatedInternalStructured only stores the Payloads. repeated StructuredRecord structured_records = 14 [(gogoproto.nullable) = false]; - reserved 10,11; + reserved 5,10,11; } // NormalizedSpan is a representation of a RecordedSpan from a trace with all From 4df8ac262e72c58cb4e09dcf1beb0b8ff6fdde27 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Mon, 20 Sep 2021 21:09:18 -0400 Subject: [PATCH 013/205] Re-introduce "kv,migration: rm code handling legacy raft truncated state" This reverts commit ef1dd6f251ec2bfba929766046f72ca8c8c87708. #70432 reverted #69887, as temporary stop-gap until we release the first 21.2 beta. See the discussion over on #70432 for why we want to queue up this revert to the original revert; this should only be merged after #69826 lands. Release note: None --- pkg/cli/debug_check_store.go | 8 +- pkg/clusterversion/cockroach_versions.go | 42 -- pkg/clusterversion/key_string.go | 86 ++- pkg/keys/constants.go | 11 +- pkg/keys/doc.go | 16 +- pkg/keys/keys.go | 57 +- pkg/keys/keys_test.go | 3 - pkg/keys/printer.go | 4 +- pkg/keys/printer_test.go | 4 - .../kvserver/batcheval/cmd_end_transaction.go | 35 +- pkg/kv/kvserver/batcheval/cmd_migrate.go | 39 -- pkg/kv/kvserver/batcheval/cmd_truncate_log.go | 24 - .../batcheval/cmd_truncate_log_test.go | 97 +-- pkg/kv/kvserver/debug_print.go | 12 +- pkg/kv/kvserver/kvserverpb/state.pb.go | 183 ++---- pkg/kv/kvserver/kvserverpb/state.proto | 11 +- pkg/kv/kvserver/raft.pb.go | 192 +++--- pkg/kv/kvserver/raft.proto | 17 +- .../kvserver/rditer/replica_data_iter_test.go | 4 - pkg/kv/kvserver/replica_application_result.go | 6 - .../replica_application_state_machine.go | 74 +-- pkg/kv/kvserver/replica_command.go | 44 +- pkg/kv/kvserver/replica_consistency.go | 11 +- pkg/kv/kvserver/replica_proposal.go | 40 +- pkg/kv/kvserver/replica_raft.go | 101 ++- .../kvserver/replica_raft_truncation_test.go | 49 +- pkg/kv/kvserver/replica_raftstorage.go | 32 +- pkg/kv/kvserver/replica_test.go | 3 - pkg/kv/kvserver/stateloader/initial.go | 26 +- pkg/kv/kvserver/stateloader/stateloader.go | 374 ++--------- pkg/kv/kvserver/store_init.go | 12 +- pkg/kv/kvserver/store_snapshot.go | 28 +- pkg/kv/kvserver/store_test.go | 3 +- .../truncated_state} | 0 .../truncated_state_migration/pre_migration | 26 - pkg/kv/kvserver/testing_knobs.go | 4 - pkg/migration/migrations/BUILD.bazel | 3 - pkg/migration/migrations/migrations.go | 10 - pkg/migration/migrations/separated_intents.go | 13 + pkg/migration/migrations/truncated_state.go | 95 --- .../truncated_state_external_test.go | 153 ----- pkg/storage/enginepb/mvcc.pb.go | 583 ++---------------- pkg/storage/enginepb/mvcc.proto | 27 - 43 files changed, 457 insertions(+), 2105 deletions(-) rename pkg/kv/kvserver/testdata/{truncated_state_migration/migration => truncated_state/truncated_state} (100%) delete mode 100644 pkg/kv/kvserver/testdata/truncated_state_migration/pre_migration delete mode 100644 pkg/migration/migrations/truncated_state.go delete mode 100644 pkg/migration/migrations/truncated_state_external_test.go diff --git a/pkg/cli/debug_check_store.go b/pkg/cli/debug_check_store.go index 7692b92d401c..240eb612464e 100644 --- a/pkg/cli/debug_check_store.go +++ b/pkg/cli/debug_check_store.go @@ -252,7 +252,7 @@ func checkStoreRaftState( return err } getReplicaInfo(rangeID).committedIndex = hs.Commit - case bytes.Equal(suffix, keys.LocalRaftTruncatedStateLegacySuffix): + case bytes.Equal(suffix, keys.LocalRaftTruncatedStateSuffix): var trunc roachpb.RaftTruncatedState if err := kv.Value.GetProto(&trunc); err != nil { return err @@ -264,12 +264,6 @@ func checkStoreRaftState( return err } getReplicaInfo(rangeID).appliedIndex = state.RaftAppliedIndex - case bytes.Equal(suffix, keys.LocalRaftAppliedIndexLegacySuffix): - idx, err := kv.Value.GetInt() - if err != nil { - return err - } - getReplicaInfo(rangeID).appliedIndex = uint64(idx) case bytes.Equal(suffix, keys.LocalRaftLogSuffix): _, index, err := encoding.DecodeUint64Ascending(detail) if err != nil { diff --git a/pkg/clusterversion/cockroach_versions.go b/pkg/clusterversion/cockroach_versions.go index 4c451522b750..543f40892287 100644 --- a/pkg/clusterversion/cockroach_versions.go +++ b/pkg/clusterversion/cockroach_versions.go @@ -156,32 +156,6 @@ const ( // v21.1 versions. // - // replacedTruncatedAndRangeAppliedStateMigration stands in for - // TruncatedAndRangeAppliedStateMigration which was re-introduced after the - // migration job was introduced. This is necessary because the jobs - // infrastructure used to run this migration in v21.1 and its later alphas - // was introduced after this version was first introduced. Later code in the - // release relies on the job to run the migration but the job relies on - // its startup migrations having been run. Versions associated with long - // running migrations must follow deletedLongRunningMigrations. - replacedTruncatedAndRangeAppliedStateMigration - // replacedPostTruncatedAndRangeAppliedStateMigration is like the above - // version. See its comment. - replacedPostTruncatedAndRangeAppliedStateMigration - // TruncatedAndRangeAppliedStateMigration is part of the migration to stop - // using the legacy truncated state within KV. After the migration, we'll be - // using the unreplicated truncated state and the RangeAppliedState on all - // ranges. Callers that wish to assert on there no longer being any legacy - // will be able to do so after PostTruncatedAndRangeAppliedStateMigration is - // active. This lets remove any holdover code handling the possibility of - // replicated truncated state in 21.2. - // - // TODO(irfansharif): Do the above in 21.2. - TruncatedAndRangeAppliedStateMigration - // PostTruncatedAndRangeAppliedStateMigration is used to purge all replicas - // using the replicated legacy TruncatedState. It's also used in asserting - // that no replicated truncated state representation is found. - PostTruncatedAndRangeAppliedStateMigration // V21_1 is CockroachDB v21.1. It's used for all v21.1.x patch releases. // // TODO(irfansharif): This can be removed as part of #71708 (bump @@ -328,22 +302,6 @@ const ( // to be added (i.e., when cutting the final release candidate). var versionsSingleton = keyedVersions{ // v21.1 versions. - { - Key: replacedTruncatedAndRangeAppliedStateMigration, - Version: roachpb.Version{Major: 20, Minor: 2, Internal: 14}, - }, - { - Key: replacedPostTruncatedAndRangeAppliedStateMigration, - Version: roachpb.Version{Major: 20, Minor: 2, Internal: 16}, - }, - { - Key: TruncatedAndRangeAppliedStateMigration, - Version: roachpb.Version{Major: 20, Minor: 2, Internal: 22}, - }, - { - Key: PostTruncatedAndRangeAppliedStateMigration, - Version: roachpb.Version{Major: 20, Minor: 2, Internal: 24}, - }, { // V21_1 is CockroachDB v21.1. It's used for all v21.1.x patch releases. Key: V21_1, diff --git a/pkg/clusterversion/key_string.go b/pkg/clusterversion/key_string.go index eda65899c2bb..1d2e73449f2f 100644 --- a/pkg/clusterversion/key_string.go +++ b/pkg/clusterversion/key_string.go @@ -8,54 +8,50 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[replacedTruncatedAndRangeAppliedStateMigration-0] - _ = x[replacedPostTruncatedAndRangeAppliedStateMigration-1] - _ = x[TruncatedAndRangeAppliedStateMigration-2] - _ = x[PostTruncatedAndRangeAppliedStateMigration-3] - _ = x[V21_1-4] - _ = x[Start21_1PLUS-5] - _ = x[Start21_2-6] - _ = x[JoinTokensTable-7] - _ = x[AcquisitionTypeInLeaseHistory-8] - _ = x[SerializeViewUDTs-9] - _ = x[ExpressionIndexes-10] - _ = x[DeleteDeprecatedNamespaceTableDescriptorMigration-11] - _ = x[FixDescriptors-12] - _ = x[DatabaseRoleSettings-13] - _ = x[TenantUsageTable-14] - _ = x[SQLInstancesTable-15] - _ = x[NewRetryableRangefeedErrors-16] - _ = x[AlterSystemWebSessionsCreateIndexes-17] - _ = x[SeparatedIntentsMigration-18] - _ = x[PostSeparatedIntentsMigration-19] - _ = x[RetryJobsWithExponentialBackoff-20] - _ = x[RecordsBasedRegistry-21] - _ = x[AutoSpanConfigReconciliationJob-22] - _ = x[PreventNewInterleavedTables-23] - _ = x[EnsureNoInterleavedTables-24] - _ = x[DefaultPrivileges-25] - _ = x[ZonesTableForSecondaryTenants-26] - _ = x[UseKeyEncodeForHashShardedIndexes-27] - _ = x[DatabasePlacementPolicy-28] - _ = x[GeneratedAsIdentity-29] - _ = x[OnUpdateExpressions-30] - _ = x[SpanConfigurationsTable-31] - _ = x[BoundedStaleness-32] - _ = x[DateAndIntervalStyle-33] - _ = x[PebbleFormatVersioned-34] - _ = x[MarkerDataKeysRegistry-35] - _ = x[PebbleSetWithDelete-36] - _ = x[TenantUsageSingleConsumptionColumn-37] - _ = x[SQLStatsTables-38] - _ = x[SQLStatsCompactionScheduledJob-39] - _ = x[V21_2-40] - _ = x[Start22_1-41] - _ = x[TargetBytesAvoidExcess-42] + _ = x[V21_1-0] + _ = x[Start21_1PLUS-1] + _ = x[Start21_2-2] + _ = x[JoinTokensTable-3] + _ = x[AcquisitionTypeInLeaseHistory-4] + _ = x[SerializeViewUDTs-5] + _ = x[ExpressionIndexes-6] + _ = x[DeleteDeprecatedNamespaceTableDescriptorMigration-7] + _ = x[FixDescriptors-8] + _ = x[DatabaseRoleSettings-9] + _ = x[TenantUsageTable-10] + _ = x[SQLInstancesTable-11] + _ = x[NewRetryableRangefeedErrors-12] + _ = x[AlterSystemWebSessionsCreateIndexes-13] + _ = x[SeparatedIntentsMigration-14] + _ = x[PostSeparatedIntentsMigration-15] + _ = x[RetryJobsWithExponentialBackoff-16] + _ = x[RecordsBasedRegistry-17] + _ = x[AutoSpanConfigReconciliationJob-18] + _ = x[PreventNewInterleavedTables-19] + _ = x[EnsureNoInterleavedTables-20] + _ = x[DefaultPrivileges-21] + _ = x[ZonesTableForSecondaryTenants-22] + _ = x[UseKeyEncodeForHashShardedIndexes-23] + _ = x[DatabasePlacementPolicy-24] + _ = x[GeneratedAsIdentity-25] + _ = x[OnUpdateExpressions-26] + _ = x[SpanConfigurationsTable-27] + _ = x[BoundedStaleness-28] + _ = x[DateAndIntervalStyle-29] + _ = x[PebbleFormatVersioned-30] + _ = x[MarkerDataKeysRegistry-31] + _ = x[PebbleSetWithDelete-32] + _ = x[TenantUsageSingleConsumptionColumn-33] + _ = x[SQLStatsTables-34] + _ = x[SQLStatsCompactionScheduledJob-35] + _ = x[V21_2-36] + _ = x[Start22_1-37] + _ = x[TargetBytesAvoidExcess-38] } -const _Key_name = "replacedTruncatedAndRangeAppliedStateMigrationreplacedPostTruncatedAndRangeAppliedStateMigrationTruncatedAndRangeAppliedStateMigrationPostTruncatedAndRangeAppliedStateMigrationV21_1Start21_1PLUSStart21_2JoinTokensTableAcquisitionTypeInLeaseHistorySerializeViewUDTsExpressionIndexesDeleteDeprecatedNamespaceTableDescriptorMigrationFixDescriptorsDatabaseRoleSettingsTenantUsageTableSQLInstancesTableNewRetryableRangefeedErrorsAlterSystemWebSessionsCreateIndexesSeparatedIntentsMigrationPostSeparatedIntentsMigrationRetryJobsWithExponentialBackoffRecordsBasedRegistryAutoSpanConfigReconciliationJobPreventNewInterleavedTablesEnsureNoInterleavedTablesDefaultPrivilegesZonesTableForSecondaryTenantsUseKeyEncodeForHashShardedIndexesDatabasePlacementPolicyGeneratedAsIdentityOnUpdateExpressionsSpanConfigurationsTableBoundedStalenessDateAndIntervalStylePebbleFormatVersionedMarkerDataKeysRegistryPebbleSetWithDeleteTenantUsageSingleConsumptionColumnSQLStatsTablesSQLStatsCompactionScheduledJobV21_2Start22_1TargetBytesAvoidExcess" +const _Key_name = "V21_1Start21_1PLUSStart21_2JoinTokensTableAcquisitionTypeInLeaseHistorySerializeViewUDTsExpressionIndexesDeleteDeprecatedNamespaceTableDescriptorMigrationFixDescriptorsDatabaseRoleSettingsTenantUsageTableSQLInstancesTableNewRetryableRangefeedErrorsAlterSystemWebSessionsCreateIndexesSeparatedIntentsMigrationPostSeparatedIntentsMigrationRetryJobsWithExponentialBackoffRecordsBasedRegistryAutoSpanConfigReconciliationJobPreventNewInterleavedTablesEnsureNoInterleavedTablesDefaultPrivilegesZonesTableForSecondaryTenantsUseKeyEncodeForHashShardedIndexesDatabasePlacementPolicyGeneratedAsIdentityOnUpdateExpressionsSpanConfigurationsTableBoundedStalenessDateAndIntervalStylePebbleFormatVersionedMarkerDataKeysRegistryPebbleSetWithDeleteTenantUsageSingleConsumptionColumnSQLStatsTablesSQLStatsCompactionScheduledJobV21_2Start22_1TargetBytesAvoidExcess" -var _Key_index = [...]uint16{0, 46, 96, 134, 176, 181, 194, 203, 218, 247, 264, 281, 330, 344, 364, 380, 397, 424, 459, 484, 513, 544, 564, 595, 622, 647, 664, 693, 726, 749, 768, 787, 810, 826, 846, 867, 889, 908, 942, 956, 986, 991, 1000, 1022} +var _Key_index = [...]uint16{0, 5, 18, 27, 42, 71, 88, 105, 154, 168, 188, 204, 221, 248, 283, 308, 337, 368, 388, 419, 446, 471, 488, 517, 550, 573, 592, 611, 634, 650, 670, 691, 713, 732, 766, 780, 810, 815, 824, 846} func (i Key) String() string { if i < 0 || i >= Key(len(_Key_index)-1) { diff --git a/pkg/keys/constants.go b/pkg/keys/constants.go index a5f11e6ef2d6..eaeda96e7a98 100644 --- a/pkg/keys/constants.go +++ b/pkg/keys/constants.go @@ -80,17 +80,12 @@ var ( // LocalRangeAppliedStateSuffix is the suffix for the range applied state // key. LocalRangeAppliedStateSuffix = []byte("rask") - // LocalRaftAppliedIndexLegacySuffix is the suffix for the raft applied index. - LocalRaftAppliedIndexLegacySuffix = []byte("rfta") - // LocalRaftTruncatedStateLegacySuffix is the suffix for the legacy - // RaftTruncatedState. See VersionUnreplicatedRaftTruncatedState. + // LocalRaftTruncatedStateSuffix is the suffix for the + // RaftTruncatedState. // Note: This suffix is also used for unreplicated Range-ID keys. - LocalRaftTruncatedStateLegacySuffix = []byte("rftt") + LocalRaftTruncatedStateSuffix = []byte("rftt") // LocalRangeLeaseSuffix is the suffix for a range lease. LocalRangeLeaseSuffix = []byte("rll-") - // LocalLeaseAppliedIndexLegacySuffix is the suffix for the applied lease - // index. - LocalLeaseAppliedIndexLegacySuffix = []byte("rlla") // LocalRangePriorReadSummarySuffix is the suffix for a range's prior read // summary. LocalRangePriorReadSummarySuffix = []byte("rprs") diff --git a/pkg/keys/doc.go b/pkg/keys/doc.go index a4f00c5dabb8..edc93fee371c 100644 --- a/pkg/keys/doc.go +++ b/pkg/keys/doc.go @@ -185,16 +185,12 @@ var _ = [...]interface{}{ // range as a whole. Though they are replicated, they are unaddressable. // Typical examples are MVCC stats and the abort span. They all share // `LocalRangeIDPrefix` and `LocalRangeIDReplicatedInfix`. - AbortSpanKey, // "abc-" - RangeGCThresholdKey, // "lgc-" - RangeAppliedStateKey, // "rask" - RaftAppliedIndexLegacyKey, // "rfta" - RaftTruncatedStateLegacyKey, // "rftt" - RangeLeaseKey, // "rll-" - LeaseAppliedIndexLegacyKey, // "rlla" - RangePriorReadSummaryKey, // "rprs" - RangeVersionKey, // "rver" - RangeStatsLegacyKey, // "stat" + AbortSpanKey, // "abc-" + RangeGCThresholdKey, // "lgc-" + RangeAppliedStateKey, // "rask" + RangeLeaseKey, // "rll-" + RangePriorReadSummaryKey, // "rprs" + RangeVersionKey, // "rver" // 2. Unreplicated range-ID local keys: These contain metadata that // pertain to just one replica of a range. They are unreplicated and diff --git a/pkg/keys/keys.go b/pkg/keys/keys.go index d9ff59035b22..dea29eaa0fdc 100644 --- a/pkg/keys/keys.go +++ b/pkg/keys/keys.go @@ -230,34 +230,10 @@ func DecodeAbortSpanKey(key roachpb.Key, dest []byte) (uuid.UUID, error) { } // RangeAppliedStateKey returns a system-local key for the range applied state key. -// This key has subsumed the responsibility of the following three keys: -// - RaftAppliedIndexLegacyKey -// - LeaseAppliedIndexLegacyKey -// - RangeStatsLegacyKey func RangeAppliedStateKey(rangeID roachpb.RangeID) roachpb.Key { return MakeRangeIDPrefixBuf(rangeID).RangeAppliedStateKey() } -// RaftAppliedIndexLegacyKey returns a system-local key for a raft applied index. -// The key is no longer written to. Its responsibility has been subsumed by the -// RangeAppliedStateKey. -func RaftAppliedIndexLegacyKey(rangeID roachpb.RangeID) roachpb.Key { - return MakeRangeIDPrefixBuf(rangeID).RaftAppliedIndexLegacyKey() -} - -// LeaseAppliedIndexLegacyKey returns a system-local key for a lease applied index. -// The key is no longer written to. Its responsibility has been subsumed by the -// RangeAppliedStateKey. -func LeaseAppliedIndexLegacyKey(rangeID roachpb.RangeID) roachpb.Key { - return MakeRangeIDPrefixBuf(rangeID).LeaseAppliedIndexLegacyKey() -} - -// RaftTruncatedStateLegacyKey returns a system-local key for a RaftTruncatedState. -// See VersionUnreplicatedRaftTruncatedState. -func RaftTruncatedStateLegacyKey(rangeID roachpb.RangeID) roachpb.Key { - return MakeRangeIDPrefixBuf(rangeID).RaftTruncatedStateLegacyKey() -} - // RangeLeaseKey returns a system-local key for a range lease. func RangeLeaseKey(rangeID roachpb.RangeID) roachpb.Key { return MakeRangeIDPrefixBuf(rangeID).RangeLeaseKey() @@ -269,13 +245,6 @@ func RangePriorReadSummaryKey(rangeID roachpb.RangeID) roachpb.Key { return MakeRangeIDPrefixBuf(rangeID).RangePriorReadSummaryKey() } -// RangeStatsLegacyKey returns the key for accessing the MVCCStats struct for -// the specified Range ID. The key is no longer written to. Its responsibility -// has been subsumed by the RangeAppliedStateKey. -func RangeStatsLegacyKey(rangeID roachpb.RangeID) roachpb.Key { - return MakeRangeIDPrefixBuf(rangeID).RangeStatsLegacyKey() -} - // RangeGCThresholdKey returns a system-local key for last used GC threshold on the // user keyspace. Reads and writes <= this timestamp will not be served. func RangeGCThresholdKey(rangeID roachpb.RangeID) roachpb.Key { @@ -943,23 +912,6 @@ func (b RangeIDPrefixBuf) RangeAppliedStateKey() roachpb.Key { return append(b.replicatedPrefix(), LocalRangeAppliedStateSuffix...) } -// RaftAppliedIndexLegacyKey returns a system-local key for a raft applied index. -// See comment on RaftAppliedIndexLegacyKey function. -func (b RangeIDPrefixBuf) RaftAppliedIndexLegacyKey() roachpb.Key { - return append(b.replicatedPrefix(), LocalRaftAppliedIndexLegacySuffix...) -} - -// LeaseAppliedIndexLegacyKey returns a system-local key for a lease applied index. -// See comment on LeaseAppliedIndexLegacyKey function. -func (b RangeIDPrefixBuf) LeaseAppliedIndexLegacyKey() roachpb.Key { - return append(b.replicatedPrefix(), LocalLeaseAppliedIndexLegacySuffix...) -} - -// RaftTruncatedStateLegacyKey returns a system-local key for a RaftTruncatedState. -func (b RangeIDPrefixBuf) RaftTruncatedStateLegacyKey() roachpb.Key { - return append(b.replicatedPrefix(), LocalRaftTruncatedStateLegacySuffix...) -} - // RangeLeaseKey returns a system-local key for a range lease. func (b RangeIDPrefixBuf) RangeLeaseKey() roachpb.Key { return append(b.replicatedPrefix(), LocalRangeLeaseSuffix...) @@ -971,13 +923,6 @@ func (b RangeIDPrefixBuf) RangePriorReadSummaryKey() roachpb.Key { return append(b.replicatedPrefix(), LocalRangePriorReadSummarySuffix...) } -// RangeStatsLegacyKey returns the key for accessing the MVCCStats struct -// for the specified Range ID. -// See comment on RangeStatsLegacyKey function. -func (b RangeIDPrefixBuf) RangeStatsLegacyKey() roachpb.Key { - return append(b.replicatedPrefix(), LocalRangeStatsLegacySuffix...) -} - // RangeGCThresholdKey returns a system-local key for the GC threshold. func (b RangeIDPrefixBuf) RangeGCThresholdKey() roachpb.Key { return append(b.replicatedPrefix(), LocalRangeGCThresholdSuffix...) @@ -995,7 +940,7 @@ func (b RangeIDPrefixBuf) RangeTombstoneKey() roachpb.Key { // RaftTruncatedStateKey returns a system-local key for a RaftTruncatedState. func (b RangeIDPrefixBuf) RaftTruncatedStateKey() roachpb.Key { - return append(b.unreplicatedPrefix(), LocalRaftTruncatedStateLegacySuffix...) + return append(b.unreplicatedPrefix(), LocalRaftTruncatedStateSuffix...) } // RaftHardStateKey returns a system-local key for a Raft HardState. diff --git a/pkg/keys/keys_test.go b/pkg/keys/keys_test.go index f92d803dcc84..c54fc0c55ab8 100644 --- a/pkg/keys/keys_test.go +++ b/pkg/keys/keys_test.go @@ -156,10 +156,7 @@ func TestKeyAddressError(t *testing.T) { "local range ID key .* is not addressable": { AbortSpanKey(0, uuid.MakeV4()), RangeTombstoneKey(0), - RaftAppliedIndexLegacyKey(0), - RaftTruncatedStateLegacyKey(0), RangeLeaseKey(0), - RangeStatsLegacyKey(0), RaftHardStateKey(0), RaftLogPrefix(0), RaftLogKey(0, 0), diff --git a/pkg/keys/printer.go b/pkg/keys/printer.go index e3a67528b355..5f33e1d1491c 100644 --- a/pkg/keys/printer.go +++ b/pkg/keys/printer.go @@ -160,13 +160,11 @@ var ( {name: "RangeTombstone", suffix: LocalRangeTombstoneSuffix}, {name: "RaftHardState", suffix: LocalRaftHardStateSuffix}, {name: "RangeAppliedState", suffix: LocalRangeAppliedStateSuffix}, - {name: "RaftAppliedIndex", suffix: LocalRaftAppliedIndexLegacySuffix}, - {name: "LeaseAppliedIndex", suffix: LocalLeaseAppliedIndexLegacySuffix}, {name: "RaftLog", suffix: LocalRaftLogSuffix, ppFunc: raftLogKeyPrint, psFunc: raftLogKeyParse, }, - {name: "RaftTruncatedState", suffix: LocalRaftTruncatedStateLegacySuffix}, + {name: "RaftTruncatedState", suffix: LocalRaftTruncatedStateSuffix}, {name: "RangeLastReplicaGCTimestamp", suffix: LocalRangeLastReplicaGCTimestampSuffix}, {name: "RangeLease", suffix: LocalRangeLeaseSuffix}, {name: "RangePriorReadSummary", suffix: LocalRangePriorReadSummarySuffix}, diff --git a/pkg/keys/printer_test.go b/pkg/keys/printer_test.go index 579ec2480005..50dbaf7a0287 100644 --- a/pkg/keys/printer_test.go +++ b/pkg/keys/printer_test.go @@ -69,13 +69,9 @@ func TestPrettyPrint(t *testing.T) { {keys.AbortSpanKey(roachpb.RangeID(1000001), txnID), fmt.Sprintf(`/Local/RangeID/1000001/r/AbortSpan/%q`, txnID), revertSupportUnknown}, {keys.RangeAppliedStateKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeAppliedState", revertSupportUnknown}, - {keys.RaftAppliedIndexLegacyKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RaftAppliedIndex", revertSupportUnknown}, - {keys.LeaseAppliedIndexLegacyKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/LeaseAppliedIndex", revertSupportUnknown}, - {keys.RaftTruncatedStateLegacyKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RaftTruncatedState", revertSupportUnknown}, {keys.RaftTruncatedStateKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/u/RaftTruncatedState", revertSupportUnknown}, {keys.RangeLeaseKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeLease", revertSupportUnknown}, {keys.RangePriorReadSummaryKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangePriorReadSummary", revertSupportUnknown}, - {keys.RangeStatsLegacyKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeStats", revertSupportUnknown}, {keys.RangeGCThresholdKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeGCThreshold", revertSupportUnknown}, {keys.RangeVersionKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeVersion", revertSupportUnknown}, diff --git a/pkg/kv/kvserver/batcheval/cmd_end_transaction.go b/pkg/kv/kvserver/batcheval/cmd_end_transaction.go index 39915fedfae1..6fe8f0e2b17f 100644 --- a/pkg/kv/kvserver/batcheval/cmd_end_transaction.go +++ b/pkg/kv/kvserver/batcheval/cmd_end_transaction.go @@ -984,34 +984,6 @@ func splitTriggerHelper( log.VEventf(ctx, 1, "LHS's GCThreshold of split is not set") } - // We're about to write the initial state for the replica. We migrated - // the formerly replicated truncated state into unreplicated keyspace - // in 19.1, but this range may still be using the replicated version - // and we need to make a decision about what to use for the RHS that - // is consistent across the followers: do for the RHS what the LHS - // does: if the LHS has the legacy key, initialize the RHS with a - // legacy key as well. - // - // See VersionUnreplicatedRaftTruncatedState. - truncStateType := stateloader.TruncatedStateUnreplicated - if found, err := storage.MVCCGetProto( - ctx, - batch, - keys.RaftTruncatedStateLegacyKey(rec.GetRangeID()), - hlc.Timestamp{}, - nil, - storage.MVCCGetOptions{}, - ); err != nil { - return enginepb.MVCCStats{}, result.Result{}, errors.Wrap(err, "unable to load legacy truncated state") - } else if found { - truncStateType = stateloader.TruncatedStateLegacyReplicated - } - - replicaVersion, err := sl.LoadVersion(ctx, batch) - if err != nil { - return enginepb.MVCCStats{}, result.Result{}, errors.Wrap(err, "unable to load GCThreshold") - } - // Writing the initial state is subtle since this also seeds the Raft // group. It becomes more subtle due to proposer-evaluated Raft. // @@ -1041,10 +1013,13 @@ func splitTriggerHelper( // HardState via a call to synthesizeRaftState. Here, we only call // writeInitialReplicaState which essentially writes a ReplicaState // only. - + replicaVersion, err := sl.LoadVersion(ctx, batch) + if err != nil { + return enginepb.MVCCStats{}, result.Result{}, errors.Wrap(err, "unable to load replica version") + } *h.AbsPostSplitRight(), err = stateloader.WriteInitialReplicaState( ctx, batch, *h.AbsPostSplitRight(), split.RightDesc, rightLease, - *gcThreshold, truncStateType, replicaVersion, + *gcThreshold, replicaVersion, ) if err != nil { return enginepb.MVCCStats{}, result.Result{}, errors.Wrap(err, "unable to write initial Replica state") diff --git a/pkg/kv/kvserver/batcheval/cmd_migrate.go b/pkg/kv/kvserver/batcheval/cmd_migrate.go index 288d8330c911..9cfe0294475f 100644 --- a/pkg/kv/kvserver/batcheval/cmd_migrate.go +++ b/pkg/kv/kvserver/batcheval/cmd_migrate.go @@ -20,7 +20,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/storage" - "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/errors" ) @@ -42,8 +41,6 @@ func declareKeysMigrate( latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: keys.RangeVersionKey(rs.GetRangeID())}) latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: keys.RangeDescriptorKey(rs.GetStartKey())}) - latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: keys.RaftTruncatedStateLegacyKey(rs.GetRangeID())}) - lockSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: keys.RaftTruncatedStateLegacyKey(rs.GetRangeID())}) } // migrationRegistry is a global registry of all KV-level migrations. See @@ -54,7 +51,6 @@ var migrationRegistry = make(map[roachpb.Version]migration) type migration func(context.Context, storage.ReadWriter, CommandArgs) (result.Result, error) func init() { - registerMigration(clusterversion.TruncatedAndRangeAppliedStateMigration, truncatedAndAppliedStateMigration) registerMigration(clusterversion.PostSeparatedIntentsMigration, postSeparatedIntentsMigration) } @@ -96,41 +92,6 @@ func Migrate( return pd, nil } -// truncatedAndRangeAppliedStateMigration lets us stop using the legacy -// replicated truncated state and start using the new RangeAppliedState for this -// specific range. -func truncatedAndAppliedStateMigration( - ctx context.Context, readWriter storage.ReadWriter, cArgs CommandArgs, -) (result.Result, error) { - var legacyTruncatedState roachpb.RaftTruncatedState - legacyKeyFound, err := storage.MVCCGetProto( - ctx, readWriter, keys.RaftTruncatedStateLegacyKey(cArgs.EvalCtx.GetRangeID()), - hlc.Timestamp{}, &legacyTruncatedState, storage.MVCCGetOptions{}, - ) - if err != nil { - return result.Result{}, err - } - - var pd result.Result - if legacyKeyFound { - // Time to migrate by deleting the legacy key. The downstream-of-Raft - // code will atomically rewrite the truncated state (supplied via the - // side effect) into the new unreplicated key. - if err := storage.MVCCDelete( - ctx, readWriter, cArgs.Stats, keys.RaftTruncatedStateLegacyKey(cArgs.EvalCtx.GetRangeID()), - hlc.Timestamp{}, nil, /* txn */ - ); err != nil { - return result.Result{}, err - } - pd.Replicated.State = &kvserverpb.ReplicaState{ - // We need to pass in a truncated state to enable the migration. - // Passing the same one is the easiest thing to do. - TruncatedState: &legacyTruncatedState, - } - } - return pd, nil -} - // postSeparatedIntentsMigration is the below-raft part of the migration for // interleaved to separated intents. It is a no-op as the only purpose of // running the Migrate command here is to clear out any orphaned replicas with diff --git a/pkg/kv/kvserver/batcheval/cmd_truncate_log.go b/pkg/kv/kvserver/batcheval/cmd_truncate_log.go index 19bd9c3138f2..f34dc1f9fc6f 100644 --- a/pkg/kv/kvserver/batcheval/cmd_truncate_log.go +++ b/pkg/kv/kvserver/batcheval/cmd_truncate_log.go @@ -19,7 +19,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/storage" - "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" ) @@ -31,7 +30,6 @@ func init() { func declareKeysTruncateLog( rs ImmutableRangeState, _ roachpb.Header, req roachpb.Request, latchSpans, _ *spanset.SpanSet, ) { - latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: keys.RaftTruncatedStateLegacyKey(rs.GetRangeID())}) prefix := keys.RaftLogPrefix(rs.GetRangeID()) latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: prefix, EndKey: prefix.PrefixEnd()}) } @@ -54,15 +52,6 @@ func TruncateLog( return result.Result{}, nil } - var legacyTruncatedState roachpb.RaftTruncatedState - legacyKeyFound, err := storage.MVCCGetProto( - ctx, readWriter, keys.RaftTruncatedStateLegacyKey(cArgs.EvalCtx.GetRangeID()), - hlc.Timestamp{}, &legacyTruncatedState, storage.MVCCGetOptions{}, - ) - if err != nil { - return result.Result{}, err - } - firstIndex, err := cArgs.EvalCtx.GetFirstIndex() if err != nil { return result.Result{}, errors.Wrap(err, "getting first index") @@ -130,18 +119,5 @@ func TruncateLog( } pd.Replicated.RaftLogDelta = ms.SysBytes - - if legacyKeyFound { - // Time to migrate by deleting the legacy key. The downstream-of-Raft - // code will atomically rewrite the truncated state (supplied via the - // side effect) into the new unreplicated key. - if err := storage.MVCCDelete( - ctx, readWriter, cArgs.Stats, keys.RaftTruncatedStateLegacyKey(cArgs.EvalCtx.GetRangeID()), - hlc.Timestamp{}, nil, /* txn */ - ); err != nil { - return result.Result{}, err - } - } - return pd, nil } diff --git a/pkg/kv/kvserver/batcheval/cmd_truncate_log_test.go b/pkg/kv/kvserver/batcheval/cmd_truncate_log_test.go index 3475f7bebfd5..b914a92457e6 100644 --- a/pkg/kv/kvserver/batcheval/cmd_truncate_log_test.go +++ b/pkg/kv/kvserver/batcheval/cmd_truncate_log_test.go @@ -12,7 +12,6 @@ package batcheval import ( "context" - "fmt" "testing" "github.com/cockroachdb/cockroach/pkg/keys" @@ -22,19 +21,13 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func putTruncatedState( - t *testing.T, - eng storage.Engine, - rangeID roachpb.RangeID, - truncState roachpb.RaftTruncatedState, - legacy bool, + t *testing.T, eng storage.Engine, rangeID roachpb.RangeID, truncState roachpb.RaftTruncatedState, ) { key := keys.RaftTruncatedStateKey(rangeID) - if legacy { - key = keys.RaftTruncatedStateLegacyKey(rangeID) - } if err := storage.MVCCPutProto( context.Background(), eng, nil, key, hlc.Timestamp{}, nil /* txn */, &truncState, @@ -45,75 +38,24 @@ func putTruncatedState( func readTruncStates( t *testing.T, eng storage.Engine, rangeID roachpb.RangeID, -) (legacy roachpb.RaftTruncatedState, unreplicated roachpb.RaftTruncatedState) { +) (truncatedState roachpb.RaftTruncatedState) { t.Helper() - legacyFound, err := storage.MVCCGetProto( - context.Background(), eng, keys.RaftTruncatedStateLegacyKey(rangeID), - hlc.Timestamp{}, &legacy, storage.MVCCGetOptions{}, - ) - if err != nil { - t.Fatal(err) - } - if legacyFound != (legacy != roachpb.RaftTruncatedState{}) { - t.Fatalf("legacy key found=%t but state is %+v", legacyFound, legacy) - } - - unreplicatedFound, err := storage.MVCCGetProto( + found, err := storage.MVCCGetProto( context.Background(), eng, keys.RaftTruncatedStateKey(rangeID), - hlc.Timestamp{}, &unreplicated, storage.MVCCGetOptions{}, + hlc.Timestamp{}, &truncatedState, storage.MVCCGetOptions{}, ) if err != nil { t.Fatal(err) } - if unreplicatedFound != (unreplicated != roachpb.RaftTruncatedState{}) { - t.Fatalf("unreplicated key found=%t but state is %+v", unreplicatedFound, unreplicated) - } + require.True(t, found) return } -const ( - expectationNeither = iota - expectationLegacy - expectationUnreplicated -) - -type unreplicatedTruncStateTest struct { - startsWithLegacy bool - exp int // see consts above -} - -func TestTruncateLogUnreplicatedTruncatedState(t *testing.T) { +func TestTruncateLog(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) - // Check out the old clusterversion.VersionUnreplicatedRaftTruncatedState - // for information on what's being tested. The cluster version is gone, but - // the migration is done range by range and so it still exists. - - const ( - startsLegacy = true - startsUnreplicated = false - ) - - testCases := []unreplicatedTruncStateTest{ - // This is the case where we've already migrated. - {startsUnreplicated, expectationUnreplicated}, - // This is the case in which the migration is triggered. As a result, - // we see neither of the keys written. The new key will be written - // atomically as a side effect (outside of the scope of this test). - {startsLegacy, expectationNeither}, - } - - for _, tc := range testCases { - t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) { - runUnreplicatedTruncatedState(t, tc) - }) - } -} - -func runUnreplicatedTruncatedState(t *testing.T, tc unreplicatedTruncStateTest) { ctx := context.Background() - const ( rangeID = 12 term = 10 @@ -134,8 +76,7 @@ func runUnreplicatedTruncatedState(t *testing.T, tc unreplicatedTruncStateTest) Term: term, } - // Put down the TruncatedState specified by the test case. - putTruncatedState(t, eng, rangeID, truncState, tc.startsWithLegacy) + putTruncatedState(t, eng, rangeID, truncState) // Send a truncation request. req := roachpb.TruncateLogRequest{ @@ -157,25 +98,13 @@ func runUnreplicatedTruncatedState(t *testing.T, tc unreplicatedTruncStateTest) Term: term, } - legacy, unreplicated := readTruncStates(t, eng, rangeID) - - switch tc.exp { - case expectationLegacy: - assert.Equal(t, expTruncState, legacy) - assert.Zero(t, unreplicated) - case expectationUnreplicated: - // The unreplicated key that we see should be the initial truncated - // state (it's only updated below Raft). - assert.Equal(t, truncState, unreplicated) - assert.Zero(t, legacy) - case expectationNeither: - assert.Zero(t, unreplicated) - assert.Zero(t, legacy) - default: - t.Fatalf("unknown expectation %d", tc.exp) - } + // The unreplicated key that we see should be the initial truncated + // state (it's only updated below Raft). + gotTruncatedState := readTruncStates(t, eng, rangeID) + assert.Equal(t, truncState, gotTruncatedState) assert.NotNil(t, res.Replicated.State) assert.NotNil(t, res.Replicated.State.TruncatedState) assert.Equal(t, expTruncState, *res.Replicated.State.TruncatedState) + } diff --git a/pkg/kv/kvserver/debug_print.go b/pkg/kv/kvserver/debug_print.go index 9d15017ea4be..4d5fac25d5a6 100644 --- a/pkg/kv/kvserver/debug_print.go +++ b/pkg/kv/kvserver/debug_print.go @@ -13,7 +13,6 @@ package kvserver import ( "bytes" "fmt" - "strconv" "strings" "github.com/cockroachdb/cockroach/pkg/keys" @@ -293,15 +292,6 @@ func tryRangeIDKey(kv storage.MVCCKeyValue) (string, error) { // switch. Other types are handled inside the switch and return. var msg protoutil.Message switch { - case bytes.Equal(suffix, keys.LocalLeaseAppliedIndexLegacySuffix): - fallthrough - case bytes.Equal(suffix, keys.LocalRaftAppliedIndexLegacySuffix): - i, err := value.GetInt() - if err != nil { - return "", err - } - return strconv.FormatInt(i, 10), nil - case bytes.Equal(suffix, keys.LocalAbortSpanSuffix): msg = &roachpb.AbortSpanEntry{} @@ -314,7 +304,7 @@ func tryRangeIDKey(kv storage.MVCCKeyValue) (string, error) { case bytes.Equal(suffix, keys.LocalRangeTombstoneSuffix): msg = &roachpb.RangeTombstone{} - case bytes.Equal(suffix, keys.LocalRaftTruncatedStateLegacySuffix): + case bytes.Equal(suffix, keys.LocalRaftTruncatedStateSuffix): msg = &roachpb.RaftTruncatedState{} case bytes.Equal(suffix, keys.LocalRangeLeaseSuffix): diff --git a/pkg/kv/kvserver/kvserverpb/state.pb.go b/pkg/kv/kvserver/kvserverpb/state.pb.go index ce7660630fac..77b5e7a4a728 100644 --- a/pkg/kv/kvserver/kvserverpb/state.pb.go +++ b/pkg/kv/kvserver/kvserverpb/state.pb.go @@ -64,15 +64,6 @@ type ReplicaState struct { // not be served. GCThreshold *hlc.Timestamp `protobuf:"bytes,6,opt,name=gc_threshold,json=gcThreshold,proto3" json:"gc_threshold,omitempty"` Stats *enginepb.MVCCStats `protobuf:"bytes,7,opt,name=stats,proto3" json:"stats,omitempty"` - // using_applied_state_key specifies whether the Range has been upgraded - // to begin using the RangeAppliedState key. This key holds a combination - // of the Raft applied index, the lease applied index, and the MVCC stats. - // - // When set to true in a ReplicatedEvalResult, the flag indicates that the - // range should begin using the RangeAppliedState key. Handling of this flag - // is idempotent by Replica state machines, meaning that it is ok for multiple - // Raft commands to set it to true. - UsingAppliedStateKey bool `protobuf:"varint,11,opt,name=using_applied_state_key,json=usingAppliedStateKey,proto3" json:"using_applied_state_key,omitempty"` // Version tells us which migrations can be assumed to have run against this // particular replica. When we introduce backwards incompatible changes to the // replica state (for example using the unreplicated truncated state instead @@ -260,76 +251,74 @@ func init() { } var fileDescriptor_cc107fbd3ff296cb = []byte{ - // 1091 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0xdb, 0x36, - 0x14, 0x8e, 0x66, 0xa5, 0x75, 0x68, 0x27, 0x71, 0xd9, 0x26, 0x51, 0xd3, 0xd5, 0x36, 0x3c, 0x6c, - 0xf0, 0x80, 0x4e, 0xc2, 0xba, 0x9f, 0x62, 0x3f, 0xc0, 0x10, 0x27, 0xc0, 0xe0, 0x2c, 0x19, 0x12, - 0xc6, 0xe8, 0x80, 0xed, 0x42, 0xa0, 0x25, 0x46, 0x26, 0x2c, 0x8b, 0x2a, 0x49, 0x1b, 0x49, 0x9f, - 0x62, 0xd8, 0x13, 0xec, 0x62, 0x0f, 0xb0, 0xc7, 0xc8, 0x65, 0x2e, 0x7b, 0x65, 0x6c, 0xce, 0xcd, - 0x6e, 0xf6, 0x02, 0xbb, 0x1a, 0x48, 0x4a, 0x8e, 0xed, 0x04, 0x68, 0x06, 0xec, 0x4e, 0x3e, 0xe7, - 0xfb, 0x78, 0xce, 0xf9, 0xf8, 0x1d, 0x59, 0xa0, 0xd1, 0x1f, 0x79, 0xfd, 0x91, 0x20, 0x7c, 0x44, - 0xf8, 0xf4, 0x21, 0xed, 0x7a, 0x42, 0x62, 0x49, 0xdc, 0x94, 0x33, 0xc9, 0x60, 0x2d, 0x60, 0x41, - 0x9f, 0x33, 0x1c, 0xf4, 0xdc, 0xfe, 0xc8, 0xcd, 0x41, 0xae, 0x90, 0x8c, 0xe3, 0x88, 0xa4, 0xdd, - 0xed, 0x27, 0xd9, 0xa3, 0x47, 0x92, 0x88, 0x26, 0x24, 0xed, 0x7a, 0x83, 0x51, 0x10, 0x18, 0xf6, - 0xf6, 0x13, 0xcd, 0x4c, 0xbb, 0x1e, 0x4d, 0x24, 0xe1, 0x09, 0x8e, 0x7d, 0x8e, 0x4f, 0x65, 0x96, - 0xdc, 0xcc, 0x93, 0x03, 0x22, 0x71, 0x88, 0x25, 0xce, 0xe2, 0x30, 0x8f, 0xcf, 0xc4, 0x9c, 0xa1, - 0xa4, 0xb1, 0xd7, 0x8b, 0x03, 0x4f, 0xd2, 0x01, 0x11, 0x12, 0x0f, 0xd2, 0x2c, 0xf3, 0x28, 0x62, - 0x11, 0xd3, 0x8f, 0x9e, 0x7a, 0x32, 0xd1, 0xc6, 0x2f, 0xcb, 0xa0, 0x8c, 0x48, 0x1a, 0xd3, 0x00, - 0x9f, 0xa8, 0x69, 0xe0, 0x33, 0x00, 0x55, 0x69, 0x1f, 0xa7, 0x69, 0x4c, 0x49, 0xe8, 0xd3, 0x24, - 0x24, 0x67, 0x8e, 0x55, 0xb7, 0x9a, 0x36, 0xaa, 0xa8, 0xcc, 0x8e, 0x49, 0xb4, 0x55, 0x1c, 0xba, - 0xe0, 0x61, 0x4c, 0xb0, 0x20, 0x0b, 0xf0, 0x77, 0x34, 0xfc, 0x81, 0x4e, 0xcd, 0xe1, 0x3f, 0x07, - 0x76, 0x48, 0x44, 0xe0, 0x14, 0xea, 0x56, 0xb3, 0xf4, 0xbc, 0xe1, 0x5e, 0x8b, 0x96, 0xcd, 0xe2, - 0x22, 0x9c, 0x44, 0x64, 0x8f, 0x88, 0x80, 0xd3, 0x54, 0x32, 0x8e, 0x34, 0x1e, 0xba, 0x60, 0x59, - 0x1f, 0xe6, 0xd8, 0x9a, 0xe8, 0xdc, 0x42, 0x3c, 0x50, 0x79, 0x64, 0x60, 0xf0, 0x7b, 0xb0, 0x2e, - 0xf9, 0x30, 0x09, 0xb0, 0x24, 0xa1, 0xaf, 0xaf, 0xc9, 0x59, 0xd6, 0xcc, 0xf7, 0x6f, 0x2d, 0x79, - 0x2a, 0x3b, 0x39, 0x5a, 0xab, 0x80, 0xd6, 0xe4, 0xdc, 0x6f, 0x78, 0x0c, 0xca, 0x51, 0xe0, 0xcb, - 0x1e, 0x27, 0xa2, 0xc7, 0xe2, 0xd0, 0xb9, 0xa7, 0x0f, 0x7b, 0x3a, 0x73, 0x98, 0xd2, 0xdd, 0xed, - 0xc5, 0x81, 0xdb, 0xc9, 0x75, 0x6f, 0xad, 0x4f, 0xc6, 0xb5, 0xd2, 0xb7, 0xbb, 0x9d, 0x9c, 0x85, - 0x4a, 0x51, 0x30, 0xfd, 0x01, 0xbf, 0x02, 0xcb, 0xaa, 0x31, 0xe1, 0xdc, 0xbf, 0xd1, 0x58, 0xe6, - 0x14, 0x37, 0x77, 0x8a, 0x7b, 0xf8, 0x72, 0x77, 0x57, 0x35, 0x22, 0x90, 0xe1, 0xc0, 0xcf, 0xc0, - 0xd6, 0x50, 0xd0, 0x24, 0x9a, 0xea, 0xae, 0x67, 0xf4, 0xfb, 0xe4, 0xdc, 0x29, 0xd5, 0xad, 0x66, - 0x11, 0x3d, 0xd2, 0xe9, 0x4c, 0x7b, 0x3d, 0xc3, 0x77, 0xe4, 0x1c, 0x7e, 0x0a, 0xee, 0x8f, 0x08, - 0x17, 0x94, 0x25, 0x4e, 0x59, 0x57, 0xdd, 0xbe, 0x45, 0x8e, 0x97, 0x06, 0x81, 0x72, 0x28, 0xfc, - 0x01, 0x6c, 0x68, 0x4b, 0x04, 0x31, 0x13, 0x24, 0xf4, 0xa7, 0xc6, 0x72, 0x56, 0xef, 0xa2, 0x82, - 0x7d, 0x31, 0xae, 0x2d, 0xa1, 0x87, 0xea, 0x84, 0x5d, 0x7d, 0xc0, 0x34, 0xf5, 0xa5, 0xfd, 0xd7, - 0xaf, 0x35, 0x6b, 0xdf, 0x2e, 0x16, 0x2b, 0x2b, 0xfb, 0x76, 0x71, 0xa5, 0x02, 0xf6, 0xed, 0x22, - 0xa8, 0x94, 0x1a, 0x7f, 0xdf, 0x07, 0x2b, 0xda, 0x07, 0xed, 0xe4, 0x94, 0xc1, 0x43, 0x23, 0x14, - 0xd1, 0x26, 0x2c, 0x3d, 0xff, 0xc8, 0x7d, 0xcb, 0xa6, 0xb9, 0xb3, 0x7e, 0x6e, 0x15, 0x55, 0xf9, - 0xcb, 0x71, 0xcd, 0x32, 0xd2, 0x11, 0xf8, 0x14, 0x80, 0x18, 0x0b, 0x39, 0xe7, 0xd4, 0x15, 0x15, - 0x31, 0x0e, 0xad, 0x81, 0x52, 0x32, 0x1c, 0xf8, 0x29, 0x49, 0x42, 0x9a, 0x44, 0xda, 0xa8, 0x36, - 0x02, 0xc9, 0x70, 0x70, 0x64, 0x22, 0x39, 0x20, 0xe4, 0x2c, 0x4d, 0x49, 0xa8, 0x6d, 0x65, 0x00, - 0x7b, 0x26, 0x02, 0x1b, 0x60, 0x55, 0xcb, 0x15, 0xb3, 0xc8, 0x17, 0xf4, 0x35, 0xd1, 0x66, 0x29, - 0xa0, 0x92, 0x0a, 0x1e, 0xb0, 0xe8, 0x84, 0xbe, 0x26, 0xf0, 0xe3, 0x4c, 0xd2, 0x1c, 0xe3, 0x4b, - 0x3e, 0x14, 0x92, 0x84, 0x0e, 0xd0, 0xb7, 0x07, 0x67, 0xb0, 0x1d, 0x93, 0x81, 0x5f, 0x83, 0x6d, - 0x9c, 0xa6, 0x9c, 0x9d, 0xd1, 0x81, 0xba, 0xea, 0x94, 0xb3, 0x94, 0x09, 0x1c, 0xfb, 0xaf, 0x86, - 0x4c, 0x62, 0x6d, 0xa2, 0x02, 0x72, 0x66, 0x10, 0x47, 0x19, 0xe0, 0x58, 0xe5, 0xe1, 0x17, 0xe0, - 0xf1, 0x3c, 0xc3, 0xef, 0xaa, 0xb5, 0x35, 0x22, 0xac, 0x69, 0xf2, 0x66, 0x3a, 0xcb, 0x68, 0x61, - 0x41, 0x8c, 0x22, 0xdf, 0x80, 0x77, 0x17, 0xa8, 0x9c, 0x98, 0xa5, 0x7f, 0x35, 0x24, 0x43, 0xe2, - 0xac, 0xd7, 0x0b, 0xcd, 0x02, 0x7a, 0x3c, 0xc7, 0x46, 0x06, 0x71, 0xac, 0x00, 0xf0, 0x03, 0xb0, - 0xce, 0xd5, 0x6d, 0xfa, 0x03, 0x7c, 0xe6, 0x77, 0xcf, 0x25, 0x11, 0x4e, 0x51, 0x57, 0x5c, 0xd5, - 0xe1, 0x43, 0x7c, 0xd6, 0x52, 0x41, 0xf8, 0x13, 0xd8, 0xc2, 0x81, 0xa4, 0x23, 0x72, 0xd3, 0x69, - 0xe5, 0xbb, 0x3b, 0x6d, 0xc3, 0x9c, 0xb1, 0xe0, 0x35, 0xf8, 0x02, 0x6c, 0xe9, 0x6a, 0xa7, 0x84, - 0x84, 0x3e, 0x27, 0x11, 0x15, 0x92, 0x63, 0x49, 0x59, 0x22, 0xb4, 0x8d, 0x0b, 0x68, 0x73, 0x9a, - 0x46, 0xb3, 0x59, 0xf8, 0x21, 0x58, 0x91, 0x24, 0xc1, 0x89, 0xf4, 0x69, 0xe8, 0x54, 0xd4, 0x6d, - 0xb7, 0xca, 0x93, 0x71, 0xad, 0xd8, 0xd1, 0xc1, 0xf6, 0x1e, 0x2a, 0x9a, 0x74, 0x3b, 0x84, 0x04, - 0x6c, 0x2d, 0x76, 0xee, 0xa7, 0x2c, 0xa6, 0xc1, 0xb9, 0x03, 0xeb, 0x56, 0x73, 0x6d, 0xce, 0xbb, - 0x73, 0x2f, 0xbc, 0x85, 0x6e, 0x8f, 0x34, 0x09, 0x6d, 0x04, 0xb7, 0x85, 0xe1, 0xef, 0x16, 0x78, - 0xef, 0x46, 0x1d, 0x41, 0x43, 0x22, 0x39, 0x4e, 0x44, 0xca, 0xb8, 0x32, 0xf7, 0x29, 0x73, 0x1e, - 0x6a, 0xd1, 0x5e, 0xbc, 0x7d, 0x5f, 0x54, 0x07, 0x27, 0x34, 0x24, 0x9d, 0x9c, 0xaf, 0xf6, 0xae, - 0xd5, 0x54, 0x72, 0x4e, 0xc6, 0xb5, 0xfa, 0x42, 0x73, 0x37, 0x90, 0xa8, 0x1e, 0xdc, 0x44, 0xc8, - 0x59, 0xc4, 0x74, 0xd3, 0xed, 0xca, 0xf2, 0x74, 0xd3, 0x4b, 0x95, 0xf2, 0xbe, 0x5d, 0x7c, 0x50, - 0x81, 0x8d, 0xdf, 0x0a, 0x60, 0xf3, 0xf6, 0x26, 0xe0, 0x3e, 0x58, 0xe3, 0x66, 0x9d, 0x33, 0x53, - 0x64, 0x6f, 0x81, 0x3b, 0x59, 0x61, 0x35, 0xa3, 0x9a, 0x01, 0xe0, 0x10, 0x94, 0xf2, 0xb3, 0x62, - 0x4c, 0xf5, 0xea, 0x17, 0x5a, 0x9d, 0xc9, 0xb8, 0x06, 0xb2, 0x37, 0xc6, 0xc1, 0x4e, 0xfb, 0x9f, - 0x71, 0xad, 0x15, 0x51, 0xd9, 0x1b, 0x76, 0xdd, 0x80, 0x0d, 0xbc, 0x69, 0x91, 0xb0, 0x7b, 0xfd, - 0xec, 0xa5, 0xfd, 0xc8, 0x9b, 0xfd, 0x24, 0x30, 0x9d, 0x49, 0xe1, 0x05, 0x52, 0xfd, 0x11, 0xed, - 0xb4, 0x11, 0xc8, 0x0a, 0x1d, 0x60, 0xaa, 0x46, 0x08, 0x48, 0x22, 0x39, 0x8e, 0xf3, 0x11, 0x0a, - 0xff, 0x61, 0x84, 0x8c, 0x7a, 0x3d, 0x42, 0x7e, 0x96, 0x1a, 0xc1, 0xbe, 0x1e, 0x61, 0xd7, 0x84, - 0xff, 0xc7, 0x11, 0xb2, 0x42, 0x07, 0x98, 0x9a, 0xeb, 0x6b, 0x3d, 0xbb, 0xf8, 0xb3, 0xba, 0x74, - 0x31, 0xa9, 0x5a, 0x97, 0x93, 0xaa, 0xf5, 0x66, 0x52, 0xb5, 0xfe, 0x98, 0x54, 0xad, 0x9f, 0xaf, - 0xaa, 0x4b, 0x97, 0x57, 0xd5, 0xa5, 0x37, 0x57, 0xd5, 0xa5, 0x1f, 0xc1, 0xf5, 0xc7, 0x51, 0xf7, - 0x9e, 0xfe, 0xc0, 0xf8, 0xe4, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x91, 0x73, 0x5f, 0x8e, 0x3d, - 0x09, 0x00, 0x00, + // 1065 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x6e, 0xdb, 0x36, + 0x18, 0x8f, 0x66, 0xa5, 0x71, 0x68, 0x27, 0x71, 0x99, 0x25, 0x51, 0xd3, 0xd5, 0x36, 0x3c, 0x6c, + 0xf0, 0x80, 0x4e, 0xc2, 0xba, 0x61, 0xc5, 0xfe, 0x00, 0x43, 0x9c, 0x00, 0x83, 0x8d, 0x64, 0x48, + 0x18, 0xa3, 0x03, 0xb6, 0x83, 0x40, 0x4b, 0x8c, 0x4c, 0x44, 0x16, 0x55, 0x92, 0x36, 0xd2, 0x3e, + 0xc5, 0x1e, 0x61, 0x87, 0x3d, 0xc0, 0x1e, 0x23, 0xc7, 0x1c, 0x7b, 0x32, 0x56, 0xe7, 0xb2, 0xcb, + 0x5e, 0x60, 0xa7, 0x81, 0xa4, 0xe4, 0xd8, 0x4e, 0x80, 0x66, 0x40, 0x6f, 0xd4, 0xf7, 0xfd, 0x7e, + 0xdf, 0x3f, 0xfe, 0x3e, 0x49, 0xa0, 0x71, 0x3e, 0xf2, 0xce, 0x47, 0x82, 0xf0, 0x11, 0xe1, 0xd3, + 0x43, 0xda, 0xf3, 0x84, 0xc4, 0x92, 0xb8, 0x29, 0x67, 0x92, 0xc1, 0x5a, 0xc0, 0x82, 0x73, 0xce, + 0x70, 0xd0, 0x77, 0xcf, 0x47, 0x6e, 0x0e, 0x72, 0x85, 0x64, 0x1c, 0x47, 0x24, 0xed, 0xed, 0x3e, + 0xce, 0x8e, 0x1e, 0x49, 0x22, 0x9a, 0x90, 0xb4, 0xe7, 0x0d, 0x46, 0x41, 0x60, 0xd8, 0xbb, 0x8f, + 0x35, 0x33, 0xed, 0x79, 0x34, 0x91, 0x84, 0x27, 0x38, 0xf6, 0x39, 0x3e, 0x93, 0x99, 0x73, 0x3b, + 0x77, 0x0e, 0x88, 0xc4, 0x21, 0x96, 0x38, 0xb3, 0xc3, 0xdc, 0x3e, 0x63, 0x73, 0x86, 0x92, 0xc6, + 0x5e, 0x3f, 0x0e, 0x3c, 0x49, 0x07, 0x44, 0x48, 0x3c, 0x48, 0x33, 0xcf, 0x87, 0x11, 0x8b, 0x98, + 0x3e, 0x7a, 0xea, 0x64, 0xac, 0x8d, 0xb7, 0x36, 0x28, 0x23, 0x92, 0xc6, 0x34, 0xc0, 0xa7, 0xaa, + 0x1b, 0xf8, 0x14, 0x40, 0x95, 0xda, 0xc7, 0x69, 0x1a, 0x53, 0x12, 0xfa, 0x34, 0x09, 0xc9, 0x85, + 0x63, 0xd5, 0xad, 0xa6, 0x8d, 0x2a, 0xca, 0xb3, 0x67, 0x1c, 0x6d, 0x65, 0x87, 0x2e, 0xd8, 0x8c, + 0x09, 0x16, 0x64, 0x01, 0xfe, 0x81, 0x86, 0x3f, 0xd4, 0xae, 0x39, 0xfc, 0xd7, 0xc0, 0x0e, 0x89, + 0x08, 0x9c, 0x42, 0xdd, 0x6a, 0x96, 0x9e, 0x35, 0xdc, 0x9b, 0xa1, 0x65, 0xbd, 0xb8, 0x08, 0x27, + 0x11, 0x39, 0x20, 0x22, 0xe0, 0x34, 0x95, 0x8c, 0x23, 0x8d, 0x87, 0x2e, 0x58, 0xd6, 0xc1, 0x1c, + 0x5b, 0x13, 0x9d, 0x3b, 0x88, 0x87, 0xca, 0x8f, 0x0c, 0x0c, 0xfe, 0x04, 0x36, 0x24, 0x1f, 0x26, + 0x01, 0x96, 0x24, 0xf4, 0xf5, 0x35, 0x39, 0xcb, 0x9a, 0xf9, 0xc9, 0x9d, 0x29, 0xcf, 0x64, 0x37, + 0x47, 0xeb, 0x29, 0xa0, 0x75, 0x39, 0xf7, 0x0c, 0x4f, 0x40, 0x39, 0x0a, 0x7c, 0xd9, 0xe7, 0x44, + 0xf4, 0x59, 0x1c, 0x3a, 0x0f, 0x74, 0xb0, 0x27, 0x33, 0xc1, 0xd4, 0xdc, 0xdd, 0x7e, 0x1c, 0xb8, + 0xdd, 0x7c, 0xee, 0xad, 0x8d, 0xc9, 0xb8, 0x56, 0xfa, 0x71, 0xbf, 0x9b, 0xb3, 0x50, 0x29, 0x0a, + 0xa6, 0x0f, 0xf0, 0x3b, 0xb0, 0xac, 0x0a, 0x13, 0xce, 0xca, 0xad, 0xc2, 0x32, 0xa5, 0xb8, 0xb9, + 0x52, 0xdc, 0xa3, 0x17, 0xfb, 0xfb, 0xaa, 0x10, 0x81, 0x0c, 0x07, 0x7e, 0x05, 0x56, 0x46, 0x84, + 0x0b, 0xca, 0x12, 0xa7, 0xac, 0xe9, 0xbb, 0x77, 0xf4, 0xf5, 0xc2, 0x20, 0x50, 0x0e, 0x85, 0x3f, + 0x83, 0x2d, 0x7d, 0xb7, 0x41, 0xcc, 0x04, 0x09, 0xfd, 0xa9, 0x42, 0x9c, 0xb5, 0xfb, 0xb4, 0x63, + 0x5f, 0x8e, 0x6b, 0x4b, 0x68, 0x53, 0x45, 0xd8, 0xd7, 0x01, 0xa6, 0xae, 0x6f, 0xed, 0xbf, 0x7f, + 0xaf, 0x59, 0x1d, 0xbb, 0x58, 0xac, 0xac, 0x76, 0xec, 0xe2, 0x6a, 0x05, 0x74, 0xec, 0x22, 0xa8, + 0x94, 0x3a, 0x76, 0xb1, 0x54, 0x29, 0x37, 0xfe, 0x59, 0x01, 0xab, 0xfa, 0x5a, 0xdb, 0xc9, 0x19, + 0x83, 0x47, 0xa6, 0x6f, 0xa2, 0x35, 0x55, 0x7a, 0xf6, 0xb9, 0xfb, 0x8e, 0xc5, 0x71, 0x67, 0xe5, + 0xd9, 0x2a, 0xaa, 0x22, 0xae, 0xc6, 0x35, 0xcb, 0x4c, 0x82, 0xc0, 0x27, 0x00, 0xc4, 0x58, 0xc8, + 0x39, 0xe1, 0xad, 0x2a, 0x8b, 0x11, 0x5c, 0x0d, 0x94, 0x92, 0xe1, 0xc0, 0x4f, 0x49, 0x12, 0xd2, + 0x24, 0xd2, 0xba, 0xb3, 0x11, 0x48, 0x86, 0x83, 0x63, 0x63, 0xc9, 0x01, 0x21, 0x67, 0x69, 0x4a, + 0x42, 0xad, 0x12, 0x03, 0x38, 0x30, 0x16, 0xd8, 0x00, 0x6b, 0x7a, 0x68, 0x31, 0x8b, 0x7c, 0x41, + 0x5f, 0x13, 0x7d, 0xf7, 0x05, 0x54, 0x52, 0xc6, 0x43, 0x16, 0x9d, 0xd2, 0xd7, 0x04, 0x7e, 0x91, + 0x0d, 0x36, 0xc7, 0xf8, 0x92, 0x0f, 0x85, 0x24, 0xa1, 0x03, 0xea, 0x56, 0xb3, 0x88, 0xe0, 0x0c, + 0xb6, 0x6b, 0x3c, 0xf0, 0x7b, 0xb0, 0x8b, 0xd3, 0x94, 0xb3, 0x0b, 0x3a, 0xc0, 0x92, 0xf8, 0x29, + 0x67, 0x29, 0x13, 0x38, 0xf6, 0x5f, 0x0e, 0x99, 0xc4, 0x5a, 0x13, 0x05, 0xe4, 0xcc, 0x20, 0x8e, + 0x33, 0xc0, 0x89, 0xf2, 0xc3, 0x6f, 0xc0, 0xa3, 0x79, 0x86, 0xdf, 0x53, 0x5b, 0x68, 0x86, 0xb0, + 0xae, 0xc9, 0xdb, 0xe9, 0x2c, 0xa3, 0x85, 0x05, 0x31, 0x13, 0xf9, 0x01, 0x7c, 0xb4, 0x40, 0xe5, + 0xc4, 0xec, 0xf0, 0xcb, 0x21, 0x19, 0x12, 0x67, 0xa3, 0x5e, 0x68, 0x16, 0xd0, 0xa3, 0x39, 0x36, + 0x32, 0x88, 0x13, 0x05, 0x80, 0x9f, 0x82, 0x0d, 0xae, 0x6e, 0xd3, 0x1f, 0xe0, 0x0b, 0xbf, 0xf7, + 0x4a, 0x12, 0xe1, 0x14, 0x75, 0xc6, 0x35, 0x6d, 0x3e, 0xc2, 0x17, 0x2d, 0x65, 0x84, 0xbf, 0x82, + 0x1d, 0x1c, 0x48, 0x3a, 0x22, 0xb7, 0xf5, 0x56, 0xbe, 0xbf, 0xde, 0xb6, 0x4c, 0x8c, 0x05, 0xc5, + 0xc1, 0xe7, 0x60, 0x47, 0x67, 0x3b, 0x23, 0x24, 0xf4, 0x39, 0x89, 0xa8, 0x90, 0x1c, 0x4b, 0xca, + 0x12, 0xa1, 0xc5, 0x5c, 0x40, 0xdb, 0x53, 0x37, 0x9a, 0xf5, 0xc2, 0xcf, 0xc0, 0xaa, 0x24, 0x09, + 0x4e, 0xa4, 0x4f, 0x43, 0xa7, 0xa2, 0x6e, 0xbb, 0x55, 0x9e, 0x8c, 0x6b, 0xc5, 0xae, 0x36, 0xb6, + 0x0f, 0x50, 0xd1, 0xb8, 0xdb, 0x21, 0x24, 0x60, 0x67, 0xb1, 0x72, 0x3f, 0x65, 0x31, 0x0d, 0x5e, + 0x39, 0xb0, 0x6e, 0x35, 0xd7, 0xe7, 0xb4, 0x3b, 0xf7, 0xfe, 0x5a, 0xa8, 0xf6, 0x58, 0x93, 0xd0, + 0x56, 0x70, 0x97, 0x19, 0xfe, 0x69, 0x81, 0x8f, 0x6f, 0xe5, 0x11, 0x34, 0x24, 0x92, 0xe3, 0x44, + 0xa4, 0x8c, 0x2b, 0x71, 0x9f, 0x31, 0x67, 0x53, 0x0f, 0xed, 0xf9, 0xbb, 0xf7, 0x45, 0x55, 0x70, + 0x4a, 0x43, 0xd2, 0xcd, 0xf9, 0x6a, 0xef, 0x5a, 0x4d, 0x35, 0xce, 0xc9, 0xb8, 0x56, 0x5f, 0x28, + 0xee, 0x16, 0x12, 0xd5, 0x83, 0xdb, 0x08, 0x39, 0x8b, 0x98, 0xee, 0xbb, 0x5d, 0x59, 0x9e, 0xee, + 0x7b, 0xa9, 0x52, 0xee, 0xd8, 0xc5, 0x87, 0x15, 0xd8, 0xf8, 0xa3, 0x00, 0xb6, 0xef, 0x2e, 0x02, + 0x76, 0xc0, 0x3a, 0x37, 0xeb, 0x9c, 0x89, 0x22, 0x7b, 0x0b, 0xdc, 0x4b, 0x0a, 0x6b, 0x19, 0xd5, + 0x34, 0x00, 0x87, 0xa0, 0x94, 0xc7, 0x8a, 0x31, 0xd5, 0xab, 0x5f, 0x68, 0x75, 0x27, 0xe3, 0x1a, + 0xc8, 0xde, 0x18, 0x87, 0x7b, 0xed, 0x7f, 0xc7, 0xb5, 0x56, 0x44, 0x65, 0x7f, 0xd8, 0x73, 0x03, + 0x36, 0xf0, 0xa6, 0x49, 0xc2, 0xde, 0xcd, 0xd9, 0x4b, 0xcf, 0x23, 0x6f, 0xf6, 0x0b, 0x6f, 0x2a, + 0x93, 0xc2, 0x0b, 0xa4, 0xfa, 0xae, 0xec, 0xb5, 0x11, 0xc8, 0x12, 0x1d, 0x62, 0xaa, 0x5a, 0x08, + 0x48, 0x22, 0x39, 0x8e, 0xf3, 0x16, 0x0a, 0xff, 0xa3, 0x85, 0x8c, 0x7a, 0xd3, 0x42, 0x1e, 0x4b, + 0xb5, 0x60, 0xdf, 0xb4, 0xb0, 0x6f, 0xcc, 0xef, 0xb1, 0x85, 0x2c, 0xd1, 0x21, 0xa6, 0xe6, 0xfa, + 0x5a, 0x4f, 0x2f, 0xdf, 0x56, 0x97, 0x2e, 0x27, 0x55, 0xeb, 0x6a, 0x52, 0xb5, 0xde, 0x4c, 0xaa, + 0xd6, 0x5f, 0x93, 0xaa, 0xf5, 0xdb, 0x75, 0x75, 0xe9, 0xea, 0xba, 0xba, 0xf4, 0xe6, 0xba, 0xba, + 0xf4, 0x0b, 0xb8, 0xf9, 0xd7, 0xe9, 0x3d, 0xd0, 0xff, 0x0b, 0x5f, 0xfe, 0x17, 0x00, 0x00, 0xff, + 0xff, 0x26, 0xb9, 0x1b, 0x85, 0x0c, 0x09, 0x00, 0x00, } func (this *ReplicaState) Equal(that interface{}) bool { @@ -372,9 +361,6 @@ func (this *ReplicaState) Equal(that interface{}) bool { if !this.Stats.Equal(that1.Stats) { return false } - if this.UsingAppliedStateKey != that1.UsingAppliedStateKey { - return false - } if !this.Version.Equal(that1.Version) { return false } @@ -529,16 +515,6 @@ func (m *ReplicaState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x62 } - if m.UsingAppliedStateKey { - i-- - if m.UsingAppliedStateKey { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x58 - } if m.Stats != nil { { size, err := m.Stats.MarshalToSizedBuffer(dAtA[:i]) @@ -846,9 +822,6 @@ func (m *ReplicaState) Size() (n int) { l = m.Stats.Size() n += 1 + l + sovState(uint64(l)) } - if m.UsingAppliedStateKey { - n += 2 - } if m.Version != nil { l = m.Version.Size() n += 1 + l + sovState(uint64(l)) @@ -1185,26 +1158,6 @@ func (m *ReplicaState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 11: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field UsingAppliedStateKey", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowState - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.UsingAppliedStateKey = bool(v != 0) case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) diff --git a/pkg/kv/kvserver/kvserverpb/state.proto b/pkg/kv/kvserver/kvserverpb/state.proto index 4caca7500bd1..857873a119ff 100644 --- a/pkg/kv/kvserver/kvserverpb/state.proto +++ b/pkg/kv/kvserver/kvserverpb/state.proto @@ -59,15 +59,6 @@ message ReplicaState { // not be served. util.hlc.Timestamp gc_threshold = 6 [(gogoproto.customname) = "GCThreshold"]; storage.enginepb.MVCCStats stats = 7; - // using_applied_state_key specifies whether the Range has been upgraded - // to begin using the RangeAppliedState key. This key holds a combination - // of the Raft applied index, the lease applied index, and the MVCC stats. - // - // When set to true in a ReplicatedEvalResult, the flag indicates that the - // range should begin using the RangeAppliedState key. Handling of this flag - // is idempotent by Replica state machines, meaning that it is ok for multiple - // Raft commands to set it to true. - bool using_applied_state_key = 11; // Version tells us which migrations can be assumed to have run against this // particular replica. When we introduce backwards incompatible changes to the // replica state (for example using the unreplicated truncated state instead @@ -108,7 +99,7 @@ message ReplicaState { // "follower reads" at or below this timestamp. util.hlc.Timestamp raft_closed_timestamp = 13 [(gogoproto.nullable) = false]; - reserved 8, 9, 10; + reserved 8, 9, 10, 11; } // RangeInfo is used for reporting status information about a range out through diff --git a/pkg/kv/kvserver/raft.pb.go b/pkg/kv/kvserver/raft.pb.go index 052648ba5f52..f57fc144b047 100644 --- a/pkg/kv/kvserver/raft.pb.go +++ b/pkg/kv/kvserver/raft.pb.go @@ -446,15 +446,16 @@ type SnapshotRequest_Header struct { // The type of the snapshot. Type SnapshotRequest_Type `protobuf:"varint,9,opt,name=type,proto3,enum=cockroach.kv.kvserver.SnapshotRequest_Type" json:"type,omitempty"` // Whether the snapshot uses the unreplicated RaftTruncatedState or not. - // This is generally always true at 2.2 and above outside of the migration - // phase, though theoretically it could take a long time for all ranges - // to update to the new mechanism. This bool is true iff the Raft log at - // the snapshot's applied index is using the new key. In particular, it - // is true if the index itself carries out the migration (in which case - // the data in the snapshot contains neither key). + // This is always true for snapshots generated in v21.1+ clusters. In v20.2 + // it was possible for ranges to be using the replicated variant. v21.1 + // therefore had code expecting that possibility (unlike v21.2 code, where + // this field is assumed to always be true and thus never read). For + // compatibility with v21.1 nodes however, v21.2 has to explicitly set this + // field to true. In v22.1 we can remove it entirely seeing as how v21.2 + // code never reads the field. // - // See VersionUnreplicatedRaftTruncatedState. - UnreplicatedTruncatedState bool `protobuf:"varint,8,opt,name=unreplicated_truncated_state,json=unreplicatedTruncatedState,proto3" json:"unreplicated_truncated_state,omitempty"` + // TODO(irfansharif): Remove this in v22.1. + DeprecatedUnreplicatedTruncatedState bool `protobuf:"varint,8,opt,name=deprecated_unreplicated_truncated_state,json=deprecatedUnreplicatedTruncatedState,proto3" json:"deprecated_unreplicated_truncated_state,omitempty"` } func (m *SnapshotRequest_Header) Reset() { *m = SnapshotRequest_Header{} } @@ -576,88 +577,89 @@ func init() { func init() { proto.RegisterFile("kv/kvserver/raft.proto", fileDescriptor_acdcf79fd972c844) } var fileDescriptor_acdcf79fd972c844 = []byte{ - // 1289 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4f, 0x73, 0xdb, 0x44, - 0x14, 0xb7, 0x12, 0xc5, 0x96, 0xd7, 0x71, 0x23, 0x96, 0x10, 0x44, 0xa6, 0xd8, 0x41, 0x53, 0x98, - 0xf0, 0x4f, 0x9e, 0xa6, 0x85, 0x03, 0x17, 0xf0, 0x1f, 0x75, 0xa2, 0x38, 0x4d, 0xd2, 0xb5, 0x13, - 0x06, 0x18, 0xd0, 0xc8, 0xf2, 0xda, 0xd6, 0xd8, 0xd6, 0xaa, 0xd2, 0xda, 0xe0, 0x7e, 0x0a, 0xae, - 0xdc, 0xb8, 0xf1, 0x49, 0x98, 0xc9, 0xb1, 0x27, 0xa6, 0x07, 0xc6, 0x03, 0xee, 0x99, 0x2f, 0xd0, - 0x13, 0xb3, 0xab, 0x95, 0xe3, 0xa6, 0x4d, 0xa9, 0x67, 0xe8, 0x0c, 0x9c, 0xf2, 0xf6, 0xf9, 0xbd, - 0xdf, 0x7b, 0xfb, 0xde, 0xef, 0xbd, 0x55, 0xc0, 0x56, 0x7f, 0x5c, 0xea, 0x8f, 0x23, 0x1c, 0x8e, - 0x71, 0x58, 0x0a, 0x9d, 0x0e, 0x35, 0x82, 0x90, 0x50, 0x02, 0xdf, 0x70, 0x89, 0xdb, 0x0f, 0x89, - 0xe3, 0xf6, 0x8c, 0xfe, 0xd8, 0x48, 0x2c, 0xb6, 0x37, 0xb9, 0x2a, 0x68, 0x95, 0x70, 0x18, 0x92, - 0x30, 0x8a, 0x8d, 0xb7, 0xb7, 0x12, 0xed, 0x10, 0x53, 0xa7, 0xed, 0x50, 0x47, 0xe8, 0x8d, 0x45, - 0xf0, 0x81, 0x37, 0xc6, 0x3e, 0x8e, 0xa2, 0xb9, 0x10, 0xb4, 0xe6, 0xa2, 0xb0, 0xd7, 0x17, 0xed, - 0x13, 0x21, 0x68, 0x95, 0x22, 0xea, 0x50, 0x2c, 0x6c, 0x0a, 0x98, 0xba, 0x6d, 0x9e, 0x69, 0x69, - 0x7c, 0x8b, 0xff, 0x0d, 0x5a, 0x0b, 0x89, 0x6f, 0x6f, 0x76, 0x49, 0x97, 0x70, 0xb1, 0xc4, 0xa4, - 0x58, 0xab, 0xff, 0x25, 0x83, 0x3c, 0x72, 0x3a, 0x74, 0x1f, 0x3b, 0x21, 0x6d, 0x61, 0x87, 0xc2, - 0xef, 0x80, 0x12, 0x3a, 0x7e, 0x17, 0xdb, 0x5e, 0x5b, 0x93, 0x76, 0xa4, 0x5d, 0xb9, 0x52, 0x9d, - 0x4d, 0x8b, 0x19, 0xc4, 0x74, 0x56, 0xed, 0xc9, 0xb4, 0x78, 0xbb, 0xeb, 0xd1, 0xde, 0xa8, 0x65, - 0xb8, 0x64, 0x58, 0x9a, 0x17, 0xa3, 0xdd, 0xba, 0x90, 0x4b, 0x41, 0xbf, 0x5b, 0x12, 0x37, 0x37, - 0x84, 0x1f, 0xca, 0x70, 0x50, 0xab, 0x0d, 0x23, 0xb0, 0xd1, 0x09, 0xc9, 0xd0, 0x0e, 0x71, 0x30, - 0xf0, 0x5c, 0x87, 0x85, 0x59, 0xd9, 0x91, 0x76, 0xf3, 0x95, 0xfa, 0x6c, 0x5a, 0xcc, 0xdf, 0x09, - 0xc9, 0x10, 0xc5, 0xbf, 0xf0, 0x60, 0x9f, 0x2e, 0x17, 0x2c, 0xf1, 0x44, 0xf9, 0xce, 0x02, 0x50, - 0x1b, 0x0e, 0x41, 0x9e, 0x92, 0xc5, 0x90, 0xab, 0x3c, 0xa4, 0x35, 0x9b, 0x16, 0x73, 0x4d, 0xf2, - 0x6f, 0x04, 0xcc, 0x51, 0x72, 0x11, 0x0e, 0x02, 0x99, 0xe2, 0x70, 0xa8, 0xc9, 0xac, 0x7e, 0x88, - 0xcb, 0x70, 0x0b, 0xa4, 0x5d, 0x32, 0x1c, 0x7a, 0x54, 0x5b, 0xe3, 0x5a, 0x71, 0x82, 0x1a, 0xc8, - 0xdc, 0x1f, 0x79, 0x38, 0x72, 0xb1, 0x96, 0xde, 0x91, 0x76, 0x15, 0x94, 0x1c, 0xe1, 0x03, 0x70, - 0x7d, 0xe0, 0x74, 0xbb, 0x9e, 0xdf, 0xb5, 0x3b, 0x64, 0x30, 0x20, 0xdf, 0xe3, 0x30, 0xb2, 0x89, - 0x6f, 0x27, 0xe6, 0xca, 0xce, 0xea, 0x6e, 0x6e, 0xef, 0x96, 0xf1, 0x5c, 0x46, 0x1a, 0x73, 0x0a, - 0x5d, 0xd0, 0xca, 0x38, 0x14, 0x62, 0x45, 0x3e, 0x9f, 0x16, 0x53, 0xe8, 0x2d, 0x01, 0x7f, 0x27, - 0x41, 0x3f, 0xf6, 0xef, 0x89, 0xd8, 0x27, 0xe0, 0xdd, 0x17, 0xc5, 0xb6, 0x1d, 0xd7, 0x1d, 0x85, - 0x0e, 0xc5, 0x1a, 0xe0, 0x39, 0xbf, 0x73, 0x25, 0x52, 0x59, 0x18, 0x1e, 0xc8, 0x4a, 0x46, 0x55, - 0xf4, 0x5f, 0xd2, 0x00, 0x32, 0xbe, 0xdd, 0xc5, 0x51, 0xe4, 0x74, 0x31, 0xc2, 0xf7, 0x47, 0x38, - 0x7a, 0xf5, 0xa4, 0xfb, 0x16, 0x6c, 0xc4, 0xf8, 0x11, 0x75, 0x42, 0x6a, 0xf7, 0xf1, 0x44, 0x53, - 0x76, 0xa4, 0xdd, 0xf5, 0xca, 0x27, 0x4f, 0xa6, 0xc5, 0x9b, 0xcb, 0x61, 0xd7, 0xf1, 0x04, 0xe5, - 0x39, 0x5a, 0x83, 0x81, 0xd5, 0xf1, 0x04, 0xde, 0x05, 0xeb, 0x8b, 0x9c, 0xe6, 0x84, 0xce, 0xed, - 0xdd, 0x58, 0xe8, 0xcc, 0x25, 0xc2, 0xd4, 0x70, 0xe4, 0x86, 0x5e, 0x40, 0x49, 0x28, 0x5a, 0x91, - 0x5b, 0xe0, 0x2b, 0xb4, 0x00, 0xb8, 0x60, 0x2b, 0xa7, 0xea, 0x72, 0x60, 0xd9, 0x39, 0x17, 0x61, - 0x09, 0x64, 0x86, 0x71, 0xa9, 0x39, 0x19, 0x73, 0x7b, 0x1b, 0x46, 0xbc, 0x1a, 0x0c, 0xd1, 0x01, - 0xe1, 0x92, 0x58, 0x2d, 0xd2, 0x71, 0x6d, 0x39, 0x3a, 0x66, 0xff, 0x4f, 0x74, 0x84, 0x07, 0x00, - 0xf4, 0x92, 0x9d, 0x17, 0x69, 0x69, 0x9e, 0xfb, 0x8d, 0x2b, 0x72, 0x7f, 0x6a, 0x41, 0x8a, 0x64, - 0x17, 0xbc, 0x61, 0x03, 0x6c, 0xcc, 0x4f, 0x76, 0x88, 0xa3, 0x20, 0xd2, 0x32, 0x4b, 0x03, 0x5e, - 0x9b, 0x43, 0x20, 0x86, 0xa0, 0x77, 0xc0, 0x9b, 0xcf, 0x0e, 0x4a, 0xc5, 0xa1, 0x6e, 0x0f, 0xd6, - 0x81, 0x12, 0xc6, 0xe7, 0x48, 0x93, 0x78, 0xa0, 0xf7, 0x5f, 0x10, 0xe8, 0x12, 0x42, 0x1c, 0x6d, - 0x0e, 0xa0, 0x9f, 0x00, 0xed, 0x29, 0xab, 0x28, 0x20, 0x7e, 0x84, 0x4f, 0x7d, 0x8f, 0xf8, 0xd0, - 0x00, 0x6b, 0xfc, 0x3d, 0xe3, 0x33, 0x99, 0xdb, 0xd3, 0x2e, 0x47, 0x09, 0x5a, 0x86, 0xc9, 0x7e, - 0x47, 0xb1, 0xd9, 0x67, 0xf2, 0xf9, 0xcf, 0x45, 0x49, 0xff, 0x7d, 0x05, 0xbc, 0xfe, 0x1c, 0xc8, - 0x57, 0x3e, 0xe4, 0xff, 0xdd, 0x29, 0xac, 0x83, 0xb5, 0x11, 0x2b, 0xa8, 0x98, 0xc1, 0xd2, 0xcb, - 0x74, 0x6b, 0xa1, 0x0f, 0x02, 0x30, 0xc6, 0xd0, 0x7f, 0x4b, 0x83, 0x8d, 0x86, 0xef, 0x04, 0x51, - 0x8f, 0xd0, 0x64, 0x7f, 0x9a, 0x20, 0xdd, 0xc3, 0x4e, 0x1b, 0x27, 0x9d, 0xfa, 0xf8, 0x8a, 0x08, - 0x97, 0xfc, 0x8c, 0x7d, 0xee, 0x84, 0x84, 0x33, 0x7c, 0x0f, 0x28, 0xfd, 0xb1, 0xdd, 0x62, 0x24, - 0xe3, 0xd5, 0x5b, 0xaf, 0xe4, 0x58, 0x87, 0xea, 0x67, 0x9c, 0x77, 0x28, 0xd3, 0x1f, 0xc7, 0x04, - 0x2c, 0x82, 0xdc, 0x80, 0x74, 0x6d, 0xec, 0xd3, 0xd0, 0xc3, 0x91, 0xb6, 0xba, 0xb3, 0xba, 0xbb, - 0x8e, 0xc0, 0x80, 0x74, 0xcd, 0x58, 0x03, 0x37, 0xc1, 0x5a, 0xc7, 0xf3, 0x9d, 0x01, 0xbf, 0xb0, - 0x82, 0xe2, 0xc3, 0xf6, 0x4f, 0x32, 0x48, 0xc7, 0x11, 0xa1, 0x05, 0xd6, 0xf8, 0xc7, 0x0b, 0x5f, - 0x32, 0x57, 0xe7, 0x1b, 0x51, 0x12, 0x3a, 0x5d, 0x7c, 0x51, 0xe5, 0x06, 0x73, 0x4a, 0xea, 0xc1, - 0x11, 0xa0, 0x03, 0x36, 0xd9, 0x4a, 0xb3, 0xc5, 0x06, 0xb3, 0x05, 0xb3, 0x45, 0xfb, 0x97, 0x9e, - 0x0c, 0x18, 0x3e, 0xfb, 0x3c, 0xbd, 0x0d, 0x80, 0x78, 0x3e, 0xbc, 0x07, 0x98, 0x53, 0x61, 0x15, - 0x65, 0xe3, 0x27, 0xc0, 0x7b, 0x80, 0x59, 0x39, 0x5c, 0xc7, 0xb7, 0xdb, 0xd8, 0x1d, 0x78, 0x3e, - 0x16, 0x77, 0x06, 0xae, 0xe3, 0xd7, 0x62, 0x0d, 0x1b, 0xd8, 0x20, 0xf4, 0x48, 0xe8, 0xd1, 0x09, - 0x7f, 0xe4, 0xaf, 0x5d, 0x49, 0x81, 0xcb, 0x0d, 0x3a, 0x11, 0x6e, 0x68, 0x0e, 0xc0, 0xc0, 0x22, - 0xca, 0x76, 0x58, 0x77, 0xa2, 0x65, 0x96, 0x02, 0x6b, 0x08, 0x37, 0x34, 0x07, 0x80, 0x9f, 0x03, - 0x99, 0x4e, 0x02, 0xb6, 0xbc, 0x19, 0xd0, 0x87, 0x2f, 0x09, 0xd4, 0x9c, 0x04, 0x18, 0x71, 0x47, - 0xf8, 0x05, 0xb8, 0x3e, 0xf2, 0xc5, 0x90, 0x50, 0xdc, 0xb6, 0x69, 0x38, 0xf2, 0x63, 0x29, 0xee, - 0xaf, 0xc2, 0x8b, 0xb1, 0xbd, 0x68, 0xd3, 0x4c, 0x4c, 0x78, 0x33, 0x0f, 0x64, 0x45, 0x52, 0x57, - 0xf4, 0xdb, 0x40, 0x49, 0xee, 0x0a, 0x73, 0x20, 0x73, 0x7a, 0x54, 0x3f, 0x3a, 0xfe, 0xf2, 0x48, - 0x4d, 0xc1, 0x75, 0xa0, 0x20, 0xb3, 0x7a, 0x7c, 0x66, 0xa2, 0xaf, 0x54, 0x09, 0xe6, 0x41, 0x16, - 0x99, 0x95, 0xf2, 0x61, 0xf9, 0xa8, 0x6a, 0xaa, 0x2b, 0xba, 0x06, 0x94, 0xe4, 0x52, 0xcc, 0xb0, - 0x7e, 0x66, 0x57, 0xca, 0xcd, 0xea, 0xbe, 0x9a, 0xd2, 0x6f, 0x02, 0x99, 0x65, 0x09, 0xb7, 0x00, - 0x3c, 0xb3, 0xca, 0x76, 0xe3, 0xa8, 0x7c, 0xd2, 0xd8, 0x3f, 0x6e, 0xda, 0xf7, 0x4e, 0xcd, 0x53, - 0x53, 0x4d, 0xb1, 0x18, 0xd6, 0x91, 0xd5, 0xb4, 0xca, 0x87, 0xaa, 0xa4, 0xcb, 0xca, 0x8a, 0xba, - 0xa2, 0xff, 0x2a, 0x01, 0xf5, 0xe2, 0xa6, 0x62, 0x69, 0xdd, 0x01, 0x69, 0x76, 0x91, 0x51, 0xc4, - 0x27, 0xeb, 0xda, 0x9e, 0xf1, 0x8f, 0x25, 0x8a, 0x1d, 0x8d, 0x06, 0xf7, 0x42, 0xc2, 0x9b, 0xbd, - 0xab, 0xc9, 0x43, 0xcc, 0x88, 0x99, 0x9d, 0xbf, 0xb8, 0xba, 0x05, 0xd2, 0xb1, 0xed, 0x33, 0xf7, - 0x2e, 0x57, 0xab, 0xe6, 0x49, 0xd3, 0xac, 0xa9, 0x12, 0xfb, 0xa9, 0x7c, 0x72, 0x72, 0x68, 0x99, - 0x35, 0x75, 0x05, 0x66, 0xc1, 0x9a, 0x89, 0xd0, 0x31, 0x52, 0x57, 0x99, 0x55, 0xcd, 0xac, 0x1e, - 0x5a, 0x47, 0x66, 0x4d, 0x95, 0x0f, 0x64, 0x65, 0x55, 0x95, 0xf5, 0x6f, 0xc0, 0x6b, 0x55, 0xe2, - 0x77, 0xaa, 0x3d, 0x46, 0xd0, 0x2a, 0xf1, 0x29, 0xfe, 0x81, 0xc2, 0x8f, 0x00, 0x60, 0x1f, 0x9c, - 0x8e, 0xdf, 0x4e, 0xd6, 0x6f, 0xb6, 0x92, 0x9f, 0x4d, 0x8b, 0xd9, 0x6a, 0xac, 0xb5, 0x6a, 0x28, - 0x2b, 0x0c, 0xac, 0x36, 0xcb, 0x36, 0x70, 0x26, 0x03, 0xe2, 0xc4, 0x1f, 0xe7, 0xeb, 0x28, 0x39, - 0x56, 0x3e, 0x38, 0xff, 0xb3, 0x90, 0x3a, 0x9f, 0x15, 0xa4, 0x87, 0xb3, 0x82, 0xf4, 0x68, 0x56, - 0x90, 0xfe, 0x98, 0x15, 0xa4, 0x1f, 0x1f, 0x17, 0x52, 0x0f, 0x1f, 0x17, 0x52, 0x8f, 0x1e, 0x17, - 0x52, 0x5f, 0x2b, 0x49, 0x4d, 0x5a, 0x69, 0xfe, 0x3f, 0xc6, 0xad, 0xbf, 0x03, 0x00, 0x00, 0xff, - 0xff, 0x50, 0x56, 0x82, 0x44, 0x4c, 0x0d, 0x00, 0x00, + // 1302 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4f, 0x6f, 0xdb, 0xc6, + 0x12, 0x17, 0x6d, 0x5a, 0xa2, 0x56, 0x56, 0xcc, 0xb7, 0xcf, 0xcf, 0x8f, 0xcf, 0x78, 0x95, 0x5c, + 0x21, 0x6d, 0xdd, 0x7f, 0x14, 0xe2, 0xa4, 0x3d, 0xf4, 0x52, 0xe8, 0x0f, 0x03, 0xcb, 0x72, 0x6c, + 0x67, 0x65, 0xbb, 0x68, 0x8b, 0x96, 0x58, 0x51, 0x2b, 0x99, 0x90, 0xc4, 0x65, 0x96, 0x2b, 0xb5, + 0xca, 0xa7, 0xe8, 0x47, 0xe8, 0xad, 0xb7, 0x7e, 0x8b, 0x02, 0x3e, 0xe6, 0x98, 0x43, 0x61, 0xb4, + 0xca, 0xb1, 0xe8, 0x17, 0xc8, 0xa9, 0xd8, 0xe5, 0x52, 0x56, 0x9c, 0x38, 0x8d, 0x80, 0x06, 0x68, + 0x4f, 0x9e, 0x1d, 0xcd, 0xfc, 0x66, 0x76, 0xe6, 0x37, 0xb3, 0x34, 0xd8, 0xe8, 0x8f, 0xcb, 0xfd, + 0x71, 0x44, 0xd8, 0x98, 0xb0, 0x32, 0xc3, 0x5d, 0x6e, 0x87, 0x8c, 0x72, 0x0a, 0xff, 0xe3, 0x51, + 0xaf, 0xcf, 0x28, 0xf6, 0xce, 0xec, 0xfe, 0xd8, 0x4e, 0x2c, 0x36, 0xd7, 0xa5, 0x2a, 0x6c, 0x97, + 0x09, 0x63, 0x94, 0x45, 0xb1, 0xf1, 0xe6, 0x46, 0xa2, 0x1d, 0x12, 0x8e, 0x3b, 0x98, 0x63, 0xa5, + 0xb7, 0xe7, 0xc1, 0x07, 0xfe, 0x98, 0x04, 0x24, 0x8a, 0x66, 0x42, 0xd8, 0x9e, 0x89, 0xca, 0xbe, + 0x34, 0x6f, 0x9f, 0x08, 0x61, 0xbb, 0x1c, 0x71, 0xcc, 0x89, 0xb2, 0x29, 0x10, 0xee, 0x75, 0x64, + 0xa6, 0xe5, 0xf1, 0x6d, 0xf9, 0x37, 0x6c, 0xcf, 0x25, 0xbe, 0xb9, 0xde, 0xa3, 0x3d, 0x2a, 0xc5, + 0xb2, 0x90, 0x62, 0x6d, 0xe9, 0x77, 0x1d, 0xe4, 0x11, 0xee, 0xf2, 0x5d, 0x82, 0x19, 0x6f, 0x13, + 0xcc, 0xe1, 0xd7, 0xc0, 0x60, 0x38, 0xe8, 0x11, 0xd7, 0xef, 0x58, 0xda, 0x96, 0xb6, 0xad, 0x57, + 0x6b, 0xd3, 0x8b, 0x62, 0x06, 0x09, 0x5d, 0xa3, 0xfe, 0xf4, 0xa2, 0x78, 0xa7, 0xe7, 0xf3, 0xb3, + 0x51, 0xdb, 0xf6, 0xe8, 0xb0, 0x3c, 0x2b, 0x46, 0xa7, 0x7d, 0x29, 0x97, 0xc3, 0x7e, 0xaf, 0xac, + 0x6e, 0x6e, 0x2b, 0x3f, 0x94, 0x91, 0xa0, 0x8d, 0x0e, 0x8c, 0xc0, 0x5a, 0x97, 0xd1, 0xa1, 0xcb, + 0x48, 0x38, 0xf0, 0x3d, 0x2c, 0xc2, 0x2c, 0x6d, 0x69, 0xdb, 0xf9, 0x6a, 0x73, 0x7a, 0x51, 0xcc, + 0xdf, 0x65, 0x74, 0x88, 0xe2, 0x5f, 0x64, 0xb0, 0x8f, 0x17, 0x0b, 0x96, 0x78, 0xa2, 0x7c, 0x77, + 0x0e, 0xa8, 0x03, 0x87, 0x20, 0xcf, 0xe9, 0x7c, 0xc8, 0x65, 0x19, 0xb2, 0x31, 0xbd, 0x28, 0xe6, + 0x8e, 0xe9, 0x5f, 0x11, 0x30, 0xc7, 0xe9, 0x65, 0x38, 0x08, 0x74, 0x4e, 0xd8, 0xd0, 0xd2, 0x45, + 0xfd, 0x90, 0x94, 0xe1, 0x06, 0x48, 0x7b, 0x74, 0x38, 0xf4, 0xb9, 0xb5, 0x22, 0xb5, 0xea, 0x04, + 0x2d, 0x90, 0x79, 0x30, 0xf2, 0x49, 0xe4, 0x11, 0x2b, 0xbd, 0xa5, 0x6d, 0x1b, 0x28, 0x39, 0xc2, + 0x87, 0xe0, 0xff, 0x03, 0xdc, 0xeb, 0xf9, 0x41, 0xcf, 0xed, 0xd2, 0xc1, 0x80, 0x7e, 0x43, 0x58, + 0xe4, 0xd2, 0xc0, 0x4d, 0xcc, 0x8d, 0xad, 0xe5, 0xed, 0xdc, 0xce, 0x6d, 0xfb, 0x85, 0x8c, 0xb4, + 0x67, 0x14, 0xba, 0xa4, 0x95, 0xbd, 0xaf, 0xc4, 0xaa, 0x7e, 0x7e, 0x51, 0x4c, 0xa1, 0xff, 0x29, + 0xf8, 0xbb, 0x09, 0xfa, 0x61, 0x70, 0x5f, 0xc5, 0x3e, 0x02, 0x6f, 0xbd, 0x2c, 0xb6, 0x8b, 0x3d, + 0x6f, 0xc4, 0x30, 0x27, 0x16, 0x90, 0x39, 0xbf, 0x79, 0x2d, 0x52, 0x45, 0x19, 0xee, 0xe9, 0x46, + 0xc6, 0x34, 0x4a, 0x3f, 0xa4, 0x01, 0x14, 0x7c, 0xbb, 0x47, 0xa2, 0x08, 0xf7, 0x08, 0x22, 0x0f, + 0x46, 0x24, 0x7a, 0xfd, 0xa4, 0xfb, 0x0a, 0xac, 0xc5, 0xf8, 0x11, 0xc7, 0x8c, 0xbb, 0x7d, 0x32, + 0xb1, 0x8c, 0x2d, 0x6d, 0x7b, 0xb5, 0xfa, 0xd1, 0xd3, 0x8b, 0xe2, 0xad, 0xc5, 0xb0, 0x9b, 0x64, + 0x82, 0xf2, 0x12, 0xad, 0x25, 0xc0, 0x9a, 0x64, 0x02, 0xef, 0x81, 0xd5, 0x79, 0x4e, 0x4b, 0x42, + 0xe7, 0x76, 0x6e, 0xce, 0x75, 0xe6, 0x0a, 0x61, 0xea, 0x24, 0xf2, 0x98, 0x1f, 0x72, 0xca, 0x54, + 0x2b, 0x72, 0x73, 0x7c, 0x85, 0x0d, 0x00, 0x2e, 0xd9, 0x2a, 0xa9, 0xba, 0x18, 0x58, 0x76, 0xc6, + 0x45, 0x58, 0x06, 0x99, 0x61, 0x5c, 0x6a, 0x49, 0xc6, 0xdc, 0xce, 0x9a, 0x1d, 0xaf, 0x06, 0x5b, + 0x75, 0x40, 0xb9, 0x24, 0x56, 0xf3, 0x74, 0x5c, 0x59, 0x8c, 0x8e, 0xd9, 0x7f, 0x12, 0x1d, 0xe1, + 0x1e, 0x00, 0x67, 0xc9, 0xce, 0x8b, 0xac, 0xb4, 0xcc, 0xfd, 0xe6, 0x35, 0xb9, 0x3f, 0xb3, 0x20, + 0x55, 0xb2, 0x73, 0xde, 0xb0, 0x05, 0xd6, 0x66, 0x27, 0x97, 0x91, 0x28, 0x8c, 0xac, 0xcc, 0xc2, + 0x80, 0x37, 0x66, 0x10, 0x48, 0x20, 0x94, 0xba, 0xe0, 0xbf, 0xcf, 0x0f, 0x4a, 0x15, 0x73, 0xef, + 0x0c, 0x36, 0x81, 0xc1, 0xe2, 0x73, 0x64, 0x69, 0x32, 0xd0, 0xbb, 0x2f, 0x09, 0x74, 0x05, 0x21, + 0x8e, 0x36, 0x03, 0x28, 0x1d, 0x01, 0xeb, 0x19, 0xab, 0x28, 0xa4, 0x41, 0x44, 0x4e, 0x02, 0x9f, + 0x06, 0xd0, 0x06, 0x2b, 0xf2, 0x3d, 0x93, 0x33, 0x99, 0xdb, 0xb1, 0xae, 0x46, 0x09, 0xdb, 0xb6, + 0x23, 0x7e, 0x47, 0xb1, 0xd9, 0x27, 0xfa, 0xf9, 0xf7, 0x45, 0xad, 0xf4, 0xf3, 0x12, 0xf8, 0xf7, + 0x0b, 0x20, 0x5f, 0xfb, 0x90, 0xff, 0x7d, 0xa7, 0xb0, 0x09, 0x56, 0x46, 0xa2, 0xa0, 0x6a, 0x06, + 0xcb, 0xaf, 0xd2, 0xad, 0xb9, 0x3e, 0x28, 0xc0, 0x18, 0xa3, 0xf4, 0x5b, 0x1a, 0xac, 0xb5, 0x02, + 0x1c, 0x46, 0x67, 0x94, 0x27, 0xfb, 0xd3, 0x01, 0xe9, 0x33, 0x82, 0x3b, 0x24, 0xe9, 0xd4, 0x87, + 0xd7, 0x44, 0xb8, 0xe2, 0x67, 0xef, 0x4a, 0x27, 0xa4, 0x9c, 0xe1, 0xdb, 0xc0, 0xe8, 0x8f, 0xdd, + 0xb6, 0x20, 0x99, 0xac, 0xde, 0x6a, 0x35, 0x27, 0x3a, 0xd4, 0x3c, 0x95, 0xbc, 0x43, 0x99, 0xfe, + 0x38, 0x26, 0x60, 0x11, 0xe4, 0x06, 0xb4, 0xe7, 0x92, 0x80, 0x33, 0x9f, 0x44, 0xd6, 0xf2, 0xd6, + 0xf2, 0xf6, 0x2a, 0x02, 0x03, 0xda, 0x73, 0x62, 0x0d, 0x5c, 0x07, 0x2b, 0x5d, 0x3f, 0xc0, 0x03, + 0x79, 0x61, 0x03, 0xc5, 0x87, 0xcd, 0x1f, 0x75, 0x90, 0x8e, 0x23, 0xc2, 0x06, 0x58, 0x91, 0x1f, + 0x2f, 0x72, 0xc9, 0x5c, 0x9f, 0x6f, 0xc4, 0x29, 0xc3, 0x3d, 0x72, 0x59, 0xe5, 0x96, 0x70, 0x4a, + 0xea, 0x21, 0x11, 0x20, 0x06, 0xeb, 0x62, 0xa5, 0xb9, 0x6a, 0x83, 0xb9, 0x8a, 0xd9, 0xaa, 0xfd, + 0x0b, 0x4f, 0x06, 0x64, 0xcf, 0x3f, 0x4f, 0x6f, 0x00, 0xa0, 0x9e, 0x0f, 0xff, 0x21, 0x91, 0x54, + 0x58, 0x46, 0xd9, 0xf8, 0x09, 0xf0, 0x1f, 0x12, 0x51, 0x0e, 0x0f, 0x07, 0x6e, 0x87, 0x78, 0x03, + 0x3f, 0x20, 0xea, 0xce, 0xc0, 0xc3, 0x41, 0x3d, 0xd6, 0x88, 0x81, 0x0d, 0x99, 0x4f, 0x99, 0xcf, + 0x27, 0xf2, 0x91, 0xbf, 0x71, 0x2d, 0x05, 0xae, 0x36, 0xe8, 0x48, 0xb9, 0xa1, 0x19, 0x80, 0x00, + 0x8b, 0xb8, 0xd8, 0x61, 0xbd, 0x89, 0x95, 0x59, 0x08, 0xac, 0xa5, 0xdc, 0xd0, 0x0c, 0x00, 0x7e, + 0x0a, 0x74, 0x3e, 0x09, 0xc5, 0xf2, 0x16, 0x40, 0xef, 0xbf, 0x22, 0xd0, 0xf1, 0x24, 0x24, 0x48, + 0x3a, 0xc2, 0x13, 0xf0, 0x4e, 0x87, 0x84, 0x8c, 0x78, 0x98, 0x93, 0x8e, 0x3b, 0x0a, 0xd4, 0xbc, + 0x88, 0x03, 0x67, 0xa3, 0x20, 0x96, 0xe2, 0x56, 0x1b, 0xb2, 0x2e, 0x37, 0x2f, 0xcd, 0x4f, 0xe6, + 0xac, 0x8f, 0x13, 0x63, 0xd9, 0xe1, 0x3d, 0xdd, 0xd0, 0xcc, 0xa5, 0xd2, 0x1d, 0x60, 0x24, 0x05, + 0x80, 0x39, 0x90, 0x39, 0x39, 0x68, 0x1e, 0x1c, 0x7e, 0x76, 0x60, 0xa6, 0xe0, 0x2a, 0x30, 0x90, + 0x53, 0x3b, 0x3c, 0x75, 0xd0, 0xe7, 0xa6, 0x06, 0xf3, 0x20, 0x8b, 0x9c, 0x6a, 0x65, 0xbf, 0x72, + 0x50, 0x73, 0xcc, 0xa5, 0x92, 0x05, 0x8c, 0xe4, 0xa6, 0xc2, 0xb0, 0x79, 0xea, 0x56, 0x2b, 0xc7, + 0xb5, 0x5d, 0x33, 0x55, 0xba, 0x05, 0x74, 0x91, 0x3a, 0xdc, 0x00, 0xf0, 0xb4, 0x51, 0x71, 0x5b, + 0x07, 0x95, 0xa3, 0xd6, 0xee, 0xe1, 0xb1, 0x7b, 0xff, 0xc4, 0x39, 0x71, 0xcc, 0x94, 0x88, 0xd1, + 0x38, 0x68, 0x1c, 0x37, 0x2a, 0xfb, 0xa6, 0x56, 0xd2, 0x8d, 0x25, 0x73, 0xa9, 0xf4, 0x93, 0x06, + 0xcc, 0xcb, 0xeb, 0xab, 0x4d, 0x76, 0x17, 0xa4, 0xc5, 0x95, 0x46, 0x91, 0x1c, 0xb7, 0x1b, 0x3b, + 0xf6, 0x9f, 0xd6, 0x2d, 0x76, 0xb4, 0x5b, 0xd2, 0x0b, 0x29, 0x6f, 0xf1, 0xd8, 0x26, 0xaf, 0xb3, + 0x60, 0x6b, 0x76, 0xf6, 0x0c, 0x97, 0x1a, 0x20, 0x1d, 0xdb, 0x3e, 0x77, 0xef, 0x4a, 0xad, 0xe6, + 0x1c, 0x1d, 0x3b, 0x75, 0x53, 0x13, 0x3f, 0x55, 0x8e, 0x8e, 0xf6, 0x1b, 0x4e, 0xdd, 0x5c, 0x82, + 0x59, 0xb0, 0xe2, 0x20, 0x74, 0x88, 0xcc, 0x65, 0x61, 0x55, 0x77, 0x6a, 0xfb, 0x8d, 0x03, 0xa7, + 0x6e, 0xea, 0x7b, 0xba, 0xb1, 0x6c, 0xea, 0xa5, 0x2f, 0xc1, 0xbf, 0x6a, 0x34, 0xe8, 0xd6, 0xce, + 0x04, 0x6b, 0x6b, 0x34, 0xe0, 0xe4, 0x5b, 0x0e, 0x3f, 0x00, 0x40, 0x7c, 0x85, 0xe2, 0xa0, 0x93, + 0xec, 0xe4, 0x6c, 0x35, 0x3f, 0xbd, 0x28, 0x66, 0x6b, 0xb1, 0xb6, 0x51, 0x47, 0x59, 0x65, 0xd0, + 0xe8, 0x88, 0x6c, 0x43, 0x3c, 0x19, 0x50, 0x1c, 0x7f, 0xb1, 0xaf, 0xa2, 0xe4, 0x58, 0x7d, 0xef, + 0xfc, 0xd7, 0x42, 0xea, 0x7c, 0x5a, 0xd0, 0x1e, 0x4d, 0x0b, 0xda, 0xe3, 0x69, 0x41, 0xfb, 0x65, + 0x5a, 0xd0, 0xbe, 0x7b, 0x52, 0x48, 0x3d, 0x7a, 0x52, 0x48, 0x3d, 0x7e, 0x52, 0x48, 0x7d, 0x61, + 0x24, 0x35, 0x69, 0xa7, 0xe5, 0x3f, 0x1e, 0xb7, 0xff, 0x08, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x9b, + 0xc1, 0x73, 0x61, 0x0d, 0x00, 0x00, } func (m *RaftHeartbeat) Marshal() (dAtA []byte, err error) { @@ -1085,9 +1087,9 @@ func (m *SnapshotRequest_Header) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x48 } - if m.UnreplicatedTruncatedState { + if m.DeprecatedUnreplicatedTruncatedState { i-- - if m.UnreplicatedTruncatedState { + if m.DeprecatedUnreplicatedTruncatedState { dAtA[i] = 1 } else { dAtA[i] = 0 @@ -1402,7 +1404,7 @@ func (m *SnapshotRequest_Header) Size() (n int) { if m.Strategy != 0 { n += 1 + sovRaft(uint64(m.Strategy)) } - if m.UnreplicatedTruncatedState { + if m.DeprecatedUnreplicatedTruncatedState { n += 2 } if m.Type != 0 { @@ -2713,7 +2715,7 @@ func (m *SnapshotRequest_Header) Unmarshal(dAtA []byte) error { } case 8: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field UnreplicatedTruncatedState", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedUnreplicatedTruncatedState", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -2730,7 +2732,7 @@ func (m *SnapshotRequest_Header) Unmarshal(dAtA []byte) error { break } } - m.UnreplicatedTruncatedState = bool(v != 0) + m.DeprecatedUnreplicatedTruncatedState = bool(v != 0) case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) diff --git a/pkg/kv/kvserver/raft.proto b/pkg/kv/kvserver/raft.proto index d525efe43b0d..de23d895581b 100644 --- a/pkg/kv/kvserver/raft.proto +++ b/pkg/kv/kvserver/raft.proto @@ -183,15 +183,16 @@ message SnapshotRequest { Type type = 9; // Whether the snapshot uses the unreplicated RaftTruncatedState or not. - // This is generally always true at 2.2 and above outside of the migration - // phase, though theoretically it could take a long time for all ranges - // to update to the new mechanism. This bool is true iff the Raft log at - // the snapshot's applied index is using the new key. In particular, it - // is true if the index itself carries out the migration (in which case - // the data in the snapshot contains neither key). + // This is always true for snapshots generated in v21.1+ clusters. In v20.2 + // it was possible for ranges to be using the replicated variant. v21.1 + // therefore had code expecting that possibility (unlike v21.2 code, where + // this field is assumed to always be true and thus never read). For + // compatibility with v21.1 nodes however, v21.2 has to explicitly set this + // field to true. In v22.1 we can remove it entirely seeing as how v21.2 + // code never reads the field. // - // See VersionUnreplicatedRaftTruncatedState. - bool unreplicated_truncated_state = 8; + // TODO(irfansharif): Remove this in v22.1. + bool deprecated_unreplicated_truncated_state = 8; } Header header = 1; diff --git a/pkg/kv/kvserver/rditer/replica_data_iter_test.go b/pkg/kv/kvserver/rditer/replica_data_iter_test.go index 0f4aecaabbfc..8f071be6f7e8 100644 --- a/pkg/kv/kvserver/rditer/replica_data_iter_test.go +++ b/pkg/kv/kvserver/rditer/replica_data_iter_test.go @@ -82,11 +82,7 @@ func createRangeData( {keys.AbortSpanKey(desc.RangeID, testTxnID2), ts0}, {keys.RangeGCThresholdKey(desc.RangeID), ts0}, {keys.RangeAppliedStateKey(desc.RangeID), ts0}, - {keys.RaftAppliedIndexLegacyKey(desc.RangeID), ts0}, - {keys.RaftTruncatedStateLegacyKey(desc.RangeID), ts0}, {keys.RangeLeaseKey(desc.RangeID), ts0}, - {keys.LeaseAppliedIndexLegacyKey(desc.RangeID), ts0}, - {keys.RangeStatsLegacyKey(desc.RangeID), ts0}, {keys.RangeTombstoneKey(desc.RangeID), ts0}, {keys.RaftHardStateKey(desc.RangeID), ts0}, {keys.RaftLogKey(desc.RangeID, 1), ts0}, diff --git a/pkg/kv/kvserver/replica_application_result.go b/pkg/kv/kvserver/replica_application_result.go index 7c933ef1bd35..33e1a3e33789 100644 --- a/pkg/kv/kvserver/replica_application_result.go +++ b/pkg/kv/kvserver/replica_application_result.go @@ -320,12 +320,6 @@ func (r *Replica) handleVersionResult(ctx context.Context, version *roachpb.Vers r.mu.Unlock() } -func (r *Replica) handleUsingAppliedStateKeyResult(ctx context.Context) { - r.mu.Lock() - r.mu.state.UsingAppliedStateKey = true - r.mu.Unlock() -} - func (r *Replica) handleComputeChecksumResult(ctx context.Context, cc *kvserverpb.ComputeChecksum) { r.computeChecksumPostApply(ctx, *cc) } diff --git a/pkg/kv/kvserver/replica_application_state_machine.go b/pkg/kv/kvserver/replica_application_state_machine.go index c5112c60e764..30dda3f5eae4 100644 --- a/pkg/kv/kvserver/replica_application_state_machine.go +++ b/pkg/kv/kvserver/replica_application_state_machine.go @@ -15,7 +15,6 @@ import ( "fmt" "time" - "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/apply" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts/ctpb" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" @@ -396,10 +395,6 @@ type replicaAppBatch struct { // closed timestamp carried by this command. Synthetic timestamps are not // registered here. maxTS hlc.ClockTimestamp - // migrateToAppliedStateKey tracks whether any command in the batch - // triggered a migration to the replica applied state key. If so, this - // migration will be performed when the application batch is committed. - migrateToAppliedStateKey bool // changeRemovesReplica tracks whether the command in the batch (there must // be only one) removes this replica from the range. changeRemovesReplica bool @@ -729,19 +724,8 @@ func (b *replicaAppBatch) runPreApplyTriggersAfterStagingWriteBatch( } if res.State != nil && res.State.TruncatedState != nil { - activeVersion := b.r.ClusterSettings().Version.ActiveVersion(ctx).Version - migrationVersion := clusterversion.ByKey(clusterversion.TruncatedAndRangeAppliedStateMigration) - // NB: We're being deliberate here in using the less-than operator (as - // opposed to LessEq). TruncatedAndRangeAppliedStateMigration indicates - // that the migration to move to the unreplicated truncated - // state is currently underway. It's only when the active cluster - // version has moved past it that we can assume that the migration has - // completed. - assertNoLegacy := migrationVersion.Less(activeVersion) - - if apply, err := handleTruncatedStateBelowRaft( + if apply, err := handleTruncatedStateBelowRaftPreApply( ctx, b.state.TruncatedState, res.State.TruncatedState, b.r.raftMu.stateLoader, b.batch, - assertNoLegacy, ); err != nil { return wrapWithNonDeterministicFailure(err, "unable to handle truncated state") } else if !apply { @@ -851,10 +835,6 @@ func (b *replicaAppBatch) stageTrivialReplicatedEvalResult( // serialize on the stats key. deltaStats := res.Delta.ToStats() b.state.Stats.Add(deltaStats) - - if res.State != nil && res.State.UsingAppliedStateKey && !b.state.UsingAppliedStateKey { - b.migrateToAppliedStateKey = true - } } // ApplyToStateMachine implements the apply.Batch interface. The method handles @@ -960,48 +940,13 @@ func (b *replicaAppBatch) ApplyToStateMachine(ctx context.Context) error { // batch's RocksDB batch. This records the highest raft and lease index that // have been applied as of this batch. It also records the Range's mvcc stats. func (b *replicaAppBatch) addAppliedStateKeyToBatch(ctx context.Context) error { + // Set the range applied state, which includes the last applied raft and + // lease index along with the mvcc stats, all in one key. loader := &b.r.raftMu.stateLoader - if b.migrateToAppliedStateKey { - // A Raft command wants us to begin using the RangeAppliedState key - // and we haven't performed the migration yet. Delete the old keys - // that this new key is replacing. - // - // NB: entering this branch indicates that the batch contains only a - // single non-trivial command. - err := loader.MigrateToRangeAppliedStateKey(ctx, b.batch, b.state.Stats) - if err != nil { - return wrapWithNonDeterministicFailure(err, "unable to migrate to range applied state") - } - b.state.UsingAppliedStateKey = true - } - if b.state.UsingAppliedStateKey { - // Set the range applied state, which includes the last applied raft and - // lease index along with the mvcc stats, all in one key. - if err := loader.SetRangeAppliedState( - ctx, b.batch, b.state.RaftAppliedIndex, b.state.LeaseAppliedIndex, - b.state.Stats, &b.state.RaftClosedTimestamp, - ); err != nil { - return wrapWithNonDeterministicFailure(err, "unable to set range applied state") - } - } else { - // Advance the last applied index. We use a blind write in order to avoid - // reading the previous applied index keys on every write operation. This - // requires a little additional work in order maintain the MVCC stats. - var appliedIndexNewMS enginepb.MVCCStats - if err := loader.SetLegacyAppliedIndexBlind( - ctx, b.batch, &appliedIndexNewMS, b.state.RaftAppliedIndex, b.state.LeaseAppliedIndex, - ); err != nil { - return wrapWithNonDeterministicFailure(err, "unable to set applied index") - } - b.state.Stats.SysBytes += appliedIndexNewMS.SysBytes - - loader.CalcAppliedIndexSysBytes(b.state.RaftAppliedIndex, b.state.LeaseAppliedIndex) - - // Set the legacy MVCC stats key. - if err := loader.SetMVCCStats(ctx, b.batch, b.state.Stats); err != nil { - return wrapWithNonDeterministicFailure(err, "unable to update MVCCStats") - } - } - return nil + return loader.SetRangeAppliedState( + ctx, b.batch, b.state.RaftAppliedIndex, b.state.LeaseAppliedIndex, + b.state.Stats, &b.state.RaftClosedTimestamp, + ) } func (b *replicaAppBatch) recordStatsOnCommit() { @@ -1301,11 +1246,6 @@ func (sm *replicaStateMachine) handleNonTrivialReplicatedEvalResult( rResult.State.Desc = nil } - if rResult.State.UsingAppliedStateKey { - sm.r.handleUsingAppliedStateKeyResult(ctx) - rResult.State.UsingAppliedStateKey = false - } - if (*rResult.State == kvserverpb.ReplicaState{}) { rResult.State = nil } diff --git a/pkg/kv/kvserver/replica_command.go b/pkg/kv/kvserver/replica_command.go index 2b7572ff4667..cf7a9db9a649 100644 --- a/pkg/kv/kvserver/replica_command.go +++ b/pkg/kv/kvserver/replica_command.go @@ -2440,42 +2440,20 @@ func (r *Replica) sendSnapshot( return &benignError{errors.Wrap(errMarkSnapshotError, "raft status not initialized")} } - usesReplicatedTruncatedState, err := storage.MVCCGetProto( - ctx, snap.EngineSnap, keys.RaftTruncatedStateLegacyKey(r.RangeID), hlc.Timestamp{}, nil, storage.MVCCGetOptions{}, - ) - if err != nil { - return errors.Wrap(err, "loading legacy truncated state") - } - - canAvoidSendingLog := !usesReplicatedTruncatedState && - snap.State.TruncatedState.Index < snap.State.RaftAppliedIndex - - if canAvoidSendingLog { - // If we're not using a legacy (replicated) truncated state, we avoid - // sending the (past) Raft log in the snapshot in the first place and - // send only those entries that are actually useful to the follower. - // This is done by changing the truncated state, which we're allowed - // to do since it is not a replicated key (and thus not subject to - // matching across replicas). The actual sending happens here: - _ = (*kvBatchSnapshotStrategy)(nil).Send - // and results in no log entries being sent at all. Note that - // Metadata.Index is really the applied index of the replica. - snap.State.TruncatedState = &roachpb.RaftTruncatedState{ - Index: snap.RaftSnap.Metadata.Index, - Term: snap.RaftSnap.Metadata.Term, - } + // We avoid shipping over the past Raft log in the snapshot by changing + // the truncated state (we're allowed to -- it's an unreplicated key and not + // subject to mapping across replicas). The actual sending happens here: + _ = (*kvBatchSnapshotStrategy)(nil).Send + // and results in no log entries being sent at all. Note that + // Metadata.Index is really the applied index of the replica. + snap.State.TruncatedState = &roachpb.RaftTruncatedState{ + Index: snap.RaftSnap.Metadata.Index, + Term: snap.RaftSnap.Metadata.Term, } req := SnapshotRequest_Header{ - State: snap.State, - // Tell the recipient whether it needs to synthesize the new - // unreplicated TruncatedState. It could tell by itself by peeking into - // the data, but it uses a write only batch for performance which - // doesn't support that; this is easier. Notably, this is true if the - // snap index itself is the one at which the migration happens. - // - // See VersionUnreplicatedRaftTruncatedState. - UnreplicatedTruncatedState: !usesReplicatedTruncatedState, + State: snap.State, + DeprecatedUnreplicatedTruncatedState: true, RaftMessageRequest: RaftMessageRequest{ RangeID: r.RangeID, FromReplica: sender, diff --git a/pkg/kv/kvserver/replica_consistency.go b/pkg/kv/kvserver/replica_consistency.go index 16f9c036a06d..cbde7ff82255 100644 --- a/pkg/kv/kvserver/replica_consistency.go +++ b/pkg/kv/kvserver/replica_consistency.go @@ -651,17 +651,10 @@ func (r *Replica) sha512( if err != nil { return nil, err } - if rangeAppliedState == nil { - // This error is transient: the range applied state is used in v2.1 already - // but is migrated into on a per-range basis for clusters bootstrapped before - // v2.1. Clusters bootstrapped at v2.1 or higher will never hit this path since - // there's always an applied state. - return nil, errors.New("no range applied state found") - } result.PersistedMS = rangeAppliedState.RangeStats.ToStats() if statsOnly { - b, err := protoutil.Marshal(rangeAppliedState) + b, err := protoutil.Marshal(&rangeAppliedState) if err != nil { return nil, err } @@ -672,7 +665,7 @@ func (r *Replica) sha512( } kv.Key = keys.RangeAppliedStateKey(desc.RangeID) var v roachpb.Value - if err := v.SetProto(rangeAppliedState); err != nil { + if err := v.SetProto(&rangeAppliedState); err != nil { return nil, err } kv.Value = v.RawBytes diff --git a/pkg/kv/kvserver/replica_proposal.go b/pkg/kv/kvserver/replica_proposal.go index 2e3181f3ac4c..de80e41ec147 100644 --- a/pkg/kv/kvserver/replica_proposal.go +++ b/pkg/kv/kvserver/replica_proposal.go @@ -18,7 +18,6 @@ import ( "unsafe" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" @@ -219,12 +218,12 @@ func (r *Replica) computeChecksumPostApply(ctx context.Context, cc kvserverpb.Co snap := r.store.engine.NewSnapshot() if cc.Checkpoint { sl := stateloader.Make(r.RangeID) - rai, _, err := sl.LoadAppliedIndex(ctx, snap) + as, err := sl.LoadRangeAppliedState(ctx, snap) if err != nil { log.Warningf(ctx, "unable to load applied index, continuing anyway") } // NB: the names here will match on all nodes, which is nice for debugging. - tag := fmt.Sprintf("r%d_at_%d", r.RangeID, rai) + tag := fmt.Sprintf("r%d_at_%d", r.RangeID, as.RaftAppliedIndex) if dir, err := r.store.checkpoint(ctx, tag); err != nil { log.Warningf(ctx, "unable to create checkpoint %s: %+v", dir, err) } else { @@ -865,41 +864,6 @@ func (r *Replica) evaluateProposal( if res.Replicated.Delta.ContainsEstimates > 0 { res.Replicated.Delta.ContainsEstimates *= 2 } - - // If the RangeAppliedState key is not being used and the cluster version is - // high enough to guarantee that all current and future binaries will - // understand the key, we send the migration flag through Raft. Because - // there is a delay between command proposal and application, we may end up - // setting this migration flag multiple times. This is ok, because the - // migration is idempotent. - // TODO(nvanbenschoten): This will be baked in to 2.1, so it can be removed - // in the 2.2 release. - r.mu.RLock() - usingAppliedStateKey := r.mu.state.UsingAppliedStateKey - r.mu.RUnlock() - if !usingAppliedStateKey { - // The range applied state was originally introduced in v2.1, and in - // v21.1 we guarantee that it's used for all ranges, which we assert - // on below. If we're not running 21.1 yet, migrate over as we've - // done since the introduction of the applied state key. - activeVersion := r.ClusterSettings().Version.ActiveVersion(ctx).Version - migrationVersion := clusterversion.ByKey(clusterversion.TruncatedAndRangeAppliedStateMigration) - if migrationVersion.Less(activeVersion) { - log.Fatal(ctx, "not using applied state key in v21.1") - } - // The range applied state was introduced in v2.1. It's possible to - // still find ranges that haven't activated it. If so, activate it. - // We can remove this code if we introduce a boot-time check that - // fails the startup process when any legacy replicas are found. The - // operator can then run the old binary for a while to upgrade the - // stragglers. - // - // TODO(irfansharif): Is this still applicable? - if res.Replicated.State == nil { - res.Replicated.State = &kvserverpb.ReplicaState{} - } - res.Replicated.State.UsingAppliedStateKey = true - } } return &res, needConsensus, nil diff --git a/pkg/kv/kvserver/replica_raft.go b/pkg/kv/kvserver/replica_raft.go index eca997e7b53a..dd1ccbb676d8 100644 --- a/pkg/kv/kvserver/replica_raft.go +++ b/pkg/kv/kvserver/replica_raft.go @@ -1798,46 +1798,44 @@ func (r *Replica) acquireMergeLock( return rightRepl.raftMu.Unlock, nil } -// handleTruncatedStateBelowRaft is called when a Raft command updates the truncated -// state. This isn't 100% trivial for two reasons: -// - in 19.1 we're making the TruncatedState key unreplicated, so there's a migration -// - we're making use of the above by not sending the Raft log in snapshots (the truncated -// state effectively determines the first index of the log, which requires it to be unreplicated). -// Updates to the HardState are sent out by a leaseholder truncating the log based on its local -// knowledge. For example, the leader might have a log 10..100 and truncates to 50, and will send -// out a TruncatedState with Index 50 to that effect. However, some replicas may not even have log -// entries that old, and must make sure to ignore this update to the truncated state, as it would -// otherwise clobber their "newer" truncated state. +// handleTruncatedStateBelowRaftPreApply is called before applying a Raft +// command that updates the truncated state. +// +// The truncated state of a replica determines where its Raft log starts (by +// giving the last index that was already deleted). It's unreplicated -- it can +// differ between replicas at the same applied index. This divergence occurs +// primarily occurs through snapshots that contain no log entries; the truncated +// index in the snapshot is set to equal the applied index it was generated +// from. The truncation itself then is a purely replicated side effect. +// +// Updates to the HardState are sent out by a leaseholder truncating the log +// based on its local knowledge. For example, the leader might have a log +// 10..100 and truncates to 50, and will send out a TruncatedState with Index 50 +// to that effect. However, some replicas may not even have log entries that +// old and must make sure to ignore this update to the truncated state, as it +// would otherwise clobber their "newer" truncated state. The truncated state +// provided by the leader then is merely a suggested one -- we could ignore it +// and still be correct. +// +// We also rely on log truncations happening in the apply loop -- this makes +// sure that a truncation does not remove entries to be applied that we haven't +// yet. Since a truncation only ever removes committed log indexes, and after +// choosing the index gets proposed, the truncation command itself will be +// assigned an index higher than the one it could possibly remove. By the time +// the truncation itself is handled, the state machine will have applied all +// entries the truncation could possibly affect. // // The returned boolean tells the caller whether to apply the truncated state's // side effects, which means replacing the in-memory TruncatedState and applying // the associated RaftLogDelta. It is usually expected to be true, but may not // be for the first truncation after on a replica that recently received a // snapshot. -func handleTruncatedStateBelowRaft( +func handleTruncatedStateBelowRaftPreApply( ctx context.Context, - oldTruncatedState, newTruncatedState *roachpb.RaftTruncatedState, + currentTruncatedState, suggestedTruncatedState *roachpb.RaftTruncatedState, loader stateloader.StateLoader, readWriter storage.ReadWriter, - assertNoLegacy bool, ) (_apply bool, _ error) { - // If this is a log truncation, load the resulting unreplicated or legacy - // replicated truncated state (in that order). If the migration is happening - // in this command, the result will be an empty message. In steady state - // after the migration, it's the unreplicated truncated state not taking - // into account the current truncation (since the key is unreplicated). - // Either way, we'll update it below. - // - // See VersionUnreplicatedRaftTruncatedState for details. - truncStatePostApply, truncStateIsLegacy, err := loader.LoadRaftTruncatedState(ctx, readWriter) - if err != nil { - return false, errors.Wrap(err, "loading truncated state") - } - - if assertNoLegacy && truncStateIsLegacy { - log.Fatalf(ctx, "found legacy truncated state which should no longer exist") - } - // Truncate the Raft log from the entry after the previous // truncation index to the new truncation index. This is performed // atomically with the raft command application so that the @@ -1851,42 +1849,31 @@ func handleTruncatedStateBelowRaft( // perform well here because the tombstones could be "collapsed", // but it is hardly worth the risk at this point. prefixBuf := &loader.RangeIDPrefixBuf - for idx := oldTruncatedState.Index + 1; idx <= newTruncatedState.Index; idx++ { + for idx := currentTruncatedState.Index + 1; idx <= suggestedTruncatedState.Index; idx++ { // NB: RangeIDPrefixBufs have sufficient capacity (32 bytes) to // avoid allocating when constructing Raft log keys (16 bytes). unsafeKey := prefixBuf.RaftLogKey(idx) if err := readWriter.ClearUnversioned(unsafeKey); err != nil { - return false, errors.Wrapf(err, "unable to clear truncated Raft entries for %+v", newTruncatedState) + return false, errors.Wrapf(err, "unable to clear truncated Raft entries for %+v at index %d", + suggestedTruncatedState, idx) } } - if !truncStateIsLegacy { - if truncStatePostApply.Index < newTruncatedState.Index { - // There are two cases here (though handled just the same). In the - // first case, the Raft command has just deleted the legacy - // replicated truncated state key as part of the migration (so - // truncStateIsLegacy is now false for the first time and - // truncStatePostApply is zero) and we need to atomically write the - // new, unreplicated, key. Or we've already migrated earlier, in - // which case truncStatePostApply equals the current value of the - // new key (which wasn't touched by the batch), and we need to - // overwrite it if this truncation "moves it forward". - - if err := storage.MVCCPutProto( - ctx, readWriter, nil /* ms */, prefixBuf.RaftTruncatedStateKey(), - hlc.Timestamp{}, nil /* txn */, newTruncatedState, - ); err != nil { - return false, errors.Wrap(err, "unable to migrate RaftTruncatedState") - } - // Have migrated and this new truncated state is moving us forward. - // Tell caller that we applied it and that so should they. - return true, nil - } - // Have migrated, but this truncated state moves the existing one - // backwards, so instruct caller to not update in-memory state. + if suggestedTruncatedState.Index <= currentTruncatedState.Index { + // The suggested truncated state moves us backwards; instruct the + // caller to not update the in-memory state. return false, nil } - // Haven't migrated yet, don't ever discard the update. + + // The suggested truncated state moves us forward; apply it and tell + // the caller as much. + if err := storage.MVCCPutProto( + ctx, readWriter, nil /* ms */, prefixBuf.RaftTruncatedStateKey(), + hlc.Timestamp{}, nil /* txn */, suggestedTruncatedState, + ); err != nil { + return false, errors.Wrap(err, "unable to write RaftTruncatedState") + } + return true, nil } diff --git a/pkg/kv/kvserver/replica_raft_truncation_test.go b/pkg/kv/kvserver/replica_raft_truncation_test.go index 4867384b1c10..ba25718fe9d0 100644 --- a/pkg/kv/kvserver/replica_raft_truncation_test.go +++ b/pkg/kv/kvserver/replica_raft_truncation_test.go @@ -25,6 +25,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/datadriven" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestHandleTruncatedStateBelowRaft(t *testing.T) { @@ -32,23 +33,16 @@ func TestHandleTruncatedStateBelowRaft(t *testing.T) { defer log.Scope(t).Close(t) // This test verifies the expected behavior of the downstream-of-Raft log - // truncation code, in particular regarding the - // VersionUnreplicatedRaftTruncatedState migration. + // truncation code. ctx := context.Background() - - // neither exists (migration) - // old one exists (no migration) - // new one exists (migrated already) - // truncstate regresses - - var prevTruncatedState roachpb.RaftTruncatedState - datadriven.Walk(t, "testdata/truncated_state_migration", func(t *testing.T, path string) { + datadriven.Walk(t, "testdata/truncated_state", func(t *testing.T, path string) { const rangeID = 12 loader := stateloader.Make(rangeID) eng := storage.NewDefaultInMemForTesting() defer eng.Close() + var prevTruncatedState roachpb.RaftTruncatedState datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { switch d.Cmd { case "prev": @@ -58,21 +52,15 @@ func TestHandleTruncatedStateBelowRaft(t *testing.T) { case "put": var index uint64 var term uint64 - var legacy bool d.ScanArgs(t, "index", &index) d.ScanArgs(t, "term", &term) - d.ScanArgs(t, "legacy", &legacy) truncState := &roachpb.RaftTruncatedState{ Index: index, Term: term, } - if legacy { - assert.NoError(t, loader.SetLegacyRaftTruncatedState(ctx, eng, nil, truncState)) - } else { - assert.NoError(t, loader.SetRaftTruncatedState(ctx, eng, truncState)) - } + assert.NoError(t, loader.SetRaftTruncatedState(ctx, eng, truncState)) return "" case "handle": var buf bytes.Buffer @@ -82,31 +70,28 @@ func TestHandleTruncatedStateBelowRaft(t *testing.T) { d.ScanArgs(t, "index", &index) d.ScanArgs(t, "term", &term) - newTruncatedState := &roachpb.RaftTruncatedState{ + suggestedTruncatedState := &roachpb.RaftTruncatedState{ Index: index, Term: term, } - apply, err := handleTruncatedStateBelowRaft(ctx, &prevTruncatedState, newTruncatedState, loader, eng, false) + currentTruncatedState, err := loader.LoadRaftTruncatedState(ctx, eng) + assert.NoError(t, err) + apply, err := handleTruncatedStateBelowRaftPreApply(ctx, ¤tTruncatedState, suggestedTruncatedState, loader, eng) if err != nil { return err.Error() } + fmt.Fprintf(&buf, "apply: %t\n", apply) - for _, key := range []roachpb.Key{ - keys.RaftTruncatedStateLegacyKey(rangeID), - keys.RaftTruncatedStateKey(rangeID), - } { - var truncatedState roachpb.RaftTruncatedState - ok, err := storage.MVCCGetProto(ctx, eng, key, hlc.Timestamp{}, &truncatedState, storage.MVCCGetOptions{}) - if err != nil { - t.Fatal(err) - } - if !ok { - continue - } - fmt.Fprintf(&buf, "%s -> index=%d term=%d\n", key, truncatedState.Index, truncatedState.Term) + key := keys.RaftTruncatedStateKey(rangeID) + var truncatedState roachpb.RaftTruncatedState + ok, err := storage.MVCCGetProto(ctx, eng, key, hlc.Timestamp{}, &truncatedState, storage.MVCCGetOptions{}) + if err != nil { + t.Fatal(err) } + require.True(t, ok) + fmt.Fprintf(&buf, "%s -> index=%d term=%d\n", key, truncatedState.Index, truncatedState.Term) return buf.String() default: } diff --git a/pkg/kv/kvserver/replica_raftstorage.go b/pkg/kv/kvserver/replica_raftstorage.go index 226c54c261f1..8587db094378 100644 --- a/pkg/kv/kvserver/replica_raftstorage.go +++ b/pkg/kv/kvserver/replica_raftstorage.go @@ -209,7 +209,7 @@ func entries( } // No results, was it due to unavailability or truncation? - ts, _, err := rsl.LoadRaftTruncatedState(ctx, reader) + ts, err := rsl.LoadRaftTruncatedState(ctx, reader) if err != nil { return nil, err } @@ -305,7 +305,7 @@ func term( // sideloaded entries. We only need the term, so this is what we do. ents, err := entries(ctx, rsl, reader, rangeID, eCache, nil /* sideloaded */, i, i+1, math.MaxUint64 /* maxBytes */) if errors.Is(err, raft.ErrCompacted) { - ts, _, err := rsl.LoadRaftTruncatedState(ctx, reader) + ts, err := rsl.LoadRaftTruncatedState(ctx, reader) if err != nil { return 0, err } @@ -342,7 +342,7 @@ func (r *Replica) raftTruncatedStateLocked( if r.mu.state.TruncatedState != nil { return *r.mu.state.TruncatedState, nil } - ts, _, err := r.mu.stateLoader.LoadRaftTruncatedState(ctx, r.store.Engine()) + ts, err := r.mu.stateLoader.LoadRaftTruncatedState(ctx, r.store.Engine()) if err != nil { return ts, err } @@ -519,18 +519,9 @@ type IncomingSnapshot struct { // The Raft log entries for this snapshot. LogEntries [][]byte // The replica state at the time the snapshot was generated (never nil). - State *kvserverpb.ReplicaState - // - // When true, this snapshot contains an unreplicated TruncatedState. When - // false, the TruncatedState is replicated (see the reference below) and the - // recipient must avoid also writing the unreplicated TruncatedState. The - // migration to an unreplicated TruncatedState will be carried out during - // the next log truncation (assuming cluster version is bumped at that - // point). - // See the comment on VersionUnreplicatedRaftTruncatedState for details. - UsesUnreplicatedTruncatedState bool - snapType SnapshotRequest_Type - placeholder *ReplicaPlaceholder + State *kvserverpb.ReplicaState + snapType SnapshotRequest_Type + placeholder *ReplicaPlaceholder } func (s *IncomingSnapshot) String() string { @@ -886,13 +877,10 @@ func (r *Replica) applySnapshot( } r.store.raftEntryCache.Drop(r.RangeID) - // Update TruncatedState if it is unreplicated. - if inSnap.UsesUnreplicatedTruncatedState { - if err := r.raftMu.stateLoader.SetRaftTruncatedState( - ctx, &unreplicatedSST, s.TruncatedState, - ); err != nil { - return errors.Wrapf(err, "unable to write UnreplicatedTruncatedState to unreplicated SST writer") - } + if err := r.raftMu.stateLoader.SetRaftTruncatedState( + ctx, &unreplicatedSST, s.TruncatedState, + ); err != nil { + return errors.Wrapf(err, "unable to write TruncatedState to unreplicated SST writer") } if err := unreplicatedSST.Finish(); err != nil { diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index 8bbb6f72f3d6..278171eb4413 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -614,9 +614,6 @@ func TestReplicaContains(t *testing.T) { r.mu.state.Desc = desc r.rangeStr.store(0, desc) - if statsKey := keys.RangeStatsLegacyKey(desc.RangeID); !r.ContainsKey(statsKey) { - t.Errorf("expected range to contain range stats key %q", statsKey) - } if !r.ContainsKey(roachpb.Key("aa")) { t.Errorf("expected range to contain key \"aa\"") } diff --git a/pkg/kv/kvserver/stateloader/initial.go b/pkg/kv/kvserver/stateloader/initial.go index 65177fea2c69..f1688147a310 100644 --- a/pkg/kv/kvserver/stateloader/initial.go +++ b/pkg/kv/kvserver/stateloader/initial.go @@ -45,7 +45,6 @@ func WriteInitialReplicaState( desc roachpb.RangeDescriptor, lease roachpb.Lease, gcThreshold hlc.Timestamp, - truncStateType TruncatedStateType, replicaVersion roachpb.Version, ) (enginepb.MVCCStats, error) { rsl := Make(desc.RangeID) @@ -64,9 +63,6 @@ func WriteInitialReplicaState( if (replicaVersion != roachpb.Version{}) { s.Version = &replicaVersion } - if truncStateType != TruncatedStateLegacyReplicatedAndNoAppliedKey { - s.UsingAppliedStateKey = true - } if existingLease, err := rsl.LoadLease(ctx, readWriter); err != nil { return enginepb.MVCCStats{}, errors.Wrap(err, "error reading lease") @@ -86,7 +82,7 @@ func WriteInitialReplicaState( log.Fatalf(ctx, "expected trivial version, but found %+v", existingVersion) } - newMS, err := rsl.Save(ctx, readWriter, s, truncStateType) + newMS, err := rsl.Save(ctx, readWriter, s) if err != nil { return enginepb.MVCCStats{}, err } @@ -101,32 +97,14 @@ func WriteInitialRangeState( readWriter storage.ReadWriter, desc roachpb.RangeDescriptor, replicaVersion roachpb.Version, -) error { - const initialTruncStateType = TruncatedStateUnreplicated - return WriteInitialRangeStateWithTruncatedState(ctx, readWriter, desc, replicaVersion, initialTruncStateType) -} - -// WriteInitialRangeStateWithTruncatedState is the same as -// WriteInitialRangeState, but allows the caller to override the truncated state -// type. -// -// TODO(irfansharif): This can be removed in the v21.2 cycle after we no longer -// need to test the truncated state migration. -func WriteInitialRangeStateWithTruncatedState( - ctx context.Context, - readWriter storage.ReadWriter, - desc roachpb.RangeDescriptor, - replicaVersion roachpb.Version, - truncState TruncatedStateType, ) error { initialLease := roachpb.Lease{} initialGCThreshold := hlc.Timestamp{} initialMS := enginepb.MVCCStats{} - initialTruncStateType := truncState if _, err := WriteInitialReplicaState( ctx, readWriter, initialMS, desc, initialLease, initialGCThreshold, - initialTruncStateType, replicaVersion, + replicaVersion, ); err != nil { return err } diff --git a/pkg/kv/kvserver/stateloader/stateloader.go b/pkg/kv/kvserver/stateloader/stateloader.go index d296219d0697..2ab3273f5320 100644 --- a/pkg/kv/kvserver/stateloader/stateloader.go +++ b/pkg/kv/kvserver/stateloader/stateloader.go @@ -73,34 +73,21 @@ func (rsl StateLoader) Load( return kvserverpb.ReplicaState{}, err } - if as, err := rsl.LoadRangeAppliedState(ctx, reader); err != nil { + as, err := rsl.LoadRangeAppliedState(ctx, reader) + if err != nil { return kvserverpb.ReplicaState{}, err - } else if as != nil { - s.UsingAppliedStateKey = true - - s.RaftAppliedIndex = as.RaftAppliedIndex - s.LeaseAppliedIndex = as.LeaseAppliedIndex - - ms := as.RangeStats.ToStats() - s.Stats = &ms - if as.RaftClosedTimestamp != nil { - s.RaftClosedTimestamp = *as.RaftClosedTimestamp - } - } else { - if s.RaftAppliedIndex, s.LeaseAppliedIndex, err = rsl.LoadAppliedIndex(ctx, reader); err != nil { - return kvserverpb.ReplicaState{}, err - } - - ms, err := rsl.LoadMVCCStats(ctx, reader) - if err != nil { - return kvserverpb.ReplicaState{}, err - } - s.Stats = &ms + } + s.RaftAppliedIndex = as.RaftAppliedIndex + s.LeaseAppliedIndex = as.LeaseAppliedIndex + ms := as.RangeStats.ToStats() + s.Stats = &ms + if as.RaftClosedTimestamp != nil { + s.RaftClosedTimestamp = *as.RaftClosedTimestamp } // The truncated state should not be optional (i.e. the pointer is // pointless), but it is and the migration is not worth it. - truncState, _, err := rsl.LoadRaftTruncatedState(ctx, reader) + truncState, err := rsl.LoadRaftTruncatedState(ctx, reader) if err != nil { return kvserverpb.ReplicaState{}, err } @@ -117,20 +104,6 @@ func (rsl StateLoader) Load( return s, nil } -// TruncatedStateType determines whether to use a replicated (legacy) or an -// unreplicated TruncatedState. See VersionUnreplicatedRaftTruncatedStateKey. -type TruncatedStateType int - -const ( - // TruncatedStateLegacyReplicated means use the legacy (replicated) key. - TruncatedStateLegacyReplicated TruncatedStateType = iota - // TruncatedStateLegacyReplicatedAndNoAppliedKey means use the legacy key - // and also don't use the RangeAppliedKey. This is for testing use only. - TruncatedStateLegacyReplicatedAndNoAppliedKey - // TruncatedStateUnreplicated means use the new (unreplicated) key. - TruncatedStateUnreplicated -) - // Save persists the given ReplicaState to disk. It assumes that the contained // Stats are up-to-date and returns the stats which result from writing the // updated State. @@ -143,10 +116,7 @@ const ( // missing whenever save is called. Optional values should be reserved // strictly for use in Result. Do before merge. func (rsl StateLoader) Save( - ctx context.Context, - readWriter storage.ReadWriter, - state kvserverpb.ReplicaState, - truncStateType TruncatedStateType, + ctx context.Context, readWriter storage.ReadWriter, state kvserverpb.ReplicaState, ) (enginepb.MVCCStats, error) { ms := state.Stats if err := rsl.SetLease(ctx, readWriter, ms, *state.Lease); err != nil { @@ -155,34 +125,17 @@ func (rsl StateLoader) Save( if err := rsl.SetGCThreshold(ctx, readWriter, ms, state.GCThreshold); err != nil { return enginepb.MVCCStats{}, err } - if truncStateType != TruncatedStateUnreplicated { - if err := rsl.SetLegacyRaftTruncatedState(ctx, readWriter, ms, state.TruncatedState); err != nil { - return enginepb.MVCCStats{}, err - } - } else { - if err := rsl.SetRaftTruncatedState(ctx, readWriter, state.TruncatedState); err != nil { - return enginepb.MVCCStats{}, err - } + if err := rsl.SetRaftTruncatedState(ctx, readWriter, state.TruncatedState); err != nil { + return enginepb.MVCCStats{}, err } if state.Version != nil { if err := rsl.SetVersion(ctx, readWriter, ms, state.Version); err != nil { return enginepb.MVCCStats{}, err } } - if state.UsingAppliedStateKey { - rai, lai, ct := state.RaftAppliedIndex, state.LeaseAppliedIndex, &state.RaftClosedTimestamp - if err := rsl.SetRangeAppliedState(ctx, readWriter, rai, lai, ms, ct); err != nil { - return enginepb.MVCCStats{}, err - } - } else { - if err := rsl.SetLegacyAppliedIndex( - ctx, readWriter, ms, state.RaftAppliedIndex, state.LeaseAppliedIndex, - ); err != nil { - return enginepb.MVCCStats{}, err - } - if err := rsl.SetLegacyMVCCStats(ctx, readWriter, ms); err != nil { - return enginepb.MVCCStats{}, err - } + rai, lai, ct := state.RaftAppliedIndex, state.LeaseAppliedIndex, &state.RaftClosedTimestamp + if err := rsl.SetRangeAppliedState(ctx, readWriter, rai, lai, ms, ct); err != nil { + return enginepb.MVCCStats{}, err } return *ms, nil } @@ -205,70 +158,14 @@ func (rsl StateLoader) SetLease( hlc.Timestamp{}, nil, &lease) } -// LoadRangeAppliedState loads the Range applied state. The returned pointer -// will be nil if the applied state key is not found. +// LoadRangeAppliedState loads the Range applied state. func (rsl StateLoader) LoadRangeAppliedState( ctx context.Context, reader storage.Reader, -) (*enginepb.RangeAppliedState, error) { +) (enginepb.RangeAppliedState, error) { var as enginepb.RangeAppliedState - found, err := storage.MVCCGetProto(ctx, reader, rsl.RangeAppliedStateKey(), hlc.Timestamp{}, &as, + _, err := storage.MVCCGetProto(ctx, reader, rsl.RangeAppliedStateKey(), hlc.Timestamp{}, &as, storage.MVCCGetOptions{}) - if !found { - return nil, err - } - return &as, err -} - -// AssertNoRangeAppliedState asserts that no Range applied state key is present. -func (rsl StateLoader) AssertNoRangeAppliedState(ctx context.Context, reader storage.Reader) error { - if as, err := rsl.LoadRangeAppliedState(ctx, reader); err != nil { - return err - } else if as != nil { - log.Fatalf(ctx, "unexpected RangeAppliedState present: %v", as) - } - return nil -} - -// LoadAppliedIndex returns the Raft applied index and the lease applied index. -func (rsl StateLoader) LoadAppliedIndex( - ctx context.Context, reader storage.Reader, -) (raftAppliedIndex uint64, leaseAppliedIndex uint64, err error) { - // Check the applied state key. - if as, err := rsl.LoadRangeAppliedState(ctx, reader); err != nil { - return 0, 0, err - } else if as != nil { - return as.RaftAppliedIndex, as.LeaseAppliedIndex, nil - } - - // If the range applied state is not found, check the legacy Raft applied - // index and the lease applied index keys. This is where these indices were - // stored before the range applied state was introduced. - v, _, err := storage.MVCCGet(ctx, reader, rsl.RaftAppliedIndexLegacyKey(), - hlc.Timestamp{}, storage.MVCCGetOptions{}) - if err != nil { - return 0, 0, err - } - if v != nil { - int64AppliedIndex, err := v.GetInt() - if err != nil { - return 0, 0, err - } - raftAppliedIndex = uint64(int64AppliedIndex) - } - // TODO(tschottdorf): code duplication. - v, _, err = storage.MVCCGet(ctx, reader, rsl.LeaseAppliedIndexLegacyKey(), - hlc.Timestamp{}, storage.MVCCGetOptions{}) - if err != nil { - return 0, 0, err - } - if v != nil { - int64LeaseAppliedIndex, err := v.GetInt() - if err != nil { - return 0, 0, err - } - leaseAppliedIndex = uint64(int64LeaseAppliedIndex) - } - return raftAppliedIndex, leaseAppliedIndex, nil + return as, err } // LoadMVCCStats loads the MVCC stats. @@ -276,19 +173,11 @@ func (rsl StateLoader) LoadMVCCStats( ctx context.Context, reader storage.Reader, ) (enginepb.MVCCStats, error) { // Check the applied state key. - if as, err := rsl.LoadRangeAppliedState(ctx, reader); err != nil { + as, err := rsl.LoadRangeAppliedState(ctx, reader) + if err != nil { return enginepb.MVCCStats{}, err - } else if as != nil { - return as.RangeStats.ToStats(), nil } - - // If the range applied state is not found, check the legacy stats - // key. This is where stats were stored before the range applied - // state was introduced. - var ms enginepb.MVCCStats - _, err := storage.MVCCGetProto(ctx, reader, rsl.RangeStatsLegacyKey(), hlc.Timestamp{}, &ms, - storage.MVCCGetOptions{}) - return ms, err + return as.RangeStats.ToStats(), nil } // SetRangeAppliedState overwrites the range applied state. This state is a @@ -323,177 +212,18 @@ func (rsl StateLoader) SetRangeAppliedState( return storage.MVCCPutProto(ctx, readWriter, ms, rsl.RangeAppliedStateKey(), hlc.Timestamp{}, nil, &as) } -// MigrateToRangeAppliedStateKey deletes the keys that were replaced by the -// RangeAppliedState key. -func (rsl StateLoader) MigrateToRangeAppliedStateKey( - ctx context.Context, readWriter storage.ReadWriter, ms *enginepb.MVCCStats, -) error { - noTS := hlc.Timestamp{} - if err := storage.MVCCDelete(ctx, readWriter, ms, rsl.RaftAppliedIndexLegacyKey(), noTS, nil); err != nil { - return err - } - if err := storage.MVCCDelete(ctx, readWriter, ms, rsl.LeaseAppliedIndexLegacyKey(), noTS, nil); err != nil { - return err - } - return storage.MVCCDelete(ctx, readWriter, ms, rsl.RangeStatsLegacyKey(), noTS, nil) -} - -// SetLegacyAppliedIndex sets the legacy {raft,lease} applied index values, -// properly accounting for existing keys in the returned stats. -// -// The range applied state key cannot already exist or an assetion will be -// triggered. See comment on SetRangeAppliedState for why this is "legacy". -func (rsl StateLoader) SetLegacyAppliedIndex( - ctx context.Context, - readWriter storage.ReadWriter, - ms *enginepb.MVCCStats, - appliedIndex, leaseAppliedIndex uint64, -) error { - if err := rsl.AssertNoRangeAppliedState(ctx, readWriter); err != nil { - return err - } - - var value roachpb.Value - value.SetInt(int64(appliedIndex)) - if err := storage.MVCCPut(ctx, readWriter, ms, - rsl.RaftAppliedIndexLegacyKey(), - hlc.Timestamp{}, - value, - nil /* txn */); err != nil { - return err - } - value.SetInt(int64(leaseAppliedIndex)) - return storage.MVCCPut(ctx, readWriter, ms, - rsl.LeaseAppliedIndexLegacyKey(), - hlc.Timestamp{}, - value, - nil /* txn */) -} - -// SetLegacyAppliedIndexBlind sets the legacy {raft,lease} applied index values -// using a "blind" put which ignores any existing keys. This is identical to -// SetLegacyAppliedIndex but is used to optimize the writing of the applied -// index values during write operations where we definitively know the size of -// the previous values. -// -// The range applied state key cannot already exist or an assetion will be -// triggered. See comment on SetRangeAppliedState for why this is "legacy". -func (rsl StateLoader) SetLegacyAppliedIndexBlind( - ctx context.Context, - readWriter storage.ReadWriter, - ms *enginepb.MVCCStats, - appliedIndex, leaseAppliedIndex uint64, -) error { - if err := rsl.AssertNoRangeAppliedState(ctx, readWriter); err != nil { - return err - } - - var value roachpb.Value - value.SetInt(int64(appliedIndex)) - if err := storage.MVCCBlindPut(ctx, readWriter, ms, - rsl.RaftAppliedIndexLegacyKey(), - hlc.Timestamp{}, - value, - nil /* txn */); err != nil { - return err - } - value.SetInt(int64(leaseAppliedIndex)) - return storage.MVCCBlindPut(ctx, readWriter, ms, - rsl.LeaseAppliedIndexLegacyKey(), - hlc.Timestamp{}, - value, - nil /* txn */) -} - -func inlineValueIntEncodedSize(v int64) int { - var value roachpb.Value - value.SetInt(v) - meta := enginepb.MVCCMetadata{RawBytes: value.RawBytes} - return meta.Size() -} - -// CalcAppliedIndexSysBytes calculates the size (MVCCStats.SysBytes) of the {raft,lease} applied -// index keys/values. -func (rsl StateLoader) CalcAppliedIndexSysBytes(appliedIndex, leaseAppliedIndex uint64) int64 { - return int64(storage.MakeMVCCMetadataKey(rsl.RaftAppliedIndexLegacyKey()).EncodedSize() + - storage.MakeMVCCMetadataKey(rsl.LeaseAppliedIndexLegacyKey()).EncodedSize() + - inlineValueIntEncodedSize(int64(appliedIndex)) + - inlineValueIntEncodedSize(int64(leaseAppliedIndex))) -} - -func (rsl StateLoader) writeLegacyMVCCStatsInternal( - ctx context.Context, readWriter storage.ReadWriter, newMS *enginepb.MVCCStats, -) error { - // We added a new field to the MVCCStats struct to track abort span bytes, which - // enlarges the size of the struct itself. This is mostly fine - we persist - // MVCCStats under the RangeAppliedState key and don't account for the size of - // the MVCCStats struct itself when doing so (we ignore the RangeAppliedState key - // in ComputeStatsForRange). This would not therefore not cause replica state divergence - // in mixed version clusters (the enlarged struct does not contribute to a - // persisted stats difference on disk because we're not accounting for the size of - // the struct itself). - - // That's all fine and good except for the fact that historically we persisted - // MVCCStats under a dedicated RangeStatsLegacyKey (as we're doing so here), and - // in this key we also accounted for the size of the MVCCStats object itself - // (which made it super cumbersome to update the schema of the MVCCStats object, - // and we basically never did). - - // Now, in order to add this extra field to the MVCCStats object, we need to be - // careful with what we write to the RangeStatsLegacyKey. We can't write this new - // version of MVCCStats, as it is going to account for it's now (enlarged) size - // and persist a value for sysbytes different from other replicas that are unaware - // of this new representation (as would be the case in mixed-version settings). To - // this end we've constructed a copy of the legacy MVCCStats representation and - // are careful to persist only that (and sidestepping any inconsistencies due to - // the differing MVCCStats schema). - legacyMS := enginepb.MVCCStatsLegacyRepresentation{ - ContainsEstimates: newMS.ContainsEstimates, - LastUpdateNanos: newMS.LastUpdateNanos, - IntentAge: newMS.IntentAge, - GCBytesAge: newMS.GCBytesAge, - LiveBytes: newMS.LiveBytes, - LiveCount: newMS.LiveCount, - KeyBytes: newMS.KeyBytes, - KeyCount: newMS.KeyCount, - ValBytes: newMS.ValBytes, - ValCount: newMS.ValCount, - IntentBytes: newMS.IntentBytes, - IntentCount: newMS.IntentCount, - SysBytes: newMS.SysBytes, - SysCount: newMS.SysCount, - } - return storage.MVCCPutProto(ctx, readWriter, nil, rsl.RangeStatsLegacyKey(), hlc.Timestamp{}, nil, &legacyMS) -} - -// SetLegacyMVCCStats overwrites the legacy MVCC stats key. -// -// The range applied state key cannot already exist or an assertion will be -// triggered. See comment on SetRangeAppliedState for why this is "legacy". -func (rsl StateLoader) SetLegacyMVCCStats( - ctx context.Context, readWriter storage.ReadWriter, newMS *enginepb.MVCCStats, -) error { - if err := rsl.AssertNoRangeAppliedState(ctx, readWriter); err != nil { - return err - } - - return rsl.writeLegacyMVCCStatsInternal(ctx, readWriter, newMS) -} - // SetMVCCStats overwrites the MVCC stats. This needs to perform a read on the // RangeAppliedState key before overwriting the stats. Use SetRangeAppliedState // when performance is important. func (rsl StateLoader) SetMVCCStats( ctx context.Context, readWriter storage.ReadWriter, newMS *enginepb.MVCCStats, ) error { - if as, err := rsl.LoadRangeAppliedState(ctx, readWriter); err != nil { + as, err := rsl.LoadRangeAppliedState(ctx, readWriter) + if err != nil { return err - } else if as != nil { - return rsl.SetRangeAppliedState( - ctx, readWriter, as.RaftAppliedIndex, as.LeaseAppliedIndex, newMS, as.RaftClosedTimestamp) } - - return rsl.writeLegacyMVCCStatsInternal(ctx, readWriter, newMS) + return rsl.SetRangeAppliedState( + ctx, readWriter, as.RaftAppliedIndex, as.LeaseAppliedIndex, newMS, as.RaftClosedTimestamp) } // SetClosedTimestamp overwrites the closed timestamp. @@ -509,20 +239,6 @@ func (rsl StateLoader) SetClosedTimestamp( as.RangeStats.ToStatsPtr(), closedTS) } -// SetLegacyRaftTruncatedState overwrites the truncated state. -func (rsl StateLoader) SetLegacyRaftTruncatedState( - ctx context.Context, - readWriter storage.ReadWriter, - ms *enginepb.MVCCStats, - truncState *roachpb.RaftTruncatedState, -) error { - if (*truncState == roachpb.RaftTruncatedState{}) { - return errors.New("cannot persist empty RaftTruncatedState") - } - return storage.MVCCPutProto(ctx, readWriter, ms, - rsl.RaftTruncatedStateLegacyKey(), hlc.Timestamp{}, nil, truncState) -} - // LoadGCThreshold loads the GC threshold. func (rsl StateLoader) LoadGCThreshold( ctx context.Context, reader storage.Reader, @@ -591,7 +307,7 @@ func (rsl StateLoader) LoadLastIndex(ctx context.Context, reader storage.Reader) if lastIndex == 0 { // The log is empty, which means we are either starting from scratch // or the entire log has been truncated away. - lastEnt, _, err := rsl.LoadRaftTruncatedState(ctx, reader) + lastEnt, err := rsl.LoadRaftTruncatedState(ctx, reader) if err != nil { return 0, err } @@ -600,35 +316,17 @@ func (rsl StateLoader) LoadLastIndex(ctx context.Context, reader storage.Reader) return lastIndex, nil } -// LoadRaftTruncatedState loads the truncated state. The returned boolean returns -// whether the result was read from the TruncatedStateLegacyKey. If both keys -// are missing, it is false which is used to migrate into the unreplicated key. -// -// See VersionUnreplicatedRaftTruncatedState. +// LoadRaftTruncatedState loads the truncated state. func (rsl StateLoader) LoadRaftTruncatedState( ctx context.Context, reader storage.Reader, -) (_ roachpb.RaftTruncatedState, isLegacy bool, _ error) { +) (roachpb.RaftTruncatedState, error) { var truncState roachpb.RaftTruncatedState - if found, err := storage.MVCCGetProto( + if _, err := storage.MVCCGetProto( ctx, reader, rsl.RaftTruncatedStateKey(), hlc.Timestamp{}, &truncState, storage.MVCCGetOptions{}, ); err != nil { - return roachpb.RaftTruncatedState{}, false, err - } else if found { - return truncState, false, nil + return roachpb.RaftTruncatedState{}, err } - - // If the "new" truncated state isn't there (yet), fall back to the legacy - // truncated state. The next log truncation will atomically rewrite them - // assuming the cluster version has advanced sufficiently. - // - // See VersionUnreplicatedRaftTruncatedState. - legacyFound, err := storage.MVCCGetProto( - ctx, reader, rsl.RaftTruncatedStateLegacyKey(), hlc.Timestamp{}, &truncState, storage.MVCCGetOptions{}, - ) - if err != nil { - return roachpb.RaftTruncatedState{}, false, err - } - return truncState, legacyFound, nil + return truncState, nil } // SetRaftTruncatedState overwrites the truncated state. @@ -691,15 +389,15 @@ func (rsl StateLoader) SynthesizeRaftState( if err != nil { return err } - truncState, _, err := rsl.LoadRaftTruncatedState(ctx, readWriter) + truncState, err := rsl.LoadRaftTruncatedState(ctx, readWriter) if err != nil { return err } - raftAppliedIndex, _, err := rsl.LoadAppliedIndex(ctx, readWriter) + as, err := rsl.LoadRangeAppliedState(ctx, readWriter) if err != nil { return err } - return rsl.SynthesizeHardState(ctx, readWriter, hs, truncState, raftAppliedIndex) + return rsl.SynthesizeHardState(ctx, readWriter, hs, truncState, as.RaftAppliedIndex) } // SynthesizeHardState synthesizes an on-disk HardState from the given input, diff --git a/pkg/kv/kvserver/store_init.go b/pkg/kv/kvserver/store_init.go index f1617daa0cd5..33aac9a89072 100644 --- a/pkg/kv/kvserver/store_init.go +++ b/pkg/kv/kvserver/store_init.go @@ -244,16 +244,8 @@ func WriteInitialClusterData( } } - if tt := knobs.TruncatedStateTypeOverride; tt != nil { - if err := stateloader.WriteInitialRangeStateWithTruncatedState( - ctx, batch, *desc, initialReplicaVersion, *tt, - ); err != nil { - return err - } - } else { - if err := stateloader.WriteInitialRangeState(ctx, batch, *desc, initialReplicaVersion); err != nil { - return err - } + if err := stateloader.WriteInitialRangeState(ctx, batch, *desc, initialReplicaVersion); err != nil { + return err } computedStats, err := rditer.ComputeStatsForRange(desc, batch, now.WallTime) if err != nil { diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 7d4752e81210..68af0e53c8ef 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -296,12 +296,11 @@ func (kvSS *kvBatchSnapshotStrategy) Receive( } inSnap := IncomingSnapshot{ - UsesUnreplicatedTruncatedState: header.UnreplicatedTruncatedState, - SnapUUID: snapUUID, - SSTStorageScratch: kvSS.scratch, - LogEntries: logEntries, - State: &header.State, - snapType: header.Type, + SnapUUID: snapUUID, + SSTStorageScratch: kvSS.scratch, + LogEntries: logEntries, + State: &header.State, + snapType: header.Type, } expLen := inSnap.State.RaftAppliedIndex - inSnap.State.TruncatedState.Index @@ -930,7 +929,6 @@ func SendEmptySnapshot( desc, roachpb.Lease{}, hlc.Timestamp{}, // gcThreshold - stateloader.TruncatedStateUnreplicated, st.Version.ActiveVersionOrEmpty(ctx).Version, ) if err != nil { @@ -994,14 +992,14 @@ func SendEmptySnapshot( } header := SnapshotRequest_Header{ - State: state, - RaftMessageRequest: req, - RangeSize: ms.Total(), - CanDecline: false, - Priority: SnapshotRequest_RECOVERY, - Strategy: SnapshotRequest_KV_BATCH, - Type: SnapshotRequest_VIA_SNAPSHOT_QUEUE, - UnreplicatedTruncatedState: true, + State: state, + RaftMessageRequest: req, + RangeSize: ms.Total(), + CanDecline: false, + Priority: SnapshotRequest_RECOVERY, + Strategy: SnapshotRequest_KV_BATCH, + Type: SnapshotRequest_VIA_SNAPSHOT_QUEUE, + DeprecatedUnreplicatedTruncatedState: true, } stream, err := NewMultiRaftClient(cc).RaftSnapshot(ctx) diff --git a/pkg/kv/kvserver/store_test.go b/pkg/kv/kvserver/store_test.go index 330599e08987..6e763a990899 100644 --- a/pkg/kv/kvserver/store_test.go +++ b/pkg/kv/kvserver/store_test.go @@ -313,8 +313,7 @@ func TestIterateIDPrefixKeys(t *testing.T) { rng := rand.New(rand.NewSource(seed)) ops := []func(rangeID roachpb.RangeID) roachpb.Key{ - keys.RaftAppliedIndexLegacyKey, // replicated; sorts before tombstone - keys.RaftHardStateKey, // unreplicated; sorts after tombstone + keys.RaftHardStateKey, // unreplicated; sorts after tombstone // Replicated key-anchored local key (i.e. not one we should care about). // Will be written at zero timestamp, but that's ok. func(rangeID roachpb.RangeID) roachpb.Key { diff --git a/pkg/kv/kvserver/testdata/truncated_state_migration/migration b/pkg/kv/kvserver/testdata/truncated_state/truncated_state similarity index 100% rename from pkg/kv/kvserver/testdata/truncated_state_migration/migration rename to pkg/kv/kvserver/testdata/truncated_state/truncated_state diff --git a/pkg/kv/kvserver/testdata/truncated_state_migration/pre_migration b/pkg/kv/kvserver/testdata/truncated_state_migration/pre_migration deleted file mode 100644 index e84177bd0b51..000000000000 --- a/pkg/kv/kvserver/testdata/truncated_state_migration/pre_migration +++ /dev/null @@ -1,26 +0,0 @@ -# Mode of operation below VersionUnreplicatedRaftTruncatedState. -# We don't mess with the on-disk state nor do we ever drop updates. - -prev index=100 term=9 ----- - -put legacy=true index=100 term=9 ----- - -handle index=100 term=9 ----- -apply: true -/Local/RangeID/12/r/RaftTruncatedState -> index=100 term=9 - -# Note that the below aren't actually possible in practice -# as a divergence won't happen before the migration. - -handle index=150 term=9 ----- -apply: true -/Local/RangeID/12/r/RaftTruncatedState -> index=100 term=9 - -handle index=60 term=9 ----- -apply: true -/Local/RangeID/12/r/RaftTruncatedState -> index=100 term=9 diff --git a/pkg/kv/kvserver/testing_knobs.go b/pkg/kv/kvserver/testing_knobs.go index 7fa2b71e6de3..786b01a86c98 100644 --- a/pkg/kv/kvserver/testing_knobs.go +++ b/pkg/kv/kvserver/testing_knobs.go @@ -15,7 +15,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" - "github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/tenantrate" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/txnwait" "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -328,9 +327,6 @@ type StoreTestingKnobs struct { // PurgeOutdatedReplicasInterceptor intercepts attempts to purge outdated // replicas in the store. PurgeOutdatedReplicasInterceptor func() - // If set, use the given truncated state type when bootstrapping ranges. - // This is used for testing the truncated state migration. - TruncatedStateTypeOverride *stateloader.TruncatedStateType // If set, use the given version as the initial replica version when // bootstrapping ranges. This is used for testing the migration // infrastructure. diff --git a/pkg/migration/migrations/BUILD.bazel b/pkg/migration/migrations/BUILD.bazel index 3e6daf34c343..109418dcc205 100644 --- a/pkg/migration/migrations/BUILD.bazel +++ b/pkg/migration/migrations/BUILD.bazel @@ -18,7 +18,6 @@ go_library( "sql_instances.go", "sql_stats.go", "tenant_usage.go", - "truncated_state.go", "zones.go", ], importpath = "github.com/cockroachdb/cockroach/pkg/migration/migrations", @@ -77,7 +76,6 @@ go_test( "retry_jobs_with_exponential_backoff_external_test.go", "separated_intents_external_test.go", "separated_intents_test.go", - "truncated_state_external_test.go", ], data = glob(["testdata/**"]), embed = [":migrations"], @@ -91,7 +89,6 @@ go_test( "//pkg/kv/kvclient/kvcoord:with-mocks", "//pkg/kv/kvserver", "//pkg/kv/kvserver/intentresolver", - "//pkg/kv/kvserver/stateloader", "//pkg/migration", "//pkg/roachpb:with-mocks", "//pkg/security", diff --git a/pkg/migration/migrations/migrations.go b/pkg/migration/migrations/migrations.go index 2ae2209208b6..19af695acc56 100644 --- a/pkg/migration/migrations/migrations.go +++ b/pkg/migration/migrations/migrations.go @@ -40,16 +40,6 @@ func NoPrecondition(context.Context, clusterversion.ClusterVersion, migration.Te var registry = make(map[clusterversion.ClusterVersion]migration.Migration) var migrations = []migration.Migration{ - migration.NewSystemMigration( - "use unreplicated TruncatedState and RangeAppliedState for all ranges", - toCV(clusterversion.TruncatedAndRangeAppliedStateMigration), - truncatedStateMigration, - ), - migration.NewSystemMigration( - "purge all replicas using the replicated TruncatedState", - toCV(clusterversion.PostTruncatedAndRangeAppliedStateMigration), - postTruncatedStateMigration, - ), migration.NewSystemMigration( "stop using monolithic encryption-at-rest registry for all stores", toCV(clusterversion.RecordsBasedRegistry), diff --git a/pkg/migration/migrations/separated_intents.go b/pkg/migration/migrations/separated_intents.go index 5e99c4796015..26b43f8ffcb1 100644 --- a/pkg/migration/migrations/separated_intents.go +++ b/pkg/migration/migrations/separated_intents.go @@ -54,6 +54,19 @@ const concurrentMigrateLockTableRequests = 4 // the migration. const migrateLockTableRetries = 3 +// defaultPageSize controls how many ranges are paged in by default when +// iterating through all ranges in a cluster during any given migration. We +// pulled this number out of thin air(-ish). Let's consider a cluster with 50k +// ranges, with each range taking ~200ms. We're being somewhat conservative with +// the duration, but in a wide-area cluster with large hops between the manager +// and the replicas, it could be true. Here's how long it'll take for various +// block sizes: +// +// page size of 1 ~ 2h 46m +// page size of 50 ~ 3m 20s +// page size of 200 ~ 50s +const defaultPageSize = 200 + // migrateLockTableRequest represents migration of one slice of the keyspace. As // part of this request, multiple non-transactional requests would need to be // run: a Barrier, a ScanInterleavedIntents, then multiple txn pushes and intent diff --git a/pkg/migration/migrations/truncated_state.go b/pkg/migration/migrations/truncated_state.go deleted file mode 100644 index 6085f049e08d..000000000000 --- a/pkg/migration/migrations/truncated_state.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package migrations - -import ( - "bytes" - "context" - "fmt" - - "github.com/cockroachdb/cockroach/pkg/clusterversion" - "github.com/cockroachdb/cockroach/pkg/jobs" - "github.com/cockroachdb/cockroach/pkg/keys" - "github.com/cockroachdb/cockroach/pkg/migration" - "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/server/serverpb" - "github.com/cockroachdb/cockroach/pkg/util/log" -) - -// defaultPageSize controls how many ranges are paged in by default when -// iterating through all ranges in a cluster during any given migration. We -// pulled this number out of thin air(-ish). Let's consider a cluster with 50k -// ranges, with each range taking ~200ms. We're being somewhat conservative with -// the duration, but in a wide-area cluster with large hops between the manager -// and the replicas, it could be true. Here's how long it'll take for various -// block sizes: -// -// page size of 1 ~ 2h 46m -// page size of 50 ~ 3m 20s -// page size of 200 ~ 50s -const defaultPageSize = 200 - -func truncatedStateMigration( - ctx context.Context, cv clusterversion.ClusterVersion, deps migration.SystemDeps, _ *jobs.Job, -) error { - var batchIdx, numMigratedRanges int - init := func() { batchIdx, numMigratedRanges = 1, 0 } - if err := deps.Cluster.IterateRangeDescriptors(ctx, defaultPageSize, init, func(descriptors ...roachpb.RangeDescriptor) error { - for _, desc := range descriptors { - // NB: This is a bit of a wart. We want to reach the first range, - // but we can't address the (local) StartKey. However, keys.LocalMax - // is on r1, so we'll just use that instead to target r1. - start, end := desc.StartKey, desc.EndKey - if bytes.Compare(desc.StartKey, keys.LocalMax) < 0 { - start, _ = keys.Addr(keys.LocalMax) - } - if err := deps.DB.Migrate(ctx, start, end, cv.Version); err != nil { - return err - } - } - - // TODO(irfansharif): Instead of logging this to the debug log, we - // should insert these into a `system.migrations` table for external - // observability. - numMigratedRanges += len(descriptors) - log.Infof(ctx, "[batch %d/??] migrated %d ranges", batchIdx, numMigratedRanges) - batchIdx++ - - return nil - }); err != nil { - return err - } - - log.Infof(ctx, "[batch %d/%d] migrated %d ranges", batchIdx, batchIdx, numMigratedRanges) - - // Make sure that all stores have synced. Given we're a below-raft - // migrations, this ensures that the applied state is flushed to disk. - req := &serverpb.SyncAllEnginesRequest{} - op := "flush-stores" - return deps.Cluster.ForEveryNode(ctx, op, func(ctx context.Context, client serverpb.MigrationClient) error { - _, err := client.SyncAllEngines(ctx, req) - return err - }) -} - -func postTruncatedStateMigration( - ctx context.Context, cv clusterversion.ClusterVersion, deps migration.SystemDeps, _ *jobs.Job, -) error { - // Purge all replicas that haven't been migrated to use the unreplicated - // truncated state and the range applied state. - truncStateVersion := clusterversion.ByKey(clusterversion.TruncatedAndRangeAppliedStateMigration) - req := &serverpb.PurgeOutdatedReplicasRequest{Version: &truncStateVersion} - op := fmt.Sprintf("purge-outdated-replicas=%s", req.Version) - return deps.Cluster.ForEveryNode(ctx, op, func(ctx context.Context, client serverpb.MigrationClient) error { - _, err := client.PurgeOutdatedReplicas(ctx, req) - return err - }) -} diff --git a/pkg/migration/migrations/truncated_state_external_test.go b/pkg/migration/migrations/truncated_state_external_test.go deleted file mode 100644 index fee03c429125..000000000000 --- a/pkg/migration/migrations/truncated_state_external_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package migrations_test - -import ( - "context" - "fmt" - "testing" - - "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/clusterversion" - "github.com/cockroachdb/cockroach/pkg/kv/kvserver" - "github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader" - "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/server" - "github.com/cockroachdb/cockroach/pkg/settings/cluster" - "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" - "github.com/cockroachdb/cockroach/pkg/util/leaktest" - "github.com/cockroachdb/errors" - "github.com/stretchr/testify/require" -) - -func TestTruncatedStateMigration(t *testing.T) { - defer leaktest.AfterTest(t)() - ctx := context.Background() - - minVersion := roachpb.Version{Major: 20, Minor: 2} - binaryVersion := clusterversion.ByKey(clusterversion.TruncatedAndRangeAppliedStateMigration + 1) - truncStateVersion := clusterversion.ByKey(clusterversion.TruncatedAndRangeAppliedStateMigration) - bootstrapVersion := clusterversion.ByKey(clusterversion.TruncatedAndRangeAppliedStateMigration - 1) - for _, testCase := range []struct { - name string - typ stateloader.TruncatedStateType - }{ - {"ts=new,as=new", stateloader.TruncatedStateUnreplicated}, - {"ts=legacy,as=new", stateloader.TruncatedStateLegacyReplicated}, - {"ts=legacy,as=legacy", stateloader.TruncatedStateLegacyReplicatedAndNoAppliedKey}, - } { - t.Run(testCase.name, func(t *testing.T) { - makeArgs := func() (args base.TestServerArgs) { - args.Settings = cluster.MakeTestingClusterSettingsWithVersions( - binaryVersion, minVersion, false, - ) - args.Knobs.Store = &kvserver.StoreTestingKnobs{TruncatedStateTypeOverride: &testCase.typ} - args.Knobs.Server = &server.TestingKnobs{ - // TruncatedAndRangeAppliedStateMigration is part of the - // migration that lets us stop using the legacy truncated state. - // When the active cluster version is greater than it, we assert - // against the presence of legacy truncated state and ensure - // we're using the range applied state key. In this test we'll - // start of at the version immediately preceding the migration, - // and migrate past it. - BinaryVersionOverride: bootstrapVersion, - // We want to exercise manual control over the upgrade process. - DisableAutomaticVersionUpgrade: 1, - } - return args - } - - tc := testcluster.StartTestCluster(t, 3, base.TestClusterArgs{ - ServerArgsPerNode: map[int]base.TestServerArgs{ - 0: makeArgs(), - 1: makeArgs(), - 2: makeArgs(), - }, - }) - defer tc.Stopper().Stop(ctx) - - forAllReplicas := func(f func(*kvserver.Replica) error) error { - for i := 0; i < tc.NumServers(); i++ { - err := tc.Server(i).GetStores().(*kvserver.Stores).VisitStores(func(s *kvserver.Store) error { - var err error - s.VisitReplicas(func(repl *kvserver.Replica) bool { - err = f(repl) - return err == nil - }) - return err - }) - if err != nil { - return err - } - } - return nil - } - - getLegacyRanges := func() []string { - t.Helper() - var out []string - require.NoError(t, forAllReplicas(func(repl *kvserver.Replica) error { - sl := stateloader.Make(repl.RangeID) - - _, legacy, err := sl.LoadRaftTruncatedState(ctx, repl.Engine()) - if err != nil { - return err - } - if legacy { - // We're using the legacy truncated state, record ourselves. - out = append(out, fmt.Sprintf("ts(r%d)", repl.RangeID)) - } - - as, err := sl.LoadRangeAppliedState(ctx, repl.Engine()) - if err != nil { - return err - } - if as == nil { - // We're not using the new applied state key, record ourselves. - out = append(out, fmt.Sprintf("as(r%d)", repl.RangeID)) - } - return nil - })) - return out - } - - legacyRanges := getLegacyRanges() - switch testCase.typ { - case stateloader.TruncatedStateUnreplicated: - if len(legacyRanges) != 0 { - t.Fatalf("expected no ranges with legacy keys if bootstrapped with unreplicated truncated state, got: %v", legacyRanges) - } - case stateloader.TruncatedStateLegacyReplicated, stateloader.TruncatedStateLegacyReplicatedAndNoAppliedKey: - if len(legacyRanges) == 0 { - t.Fatalf("expected ranges with legacy keys if bootstrapped with replicated truncated state, got none") - } - } - - // NB: we'll never spot a legacy applied state here. This is - // because that migration is so aggressive that it has already - // happened as part of the initial up-replication. - t.Logf("ranges with legacy keys before migration: %v", legacyRanges) - - _, err := tc.Conns[0].ExecContext( - ctx, `SET CLUSTER SETTING version = $1`, binaryVersion.String(), - ) - require.NoError(t, err) - require.Zero(t, getLegacyRanges()) - - require.NoError(t, forAllReplicas(func(repl *kvserver.Replica) error { - if repl.Version().Less(truncStateVersion) { - return errors.Newf("unexpected version %s", repl.Version()) - } - return nil - })) - }) - } -} diff --git a/pkg/storage/enginepb/mvcc.pb.go b/pkg/storage/enginepb/mvcc.pb.go index 3508802d554f..da306fc2ea25 100644 --- a/pkg/storage/enginepb/mvcc.pb.go +++ b/pkg/storage/enginepb/mvcc.pb.go @@ -324,123 +324,66 @@ func (m *MVCCStats) XXX_DiscardUnknown() { var xxx_messageInfo_MVCCStats proto.InternalMessageInfo -// MVCCStatsLegacyRepresentation is almost identical to MVCCStats, except -// it does not have a field tracking abort span bytes. -// The abort span bytes field in MVCCStats didn't exist in earlier versions, -// and its addition of it to MVCCStats causes the struct to be larger than it was -// previously. The discrepancy between the size of MVCCStats between versions -// could cause incorrect sysbyte counts during version migration. -// MVCCStatsLegacyRepresentation is the older version of MVCCStats, and allows -// us to prevent this discrepancy during version migration. -type MVCCStatsLegacyRepresentation struct { - ContainsEstimates int64 `protobuf:"varint,14,opt,name=contains_estimates,json=containsEstimates" json:"contains_estimates"` - LastUpdateNanos int64 `protobuf:"fixed64,1,opt,name=last_update_nanos,json=lastUpdateNanos" json:"last_update_nanos"` - IntentAge int64 `protobuf:"fixed64,2,opt,name=intent_age,json=intentAge" json:"intent_age"` - GCBytesAge int64 `protobuf:"fixed64,3,opt,name=gc_bytes_age,json=gcBytesAge" json:"gc_bytes_age"` - LiveBytes int64 `protobuf:"fixed64,4,opt,name=live_bytes,json=liveBytes" json:"live_bytes"` - LiveCount int64 `protobuf:"fixed64,5,opt,name=live_count,json=liveCount" json:"live_count"` - KeyBytes int64 `protobuf:"fixed64,6,opt,name=key_bytes,json=keyBytes" json:"key_bytes"` - KeyCount int64 `protobuf:"fixed64,7,opt,name=key_count,json=keyCount" json:"key_count"` - ValBytes int64 `protobuf:"fixed64,8,opt,name=val_bytes,json=valBytes" json:"val_bytes"` - ValCount int64 `protobuf:"fixed64,9,opt,name=val_count,json=valCount" json:"val_count"` - IntentBytes int64 `protobuf:"fixed64,10,opt,name=intent_bytes,json=intentBytes" json:"intent_bytes"` - IntentCount int64 `protobuf:"fixed64,11,opt,name=intent_count,json=intentCount" json:"intent_count"` - SysBytes int64 `protobuf:"fixed64,12,opt,name=sys_bytes,json=sysBytes" json:"sys_bytes"` - SysCount int64 `protobuf:"fixed64,13,opt,name=sys_count,json=sysCount" json:"sys_count"` -} - -func (m *MVCCStatsLegacyRepresentation) Reset() { *m = MVCCStatsLegacyRepresentation{} } -func (m *MVCCStatsLegacyRepresentation) String() string { return proto.CompactTextString(m) } -func (*MVCCStatsLegacyRepresentation) ProtoMessage() {} -func (*MVCCStatsLegacyRepresentation) Descriptor() ([]byte, []int) { - return fileDescriptor_324ef2186f146e22, []int{3} -} -func (m *MVCCStatsLegacyRepresentation) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MVCCStatsLegacyRepresentation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil -} -func (m *MVCCStatsLegacyRepresentation) XXX_Merge(src proto.Message) { - xxx_messageInfo_MVCCStatsLegacyRepresentation.Merge(m, src) -} -func (m *MVCCStatsLegacyRepresentation) XXX_Size() int { - return m.Size() -} -func (m *MVCCStatsLegacyRepresentation) XXX_DiscardUnknown() { - xxx_messageInfo_MVCCStatsLegacyRepresentation.DiscardUnknown(m) -} - -var xxx_messageInfo_MVCCStatsLegacyRepresentation proto.InternalMessageInfo - func init() { proto.RegisterType((*MVCCMetadata)(nil), "cockroach.storage.enginepb.MVCCMetadata") proto.RegisterType((*MVCCMetadata_SequencedIntent)(nil), "cockroach.storage.enginepb.MVCCMetadata.SequencedIntent") proto.RegisterType((*MVCCMetadataSubsetForMergeSerialization)(nil), "cockroach.storage.enginepb.MVCCMetadataSubsetForMergeSerialization") proto.RegisterType((*MVCCStats)(nil), "cockroach.storage.enginepb.MVCCStats") - proto.RegisterType((*MVCCStatsLegacyRepresentation)(nil), "cockroach.storage.enginepb.MVCCStatsLegacyRepresentation") } func init() { proto.RegisterFile("storage/enginepb/mvcc.proto", fileDescriptor_324ef2186f146e22) } var fileDescriptor_324ef2186f146e22 = []byte{ - // 803 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x95, 0xb1, 0x6f, 0xfb, 0x44, - 0x14, 0xc7, 0x63, 0x92, 0xb4, 0xf6, 0x25, 0xbf, 0xa4, 0x35, 0x15, 0x44, 0x29, 0x38, 0x69, 0x3b, - 0x34, 0xea, 0xe0, 0xa0, 0xb6, 0x48, 0x28, 0x5b, 0x13, 0xa0, 0x54, 0x6a, 0x3b, 0x38, 0x85, 0x81, - 0xc5, 0xba, 0xd8, 0x4f, 0x8e, 0x55, 0xe7, 0xec, 0xfa, 0x2e, 0x69, 0xc2, 0x5f, 0xc1, 0xc8, 0x58, - 0xf1, 0x1f, 0xb0, 0xf1, 0x27, 0x74, 0xec, 0x58, 0x96, 0x0a, 0xd2, 0x05, 0xfe, 0x05, 0x26, 0x74, - 0x3e, 0xdb, 0x71, 0x52, 0xa8, 0x18, 0xba, 0xc1, 0xe6, 0x7b, 0xef, 0xf3, 0xbe, 0x79, 0xf7, 0xbd, - 0xf7, 0x14, 0xb4, 0x4d, 0x99, 0x1f, 0x62, 0x07, 0xda, 0x40, 0x1c, 0x97, 0x40, 0x30, 0x68, 0x8f, - 0x26, 0x96, 0xa5, 0x07, 0xa1, 0xcf, 0x7c, 0xb5, 0x6e, 0xf9, 0xd6, 0x75, 0xe8, 0x63, 0x6b, 0xa8, - 0xc7, 0x98, 0x9e, 0x60, 0xf5, 0x8f, 0xfe, 0xb6, 0xf0, 0x48, 0x54, 0xd6, 0x1b, 0x63, 0xe6, 0x7a, - 0xed, 0xa1, 0x67, 0xb5, 0x3d, 0x70, 0xb0, 0x35, 0x33, 0x99, 0x3b, 0x02, 0xca, 0xf0, 0x28, 0x88, - 0x81, 0x2d, 0xc7, 0x77, 0xfc, 0xe8, 0xb3, 0xcd, 0xbf, 0x44, 0x74, 0xf7, 0x8f, 0x02, 0x2a, 0x5f, - 0x7c, 0xd3, 0xeb, 0x5d, 0x00, 0xc3, 0x36, 0x66, 0x58, 0xfd, 0x14, 0xe5, 0xd9, 0x94, 0xd4, 0xa4, - 0xa6, 0xd4, 0x2a, 0x1d, 0xee, 0xe9, 0xff, 0xdc, 0x8f, 0x7e, 0x35, 0x25, 0xbc, 0xca, 0xe0, 0xbc, - 0x7a, 0x8a, 0x94, 0xf4, 0x07, 0x6b, 0xef, 0xbd, 0x28, 0xe6, 0xcd, 0xe9, 0x43, 0xcf, 0xd2, 0xcf, - 0xa3, 0xe6, 0xae, 0x12, 0xb4, 0x5b, 0xb8, 0x7f, 0x6a, 0xe4, 0x8c, 0x45, 0xad, 0xaa, 0xa1, 0x75, - 0x1b, 0x3c, 0x60, 0x60, 0xd7, 0xf2, 0x4d, 0xa9, 0x25, 0xc7, 0x44, 0x12, 0x54, 0x77, 0x90, 0x72, - 0x0d, 0x33, 0x73, 0x30, 0x63, 0x40, 0x6b, 0x85, 0xa6, 0xd4, 0xca, 0xc7, 0x84, 0x7c, 0x0d, 0xb3, - 0x2e, 0x8f, 0x72, 0x64, 0x82, 0xbd, 0x18, 0x29, 0x66, 0x91, 0x09, 0xf6, 0x04, 0xb2, 0x8d, 0x94, - 0x10, 0xdf, 0xc6, 0xc8, 0x5a, 0x53, 0x6a, 0x95, 0x0d, 0x39, 0xc4, 0xb7, 0x22, 0x09, 0xa8, 0xe2, - 0x12, 0x06, 0x84, 0x99, 0x43, 0x97, 0xdf, 0x7a, 0x56, 0x93, 0x9b, 0xf9, 0x56, 0xe9, 0xf0, 0xb3, - 0xd7, 0xdc, 0xc8, 0x9a, 0xa8, 0xf7, 0xe1, 0x66, 0x0c, 0xc4, 0x02, 0xfb, 0x2c, 0xd2, 0x89, 0x7f, - 0xfe, 0x9d, 0x50, 0xfd, 0x4a, 0x88, 0xaa, 0xe7, 0xa8, 0x3a, 0x82, 0xd0, 0x81, 0xc5, 0x4b, 0xd5, - 0xd6, 0xff, 0xb5, 0x71, 0x46, 0x25, 0xaa, 0x4d, 0xcf, 0xea, 0x31, 0xfa, 0x90, 0x4d, 0x89, 0x69, - 0xbb, 0xb6, 0x49, 0x7c, 0x66, 0x8e, 0x03, 0x1b, 0x33, 0x30, 0x47, 0xc0, 0x70, 0x4d, 0xe1, 0x3e, - 0x1a, 0xef, 0xb3, 0x29, 0xf9, 0xdc, 0xb5, 0x2f, 0x7d, 0xf6, 0x75, 0x94, 0xe3, 0xcd, 0xd6, 0x31, - 0xaa, 0xae, 0xf4, 0xaa, 0x1e, 0x20, 0x99, 0xc6, 0xa1, 0x68, 0x0a, 0x8a, 0xdd, 0x0a, 0xef, 0xfe, - 0xcf, 0xa7, 0xc6, 0xda, 0xd5, 0x94, 0xf4, 0xe1, 0xc6, 0x48, 0xf3, 0xea, 0x16, 0x2a, 0x4e, 0xb0, - 0x37, 0x86, 0xe8, 0xc5, 0xcb, 0x86, 0x38, 0x74, 0xca, 0x3f, 0xdc, 0x35, 0x72, 0x3f, 0xdf, 0x35, - 0xa4, 0xdf, 0xef, 0x1a, 0x52, 0x47, 0x4e, 0x4e, 0xbb, 0x3f, 0x4a, 0x68, 0x3f, 0x6b, 0x53, 0x7f, - 0x3c, 0xa0, 0xc0, 0xbe, 0xf4, 0xc3, 0x0b, 0x7e, 0x95, 0x3e, 0x84, 0x2e, 0xf6, 0xdc, 0xef, 0x30, - 0x73, 0x7d, 0xf2, 0xfa, 0x03, 0xbd, 0xa9, 0x73, 0xcb, 0xed, 0xee, 0xfe, 0x54, 0x44, 0x0a, 0x6f, - 0xb2, 0xcf, 0x30, 0xa3, 0xea, 0x11, 0x52, 0x2d, 0x9f, 0x30, 0xec, 0x12, 0x6a, 0x02, 0x65, 0xee, - 0x08, 0xf3, 0x7e, 0x2a, 0x99, 0x99, 0xda, 0x4c, 0xf2, 0x5f, 0x24, 0x69, 0xf5, 0x13, 0xb4, 0xe9, - 0x61, 0x9a, 0xbe, 0x01, 0xc1, 0xc4, 0xa7, 0x91, 0x95, 0x1b, 0x71, 0x4d, 0x95, 0xa7, 0xc5, 0x2b, - 0x5c, 0xf2, 0xa4, 0xba, 0x87, 0x50, 0x3c, 0x71, 0xd8, 0x11, 0x66, 0x26, 0xa8, 0x22, 0xe2, 0x27, - 0x0e, 0xa8, 0xc7, 0xa8, 0xec, 0x58, 0xc2, 0x91, 0x08, 0xcb, 0x47, 0x98, 0xca, 0xb1, 0xf9, 0x53, - 0x03, 0x9d, 0xf6, 0x22, 0x73, 0x4e, 0x1c, 0x30, 0x90, 0x63, 0x25, 0xdf, 0x5c, 0xda, 0x73, 0x27, - 0x90, 0x59, 0x98, 0x54, 0x9a, 0xc7, 0x85, 0xa1, 0x09, 0x64, 0xf9, 0x63, 0xc2, 0xa2, 0x95, 0x59, - 0x82, 0x7a, 0x3c, 0xbc, 0xbc, 0x79, 0x6b, 0x19, 0x66, 0x69, 0xf3, 0x38, 0x22, 0x64, 0xd6, 0x57, - 0x90, 0x54, 0x65, 0xb1, 0x9c, 0x72, 0x16, 0x49, 0x97, 0x33, 0x46, 0x84, 0x8a, 0xb2, 0x82, 0x08, - 0x95, 0x7d, 0x54, 0x8e, 0x0d, 0x13, 0x42, 0x28, 0x43, 0x95, 0x44, 0x46, 0x68, 0x2d, 0x40, 0x21, - 0x57, 0x7a, 0x09, 0x0a, 0xc5, 0x0e, 0xfa, 0x80, 0x42, 0x80, 0x43, 0xcc, 0xc0, 0x36, 0x97, 0x4a, - 0x36, 0x32, 0x25, 0x5b, 0x29, 0x73, 0x96, 0xa9, 0xdd, 0x41, 0x0a, 0x9d, 0xd1, 0xb8, 0x95, 0x72, - 0xb6, 0x61, 0x3a, 0xa3, 0xe9, 0x9d, 0x38, 0x22, 0x14, 0xdf, 0xad, 0x20, 0x42, 0x45, 0x47, 0x1b, - 0x78, 0xe0, 0x87, 0xcc, 0xa4, 0x01, 0x26, 0xb1, 0x58, 0x35, 0x43, 0x56, 0xa2, 0x6c, 0x3f, 0xc0, - 0x24, 0x92, 0xec, 0xc8, 0xe9, 0xcc, 0xfe, 0x52, 0x40, 0x1f, 0xa7, 0x33, 0x2b, 0xc6, 0xdd, 0x80, - 0x20, 0x04, 0x0a, 0x84, 0x89, 0x75, 0xfa, 0x7f, 0x8e, 0xff, 0x2b, 0x73, 0xfc, 0x26, 0xb3, 0xb8, - 0x98, 0xad, 0xee, 0xc1, 0xfd, 0x6f, 0x5a, 0xee, 0x7e, 0xae, 0x49, 0x0f, 0x73, 0x4d, 0x7a, 0x9c, - 0x6b, 0xd2, 0xaf, 0x73, 0x4d, 0xfa, 0xfe, 0x59, 0xcb, 0x3d, 0x3c, 0x6b, 0xb9, 0xc7, 0x67, 0x2d, - 0xf7, 0xad, 0x9c, 0xfc, 0x07, 0xfe, 0x15, 0x00, 0x00, 0xff, 0xff, 0xaf, 0xd1, 0x7e, 0xfe, 0xdb, - 0x08, 0x00, 0x00, + // 775 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x41, 0x4f, 0xdb, 0x48, + 0x14, 0xc7, 0xe3, 0x0d, 0x01, 0x7b, 0x12, 0x12, 0xf0, 0xa2, 0xdd, 0x28, 0xac, 0x9c, 0x00, 0x07, + 0x22, 0x0e, 0xce, 0x0a, 0x58, 0x69, 0x95, 0x1b, 0xc9, 0xee, 0xb2, 0x48, 0xc0, 0xc1, 0x61, 0xf7, + 0xd0, 0x8b, 0x35, 0xb1, 0x9f, 0x1c, 0x0b, 0x67, 0x1c, 0x3c, 0x93, 0x90, 0xf4, 0x53, 0xf4, 0xd8, + 0x23, 0xea, 0x37, 0xe8, 0xad, 0x1f, 0x81, 0x23, 0x47, 0x4e, 0xa8, 0x0d, 0x97, 0xf6, 0x2b, 0xf4, + 0x54, 0x8d, 0xc7, 0x76, 0x9c, 0xd0, 0xa2, 0x1e, 0x7a, 0x9b, 0x79, 0xef, 0xf7, 0xfe, 0x7e, 0xf3, + 0x9f, 0x79, 0x46, 0x9b, 0x94, 0xf9, 0x01, 0x76, 0xa0, 0x01, 0xc4, 0x71, 0x09, 0x0c, 0xba, 0x8d, + 0xfe, 0xc8, 0xb2, 0xf4, 0x41, 0xe0, 0x33, 0x5f, 0xad, 0x58, 0xbe, 0x75, 0x19, 0xf8, 0xd8, 0xea, + 0xe9, 0x11, 0xa6, 0xc7, 0x58, 0xe5, 0xb7, 0xaf, 0x16, 0x1e, 0x88, 0xca, 0x4a, 0x75, 0xc8, 0x5c, + 0xaf, 0xd1, 0xf3, 0xac, 0x86, 0x07, 0x0e, 0xb6, 0x26, 0x26, 0x73, 0xfb, 0x40, 0x19, 0xee, 0x0f, + 0x22, 0x60, 0xc3, 0xf1, 0x1d, 0x3f, 0x5c, 0x36, 0xf8, 0x4a, 0x44, 0xb7, 0x3f, 0x2d, 0xa1, 0xc2, + 0xd9, 0xff, 0xed, 0xf6, 0x19, 0x30, 0x6c, 0x63, 0x86, 0xd5, 0x3f, 0x50, 0x96, 0x8d, 0x49, 0x59, + 0xaa, 0x49, 0xf5, 0xfc, 0xfe, 0x8e, 0xfe, 0xed, 0x7e, 0xf4, 0x8b, 0x31, 0xe1, 0x55, 0x06, 0xe7, + 0xd5, 0x63, 0xa4, 0x24, 0x1f, 0x2c, 0xff, 0xf4, 0xa4, 0x98, 0x37, 0xa7, 0xf7, 0x3c, 0x4b, 0x3f, + 0x0d, 0x9b, 0xbb, 0x88, 0xd1, 0xd6, 0xd2, 0xed, 0x43, 0x35, 0x63, 0xcc, 0x6a, 0x55, 0x0d, 0xad, + 0xd8, 0xe0, 0x01, 0x03, 0xbb, 0x9c, 0xad, 0x49, 0x75, 0x39, 0x22, 0xe2, 0xa0, 0xba, 0x85, 0x94, + 0x4b, 0x98, 0x98, 0xdd, 0x09, 0x03, 0x5a, 0x5e, 0xaa, 0x49, 0xf5, 0x6c, 0x44, 0xc8, 0x97, 0x30, + 0x69, 0xf1, 0x28, 0x47, 0x46, 0xd8, 0x8b, 0x90, 0x5c, 0x1a, 0x19, 0x61, 0x4f, 0x20, 0x9b, 0x48, + 0x09, 0xf0, 0x75, 0x84, 0x2c, 0xd7, 0xa4, 0x7a, 0xc1, 0x90, 0x03, 0x7c, 0x2d, 0x92, 0x80, 0x8a, + 0x2e, 0x61, 0x40, 0x98, 0xd9, 0x73, 0xf9, 0xa9, 0x27, 0x65, 0xb9, 0x96, 0xad, 0xe7, 0xf7, 0xff, + 0x7c, 0xce, 0x8d, 0xb4, 0x89, 0x7a, 0x07, 0xae, 0x86, 0x40, 0x2c, 0xb0, 0x4f, 0x42, 0x9d, 0xe8, + 0xf3, 0xab, 0x42, 0xf5, 0x5f, 0x21, 0xaa, 0x9e, 0xa2, 0x52, 0x1f, 0x02, 0x07, 0x66, 0x37, 0x55, + 0x5e, 0xf9, 0x6e, 0xe3, 0x8c, 0x62, 0x58, 0x9b, 0xec, 0xd5, 0x43, 0xf4, 0x2b, 0x1b, 0x13, 0xd3, + 0x76, 0x6d, 0x93, 0xf8, 0xcc, 0x1c, 0x0e, 0x6c, 0xcc, 0xc0, 0xec, 0x03, 0xc3, 0x65, 0x85, 0xfb, + 0x68, 0xfc, 0xcc, 0xc6, 0xe4, 0x2f, 0xd7, 0x3e, 0xf7, 0xd9, 0x7f, 0x61, 0x8e, 0x37, 0x5b, 0xc1, + 0xa8, 0xb4, 0xd0, 0xab, 0xba, 0x87, 0x64, 0x1a, 0x85, 0xc2, 0x57, 0x90, 0x6b, 0x15, 0x79, 0xf7, + 0x9f, 0x1f, 0xaa, 0xcb, 0x17, 0x63, 0xd2, 0x81, 0x2b, 0x23, 0xc9, 0xab, 0x1b, 0x28, 0x37, 0xc2, + 0xde, 0x10, 0xc2, 0x1b, 0x2f, 0x18, 0x62, 0xd3, 0x2c, 0xbc, 0xbe, 0xa9, 0x66, 0xde, 0xdd, 0x54, + 0xa5, 0x8f, 0x37, 0x55, 0xa9, 0x29, 0xc7, 0xbb, 0xed, 0x37, 0x12, 0xda, 0x4d, 0xdb, 0xd4, 0x19, + 0x76, 0x29, 0xb0, 0x7f, 0xfc, 0xe0, 0x8c, 0x1f, 0xa5, 0x03, 0x81, 0x8b, 0x3d, 0xf7, 0x25, 0x66, + 0xae, 0x4f, 0x9e, 0xbf, 0xa0, 0x1f, 0xea, 0xdc, 0x7c, 0xbb, 0xdb, 0x6f, 0x73, 0x48, 0xe1, 0x4d, + 0x76, 0x18, 0x66, 0x54, 0x3d, 0x40, 0xaa, 0xe5, 0x13, 0x86, 0x5d, 0x42, 0x4d, 0xa0, 0xcc, 0xed, + 0x63, 0xde, 0x4f, 0x31, 0xf5, 0xa6, 0xd6, 0xe3, 0xfc, 0xdf, 0x71, 0x5a, 0xfd, 0x1d, 0xad, 0x7b, + 0x98, 0x26, 0x77, 0x40, 0x30, 0xf1, 0x69, 0x68, 0xe5, 0x5a, 0x54, 0x53, 0xe2, 0x69, 0x71, 0x0b, + 0xe7, 0x3c, 0xa9, 0xee, 0x20, 0x14, 0xbd, 0x38, 0xec, 0x08, 0x33, 0x63, 0x54, 0x11, 0xf1, 0x23, + 0x07, 0xd4, 0x43, 0x54, 0x70, 0x2c, 0xe1, 0x48, 0x88, 0x65, 0x43, 0x4c, 0xe5, 0xd8, 0xf4, 0xa1, + 0x8a, 0x8e, 0xdb, 0xa1, 0x39, 0x47, 0x0e, 0x18, 0xc8, 0xb1, 0xe2, 0x35, 0x97, 0xf6, 0xdc, 0x11, + 0xa4, 0x06, 0x26, 0x91, 0xe6, 0x71, 0x61, 0x68, 0x0c, 0x59, 0xfe, 0x90, 0xb0, 0x70, 0x64, 0xe6, + 0xa0, 0x36, 0x0f, 0xcf, 0x4f, 0xde, 0x72, 0x8a, 0x99, 0x9b, 0x3c, 0x8e, 0x08, 0x99, 0x95, 0x05, + 0x24, 0x51, 0x99, 0x0d, 0xa7, 0x9c, 0x46, 0x92, 0xe1, 0x8c, 0x10, 0xa1, 0xa2, 0x2c, 0x20, 0x42, + 0x65, 0x17, 0x15, 0x22, 0xc3, 0x84, 0x10, 0x4a, 0x51, 0x79, 0x91, 0x11, 0x5a, 0x33, 0x50, 0xc8, + 0xe5, 0x9f, 0x82, 0x42, 0xb1, 0x89, 0x7e, 0xa1, 0x30, 0xc0, 0x01, 0x66, 0x60, 0x9b, 0x73, 0x25, + 0x6b, 0xa9, 0x92, 0x8d, 0x84, 0x39, 0x49, 0xd5, 0x6e, 0x21, 0x85, 0x4e, 0x68, 0xd4, 0x4a, 0x21, + 0xdd, 0x30, 0x9d, 0xd0, 0xe4, 0x4c, 0x1c, 0x11, 0x8a, 0xab, 0x0b, 0x88, 0x50, 0xd1, 0xd1, 0x1a, + 0xee, 0xfa, 0x01, 0x33, 0xe9, 0x00, 0x93, 0x48, 0xac, 0x94, 0x22, 0x8b, 0x61, 0xb6, 0x33, 0xc0, + 0x24, 0x94, 0x6c, 0xca, 0xf1, 0x9b, 0x6d, 0xed, 0xdd, 0x7e, 0xd0, 0x32, 0xb7, 0x53, 0x4d, 0xba, + 0x9b, 0x6a, 0xd2, 0xfd, 0x54, 0x93, 0xde, 0x4f, 0x35, 0xe9, 0xd5, 0xa3, 0x96, 0xb9, 0x7b, 0xd4, + 0x32, 0xf7, 0x8f, 0x5a, 0xe6, 0x85, 0x1c, 0xff, 0xa7, 0xbe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x51, + 0x21, 0x67, 0x2c, 0x7f, 0x06, 0x00, 0x00, } func (this *MVCCMetadata_SequencedIntent) Equal(that interface{}) bool { @@ -566,69 +509,6 @@ func (this *MVCCStats) Equal(that interface{}) bool { } return true } -func (this *MVCCStatsLegacyRepresentation) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*MVCCStatsLegacyRepresentation) - if !ok { - that2, ok := that.(MVCCStatsLegacyRepresentation) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if this.ContainsEstimates != that1.ContainsEstimates { - return false - } - if this.LastUpdateNanos != that1.LastUpdateNanos { - return false - } - if this.IntentAge != that1.IntentAge { - return false - } - if this.GCBytesAge != that1.GCBytesAge { - return false - } - if this.LiveBytes != that1.LiveBytes { - return false - } - if this.LiveCount != that1.LiveCount { - return false - } - if this.KeyBytes != that1.KeyBytes { - return false - } - if this.KeyCount != that1.KeyCount { - return false - } - if this.ValBytes != that1.ValBytes { - return false - } - if this.ValCount != that1.ValCount { - return false - } - if this.IntentBytes != that1.IntentBytes { - return false - } - if this.IntentCount != that1.IntentCount { - return false - } - if this.SysBytes != that1.SysBytes { - return false - } - if this.SysCount != that1.SysCount { - return false - } - return true -} func (m *MVCCMetadata) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -894,84 +774,6 @@ func (m *MVCCStats) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *MVCCStatsLegacyRepresentation) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MVCCStatsLegacyRepresentation) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MVCCStatsLegacyRepresentation) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - i = encodeVarintMvcc(dAtA, i, uint64(m.ContainsEstimates)) - i-- - dAtA[i] = 0x70 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.SysCount)) - i-- - dAtA[i] = 0x69 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.SysBytes)) - i-- - dAtA[i] = 0x61 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.IntentCount)) - i-- - dAtA[i] = 0x59 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.IntentBytes)) - i-- - dAtA[i] = 0x51 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.ValCount)) - i-- - dAtA[i] = 0x49 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.ValBytes)) - i-- - dAtA[i] = 0x41 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.KeyCount)) - i-- - dAtA[i] = 0x39 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.KeyBytes)) - i-- - dAtA[i] = 0x31 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.LiveCount)) - i-- - dAtA[i] = 0x29 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.LiveBytes)) - i-- - dAtA[i] = 0x21 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.GCBytesAge)) - i-- - dAtA[i] = 0x19 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.IntentAge)) - i-- - dAtA[i] = 0x11 - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.LastUpdateNanos)) - i-- - dAtA[i] = 0x9 - return len(dAtA) - i, nil -} - func encodeVarintMvcc(dAtA []byte, offset int, v uint64) int { offset -= sovMvcc(v) base := offset @@ -1132,69 +934,6 @@ func NewPopulatedMVCCStats(r randyMvcc, easy bool) *MVCCStats { return this } -func NewPopulatedMVCCStatsLegacyRepresentation(r randyMvcc, easy bool) *MVCCStatsLegacyRepresentation { - this := &MVCCStatsLegacyRepresentation{} - this.LastUpdateNanos = int64(r.Int63()) - if r.Intn(2) == 0 { - this.LastUpdateNanos *= -1 - } - this.IntentAge = int64(r.Int63()) - if r.Intn(2) == 0 { - this.IntentAge *= -1 - } - this.GCBytesAge = int64(r.Int63()) - if r.Intn(2) == 0 { - this.GCBytesAge *= -1 - } - this.LiveBytes = int64(r.Int63()) - if r.Intn(2) == 0 { - this.LiveBytes *= -1 - } - this.LiveCount = int64(r.Int63()) - if r.Intn(2) == 0 { - this.LiveCount *= -1 - } - this.KeyBytes = int64(r.Int63()) - if r.Intn(2) == 0 { - this.KeyBytes *= -1 - } - this.KeyCount = int64(r.Int63()) - if r.Intn(2) == 0 { - this.KeyCount *= -1 - } - this.ValBytes = int64(r.Int63()) - if r.Intn(2) == 0 { - this.ValBytes *= -1 - } - this.ValCount = int64(r.Int63()) - if r.Intn(2) == 0 { - this.ValCount *= -1 - } - this.IntentBytes = int64(r.Int63()) - if r.Intn(2) == 0 { - this.IntentBytes *= -1 - } - this.IntentCount = int64(r.Int63()) - if r.Intn(2) == 0 { - this.IntentCount *= -1 - } - this.SysBytes = int64(r.Int63()) - if r.Intn(2) == 0 { - this.SysBytes *= -1 - } - this.SysCount = int64(r.Int63()) - if r.Intn(2) == 0 { - this.SysCount *= -1 - } - this.ContainsEstimates = int64(r.Int63()) - if r.Intn(2) == 0 { - this.ContainsEstimates *= -1 - } - if !easy && r.Intn(10) != 0 { - } - return this -} - type randyMvcc interface { Float32() float32 Float64() float64 @@ -1358,29 +1097,6 @@ func (m *MVCCStats) Size() (n int) { return n } -func (m *MVCCStatsLegacyRepresentation) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 9 - n += 1 + sovMvcc(uint64(m.ContainsEstimates)) - return n -} - func sovMvcc(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2131,205 +1847,6 @@ func (m *MVCCStats) Unmarshal(dAtA []byte) error { } return nil } -func (m *MVCCStatsLegacyRepresentation) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMvcc - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MVCCStatsLegacyRepresentation: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MVCCStatsLegacyRepresentation: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field LastUpdateNanos", wireType) - } - m.LastUpdateNanos = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.LastUpdateNanos = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 2: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field IntentAge", wireType) - } - m.IntentAge = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.IntentAge = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 3: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field GCBytesAge", wireType) - } - m.GCBytesAge = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.GCBytesAge = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 4: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field LiveBytes", wireType) - } - m.LiveBytes = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.LiveBytes = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 5: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field LiveCount", wireType) - } - m.LiveCount = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.LiveCount = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 6: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field KeyBytes", wireType) - } - m.KeyBytes = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.KeyBytes = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 7: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field KeyCount", wireType) - } - m.KeyCount = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.KeyCount = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 8: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field ValBytes", wireType) - } - m.ValBytes = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.ValBytes = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 9: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field ValCount", wireType) - } - m.ValCount = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.ValCount = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 10: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field IntentBytes", wireType) - } - m.IntentBytes = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.IntentBytes = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 11: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field IntentCount", wireType) - } - m.IntentCount = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.IntentCount = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 12: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field SysBytes", wireType) - } - m.SysBytes = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.SysBytes = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 13: - if wireType != 1 { - return fmt.Errorf("proto: wrong wireType = %d for field SysCount", wireType) - } - m.SysCount = 0 - if (iNdEx + 8) > l { - return io.ErrUnexpectedEOF - } - m.SysCount = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) - iNdEx += 8 - case 14: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ContainsEstimates", wireType) - } - m.ContainsEstimates = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMvcc - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ContainsEstimates |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipMvcc(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthMvcc - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipMvcc(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/storage/enginepb/mvcc.proto b/pkg/storage/enginepb/mvcc.proto index 863b9f146250..ffaf911e898e 100644 --- a/pkg/storage/enginepb/mvcc.proto +++ b/pkg/storage/enginepb/mvcc.proto @@ -218,30 +218,3 @@ message MVCCStats { // WARNING: Do not add any PII-holding fields here, as this // whole message is marked as safe for log redaction. } - -// MVCCStatsLegacyRepresentation is almost identical to MVCCStats, except -// it does not have a field tracking abort span bytes. -// The abort span bytes field in MVCCStats didn't exist in earlier versions, -// and its addition of it to MVCCStats causes the struct to be larger than it was -// previously. The discrepancy between the size of MVCCStats between versions -// could cause incorrect sysbyte counts during version migration. -// MVCCStatsLegacyRepresentation is the older version of MVCCStats, and allows -// us to prevent this discrepancy during version migration. -message MVCCStatsLegacyRepresentation { - option (gogoproto.equal) = true; - option (gogoproto.populate) = true; - optional int64 contains_estimates = 14 [(gogoproto.nullable) = false]; - optional sfixed64 last_update_nanos = 1 [(gogoproto.nullable) = false]; - optional sfixed64 intent_age = 2 [(gogoproto.nullable) = false]; - optional sfixed64 gc_bytes_age = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "GCBytesAge"]; - optional sfixed64 live_bytes = 4 [(gogoproto.nullable) = false]; - optional sfixed64 live_count = 5 [(gogoproto.nullable) = false]; - optional sfixed64 key_bytes = 6 [(gogoproto.nullable) = false]; - optional sfixed64 key_count = 7 [(gogoproto.nullable) = false]; - optional sfixed64 val_bytes = 8 [(gogoproto.nullable) = false]; - optional sfixed64 val_count = 9 [(gogoproto.nullable) = false]; - optional sfixed64 intent_bytes = 10 [(gogoproto.nullable) = false]; - optional sfixed64 intent_count = 11 [(gogoproto.nullable) = false]; - optional sfixed64 sys_bytes = 12 [(gogoproto.nullable) = false]; - optional sfixed64 sys_count = 13 [(gogoproto.nullable) = false]; -} From 45cd1407a9499aa6f717d9f5e1a05e4f132358e8 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Mon, 25 Oct 2021 17:30:14 +0100 Subject: [PATCH 014/205] rttanalysis: allow selecting benchmark expectation subtests This allows us to run a smaller set of the benchmarks using a filter such as `TestBenchmarkExpectation/BenchmarkAlterTableAddColumn`. This is nice when trying to reproduce an error seen in CI using `make stress`. Release note: None --- .../rttanalysis/validate_benchmark_data.go | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/pkg/bench/rttanalysis/validate_benchmark_data.go b/pkg/bench/rttanalysis/validate_benchmark_data.go index 320f964f666f..dc5e9b231351 100644 --- a/pkg/bench/rttanalysis/validate_benchmark_data.go +++ b/pkg/bench/rttanalysis/validate_benchmark_data.go @@ -74,30 +74,32 @@ func RunBenchmarkExpectationTests(t *testing.T) { }() for _, b := range benchmarks { - flags := []string{ - "--test.run=^$", - "--test.bench=" + b, - "--test.benchtime=1x", - } - if testing.Verbose() { - flags = append(flags, "--test.v") - } - results := runBenchmarks(t, flags...) - - for _, r := range results { - exp, ok := expectations.find(r.name) - if !ok { - t.Logf("no expectation for benchmark %s, got %d", r.name, r.result) - continue + t.Run(b, func(t *testing.T) { + flags := []string{ + "--test.run=^$", + "--test.bench=" + b, + "--test.benchtime=1x", } - if !exp.matches(r.result) { - t.Errorf("fail: expected %s to perform KV lookups in [%d, %d], got %d", - r.name, exp.min, exp.max, r.result) - } else { - t.Logf("success: expected %s to perform KV lookups in [%d, %d], got %d", - r.name, exp.min, exp.max, r.result) + if testing.Verbose() { + flags = append(flags, "--test.v") } - } + results := runBenchmarks(t, flags...) + + for _, r := range results { + exp, ok := expectations.find(r.name) + if !ok { + t.Logf("no expectation for benchmark %s, got %d", r.name, r.result) + continue + } + if !exp.matches(r.result) { + t.Errorf("fail: expected %s to perform KV lookups in [%d, %d], got %d", + r.name, exp.min, exp.max, r.result) + } else { + t.Logf("success: expected %s to perform KV lookups in [%d, %d], got %d", + r.name, exp.min, exp.max, r.result) + } + } + }) } } From 4632f435260320d266b602161e363ad68ef9c552 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Wed, 20 Oct 2021 18:38:00 -0700 Subject: [PATCH 015/205] opt: improve FDs for left join with single-row left input This change improves the left join FDs when the left input has at most one row. This is useful in cases like: ``` SELECT FROM a LEFT JOIN b ON a.v = b.v WHERE f.key = $1 ``` In this case, we can declare the `b.v` column as constant: it is either always equal to `a.v`, or the output has a single row. If we have a `GROUP BY b.v` on top, this FD can be helpful. Informs #71768. Release note: None --- pkg/sql/opt/memo/testdata/logprops/join | 51 +++++++++++++++++++ pkg/sql/opt/memo/testdata/logprops/upsert | 14 ++--- pkg/sql/opt/norm/testdata/rules/decorrelate | 4 +- pkg/sql/opt/norm/testdata/rules/groupby | 2 +- pkg/sql/opt/props/func_dep.go | 33 +++++++++--- pkg/sql/opt/props/func_dep_test.go | 16 ++++++ pkg/sql/opt/xform/testdata/external/hibernate | 20 ++++---- pkg/sql/opt/xform/testdata/external/nova | 48 ++++++++--------- 8 files changed, 138 insertions(+), 50 deletions(-) diff --git a/pkg/sql/opt/memo/testdata/logprops/join b/pkg/sql/opt/memo/testdata/logprops/join index 64727591181d..f1a9755e1035 100644 --- a/pkg/sql/opt/memo/testdata/logprops/join +++ b/pkg/sql/opt/memo/testdata/logprops/join @@ -281,6 +281,57 @@ project ├── variable: x:1 [type=int] └── variable: u:7 [type=int] +# Left-join with single row left input: column u should be constant. +build +SELECT * FROM (SELECT * FROM xysd WHERE x=123) AS xysd1 LEFT JOIN uv ON y=u +---- +project + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) u:7(int) v:8(int) + ├── fd: ()-->(1-4,7) + ├── prune: (1-4,7,8) + ├── reject-nulls: (7,8) + └── left-join (hash) + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) u:7(int) v:8(int) rowid:9(int) uv.crdb_internal_mvcc_timestamp:10(decimal) uv.tableoid:11(oid) + ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-one) + ├── key: (9) + ├── fd: ()-->(1-4,7), (9)-->(8,10,11) + ├── prune: (1,3,4,8-11) + ├── reject-nulls: (7-11) + ├── interesting orderings: (+9) + ├── project + │ ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(1-4) + │ ├── prune: (1-4) + │ └── select + │ ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) xysd.crdb_internal_mvcc_timestamp:5(decimal) xysd.tableoid:6(oid) + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(1-6) + │ ├── prune: (2-6) + │ ├── scan xysd + │ │ ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) xysd.crdb_internal_mvcc_timestamp:5(decimal) xysd.tableoid:6(oid) + │ │ ├── key: (1) + │ │ ├── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6) + │ │ ├── prune: (1-6) + │ │ └── interesting orderings: (+1) (-3,+4,+1) + │ └── filters + │ └── eq [type=bool, outer=(1), constraints=(/1: [/123 - /123]; tight), fd=()-->(1)] + │ ├── variable: x:1 [type=int] + │ └── const: 123 [type=int] + ├── scan uv + │ ├── columns: u:7(int) v:8(int!null) rowid:9(int!null) uv.crdb_internal_mvcc_timestamp:10(decimal) uv.tableoid:11(oid) + │ ├── key: (9) + │ ├── fd: (9)-->(7,8,10,11) + │ ├── prune: (7-11) + │ ├── interesting orderings: (+9) + │ └── unfiltered-cols: (7-11) + └── filters + └── eq [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)] + ├── variable: y:2 [type=int] + └── variable: u:7 [type=int] + # Left-join-apply. opt SELECT * FROM xysd WHERE (SELECT u FROM uv WHERE u=x) IS NULL diff --git a/pkg/sql/opt/memo/testdata/logprops/upsert b/pkg/sql/opt/memo/testdata/logprops/upsert index 2d90d5f33a55..e3d83f1ce2c1 100644 --- a/pkg/sql/opt/memo/testdata/logprops/upsert +++ b/pkg/sql/opt/memo/testdata/logprops/upsert @@ -57,35 +57,35 @@ project ├── cardinality: [0 - 1] ├── volatile ├── key: (17) - ├── fd: ()-->(7,8,12,13,20), (17)-->(14-16,18,19,23-26), (14)-->(15-19), (15,16)~~>(14,17-19), (15)~~>(16), (16)-->(21), (21)-->(22) + ├── fd: ()-->(7,8,12,13,15,16,20-22), (17)-->(14,18,19,23-26), (14)-->(17-19), (15,16)~~>(14,17-19) ├── prune: (7,8,12-26) ├── reject-nulls: (14-19,21,22) - ├── interesting orderings: (+17 opt(7,8,12,13,20)) (+14 opt(7,8,12,13,20)) (+15,+17 opt(7,8,12,13,20)) + ├── interesting orderings: (+17 opt(7,8,12,13,15,16,20-22)) (+14 opt(7,8,12,13,15,16,20-22)) ├── project │ ├── columns: c_comp:22(int) x:7(int!null) y:8(int!null) rowid_default:12(int) c_comp:13(int!null) a:14(int) b:15(int) c:16(int) rowid:17(int) abc.crdb_internal_mvcc_timestamp:18(decimal) abc.tableoid:19(oid) a_new:20(int!null) b_new:21(int) │ ├── cardinality: [0 - 1] │ ├── volatile │ ├── key: (17) - │ ├── fd: ()-->(7,8,12,13,20), (17)-->(14-16,18,19), (14)-->(15-19), (15,16)~~>(14,17-19), (15)~~>(16), (16)-->(21), (21)-->(22) + │ ├── fd: ()-->(7,8,12,13,15,16,20-22), (17)-->(14,18,19), (14)-->(17-19), (15,16)~~>(14,17-19) │ ├── prune: (7,8,12-22) │ ├── reject-nulls: (14-19,21,22) - │ ├── interesting orderings: (+17 opt(7,8,12,13,20)) (+14 opt(7,8,12,13,20)) (+15,+17 opt(7,8,12,13,20)) + │ ├── interesting orderings: (+17 opt(7,8,12,13,15,16,20-22)) (+14 opt(7,8,12,13,15,16,20-22)) │ ├── project │ │ ├── columns: a_new:20(int!null) b_new:21(int) x:7(int!null) y:8(int!null) rowid_default:12(int) c_comp:13(int!null) a:14(int) b:15(int) c:16(int) rowid:17(int) abc.crdb_internal_mvcc_timestamp:18(decimal) abc.tableoid:19(oid) │ │ ├── cardinality: [0 - 1] │ │ ├── volatile │ │ ├── key: (17) - │ │ ├── fd: ()-->(7,8,12,13,20), (17)-->(14-16,18,19), (14)-->(15-19), (15,16)~~>(14,17-19), (15)~~>(16), (16)-->(21) + │ │ ├── fd: ()-->(7,8,12,13,15,16,20,21), (17)-->(14,18,19), (14)-->(17-19), (15,16)~~>(14,17-19) │ │ ├── prune: (7,8,12-21) │ │ ├── reject-nulls: (14-19,21) - │ │ ├── interesting orderings: (+17 opt(7,8,12,13,20)) (+14 opt(7,8,12,13,20)) (+15,+17 opt(7,8,12,13,20)) + │ │ ├── interesting orderings: (+17 opt(7,8,12,13,15,16,20,21)) (+14 opt(7,8,12,13,15,16,20,21)) │ │ ├── left-join (hash) │ │ │ ├── columns: x:7(int!null) y:8(int!null) rowid_default:12(int) c_comp:13(int!null) a:14(int) b:15(int) c:16(int) rowid:17(int) abc.crdb_internal_mvcc_timestamp:18(decimal) abc.tableoid:19(oid) │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-one) │ │ │ ├── volatile │ │ │ ├── key: (17) - │ │ │ ├── fd: ()-->(7,8,12,13), (17)-->(14-16,18,19), (14)-->(15-19), (15,16)~~>(14,17-19), (15)~~>(16) + │ │ │ ├── fd: ()-->(7,8,12,13,15,16), (17)-->(14,18,19), (14)-->(17-19), (15,16)~~>(14,17-19) │ │ │ ├── prune: (14,17-19) │ │ │ ├── reject-nulls: (14-19) │ │ │ ├── interesting orderings: (+17) (+14) (+15,+17) diff --git a/pkg/sql/opt/norm/testdata/rules/decorrelate b/pkg/sql/opt/norm/testdata/rules/decorrelate index 6afe411bcff6..1ca6a8247d23 100644 --- a/pkg/sql/opt/norm/testdata/rules/decorrelate +++ b/pkg/sql/opt/norm/testdata/rules/decorrelate @@ -4078,7 +4078,7 @@ project │ ├── left-join (hash) │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 y:9 │ │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-one) - │ │ ├── fd: ()-->(1-5) + │ │ ├── fd: ()-->(1-5,9) │ │ ├── limit hint: 1.00 │ │ ├── select │ │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 @@ -4722,7 +4722,7 @@ values ├── left-join (hash) │ ├── columns: k:1!null i:2 y:9 true:13 │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-one) - │ ├── fd: ()-->(1,2) + │ ├── fd: ()-->(1,2,9) │ ├── limit hint: 1.00 │ ├── limit │ │ ├── columns: k:1!null i:2 diff --git a/pkg/sql/opt/norm/testdata/rules/groupby b/pkg/sql/opt/norm/testdata/rules/groupby index 3e046f4bb8c8..378af9f6cd22 100644 --- a/pkg/sql/opt/norm/testdata/rules/groupby +++ b/pkg/sql/opt/norm/testdata/rules/groupby @@ -1734,7 +1734,7 @@ project │ ├── columns: k:1!null i:2!null xy.x:8 y:9 │ ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-one) │ ├── key: (8) - │ ├── fd: ()-->(1,2), (8)-->(9) + │ ├── fd: ()-->(1,2,9) │ ├── select │ │ ├── columns: k:1!null i:2!null │ │ ├── cardinality: [0 - 1] diff --git a/pkg/sql/opt/props/func_dep.go b/pkg/sql/opt/props/func_dep.go index 3aa1758caf7e..41087450088d 100644 --- a/pkg/sql/opt/props/func_dep.go +++ b/pkg/sql/opt/props/func_dep.go @@ -700,11 +700,12 @@ func (f *FuncDepSet) AreColsEquiv(col1, col2 opt.ColumnID) bool { // (a)==(b) // (b)==(c) // (a)==(d) +// (e)==(f) // -// The equivalence closure for (a) is (a,b,c,d) because (a) is transitively -// equivalent to all other columns. Therefore, all columns must have equal -// non-NULL values, or else all must be NULL (see definition for NULL= in the -// comment for FuncDepSet). +// The equivalence closure for (a,e) is (a,b,c,d,e,f) because all these columns +// are transitively equal to either a or e. Therefore, all columns must have +// equal non-NULL values, or else all must be NULL (see definition for NULL= in +// the comment for FuncDepSet). func (f *FuncDepSet) ComputeEquivClosure(cols opt.ColSet) opt.ColSet { // Don't need to get transitive closure, because equivalence closures are // already maintained for every column. @@ -890,8 +891,8 @@ func (f *FuncDepSet) AddEquivalency(a, b opt.ColumnID) { f.addEquivalency(equiv) } -// AddConstants adds a strict FD to the set that declares the given column as -// having the same constant value for all rows. If the column is nullable, then +// AddConstants adds a strict FD to the set that declares each given column as +// having the same constant value for all rows. If a column is nullable, then // its value may be NULL, but then the column must be NULL for all rows. For // column "a", the FD looks like this: // @@ -1376,6 +1377,26 @@ func (f *FuncDepSet) MakeLeftOuter( // leftKey because nullExtendRightRows can remove FDs, such that the closure // of oldKey ends up missing some columns from the right. f.ensureKeyClosure(leftCols.Union(rightCols)) + + // If the left input has at most one row, any columns that - when the join + // filters hold - are functionally dependent on the left columns are constant + // in the left join output: + // - either the one left row has a match, and all output rows have the same + // values for the left columns + // - or the left row has no match, and there is a single output row (where + // any functional dependencies hold trivially). + // + // This does not hold in general when the left equality column is + // constant but the left input has more than one row, for example: + // ab contains (1, 1), (1, 2) + // cd contains (1, 1) + // ab JOIN cd ON (a=c AND b=d) contains (1, 1, 1, 1), (1, 2, NULL, NULL) + // Here a is constant in ab but c is not constant in the result. + if leftFDs.HasMax1Row() { + constCols := filtersFDs.ComputeClosure(leftCols) + constCols.IntersectionWith(rightCols) + f.AddConstants(constCols) + } } // MakeFullOuter modifies the cartesian product FD set to reflect the impact of diff --git a/pkg/sql/opt/props/func_dep_test.go b/pkg/sql/opt/props/func_dep_test.go index ef156b8137de..e162f8c3d729 100644 --- a/pkg/sql/opt/props/func_dep_test.go +++ b/pkg/sql/opt/props/func_dep_test.go @@ -1192,6 +1192,22 @@ func TestFuncDeps_MakeLeftOuter(t *testing.T) { verifyFD(t, &loj, "(1)-->(2-5), (2,3)~~>(1,4,5), (1,12,13)-->(14)") loj.MakeLeftOuter(abcde, &props.FuncDepSet{}, preservedCols, nullExtendedCols, c(1)) verifyFD(t, &loj, "(1)-->(2-5), (2,3)~~>(1,4,5)") + + // Special case where left side has at most one row. + abcde = makeAbcdeFD(t) + abcde.MakeMax1Row(abcde.ColSet()) + verifyFD(t, abcde, "key(); ()-->(1-5)") + mnpq = makeMnpqFD(t) + verifyFD(t, mnpq, "key(10,11); (10,11)-->(12,13)") + filterFDs := &props.FuncDepSet{} + filterFDs.AddEquivalency(2, 12) + filterFDs.AddSynthesizedCol(c(1, 2, 3), 13) + loj.CopyFrom(abcde) + loj.MakeProduct(mnpq) + verifyFD(t, &loj, "key(10,11); ()-->(1-5), (10,11)-->(12,13)") + loj.MakeLeftOuter(abcde, filterFDs, abcde.ColSet(), mnpq.ColSet(), c()) + // 12 and 13 should be constant columns. + verifyFD(t, &loj, "key(10,11); ()-->(1-5,12,13)") } func TestFuncDeps_MakeFullOuter(t *testing.T) { diff --git a/pkg/sql/opt/xform/testdata/external/hibernate b/pkg/sql/opt/xform/testdata/external/hibernate index 7e6ca57106ef..31e2e74f7cf9 100644 --- a/pkg/sql/opt/xform/testdata/external/hibernate +++ b/pkg/sql/opt/xform/testdata/external/hibernate @@ -1998,21 +1998,21 @@ project ├── columns: customer1_1_0_:1!null ordernum2_1_0_:2!null orderdat3_1_0_:3!null formula101_0_:26 customer1_2_1_:6 ordernum2_2_1_:7 producti3_2_1_:8 customer1_2_2_:6 ordernum2_2_2_:7 producti3_2_2_:8 quantity4_2_2_:9 ├── immutable ├── key: (8) - ├── fd: ()-->(1-3), (8)-->(6,7,9,26) + ├── fd: ()-->(1-3,6,7), (8)-->(9,26) ├── group-by │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 sum:25 │ ├── grouping columns: lineitems1_.productid:8 │ ├── immutable │ ├── key: (8) - │ ├── fd: ()-->(1-3), (8)-->(1-3,6,7,9,25) + │ ├── fd: ()-->(1-3,6,7), (8)-->(1-3,6,7,9,25) │ ├── project │ │ ├── columns: column24:24 order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 li.customerid:12 li.ordernumber:13 │ │ ├── immutable - │ │ ├── fd: ()-->(1-3), (8)-->(6,7,9) + │ │ ├── fd: ()-->(1-3,6,7), (8)-->(9) │ │ ├── right-join (hash) │ │ │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 li.customerid:12 li.ordernumber:13 li.productid:14 li.quantity:15 p.productid:18 cost:20 │ │ │ ├── key: (8,12,13,18) - │ │ │ ├── fd: ()-->(1-3), (8)-->(6,7,9), (12-14)-->(15), (18)-->(20), (14)==(18), (18)==(14) + │ │ │ ├── fd: ()-->(1-3,6,7), (8)-->(9), (12-14)-->(15), (18)-->(20), (14)==(18), (18)==(14) │ │ │ ├── inner-join (hash) │ │ │ │ ├── columns: li.customerid:12!null li.ordernumber:13!null li.productid:14!null li.quantity:15 p.productid:18!null cost:20 │ │ │ │ ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more) @@ -2033,7 +2033,7 @@ project │ │ │ │ ├── left ordering: +1,+2 │ │ │ │ ├── right ordering: +6,+7 │ │ │ │ ├── key: (8) - │ │ │ │ ├── fd: ()-->(1-3), (8)-->(6,7,9) + │ │ │ │ ├── fd: ()-->(1-3,6,7), (8)-->(9) │ │ │ │ ├── scan customerorder [as=order0_] │ │ │ │ │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null │ │ │ │ │ ├── constraint: /1/2: [/'c111'/0 - /'c111'/0] @@ -2294,21 +2294,21 @@ project ├── columns: customer1_1_0_:1!null ordernum2_1_0_:2!null orderdat3_1_0_:3!null formula105_0_:26 customer1_2_1_:6 ordernum2_2_1_:7 producti3_2_1_:8 customer1_2_2_:6 ordernum2_2_2_:7 producti3_2_2_:8 quantity4_2_2_:9 ├── immutable ├── key: (8) - ├── fd: ()-->(1-3), (8)-->(6,7,9,26) + ├── fd: ()-->(1-3,6,7), (8)-->(9,26) ├── group-by │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 sum:25 │ ├── grouping columns: lineitems1_.productid:8 │ ├── immutable │ ├── key: (8) - │ ├── fd: ()-->(1-3), (8)-->(1-3,6,7,9,25) + │ ├── fd: ()-->(1-3,6,7), (8)-->(1-3,6,7,9,25) │ ├── project │ │ ├── columns: column24:24 order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 li.customerid:12 li.ordernumber:13 │ │ ├── immutable - │ │ ├── fd: ()-->(1-3), (8)-->(6,7,9) + │ │ ├── fd: ()-->(1-3,6,7), (8)-->(9) │ │ ├── right-join (hash) │ │ │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 li.customerid:12 li.ordernumber:13 li.productid:14 li.quantity:15 p.productid:18 cost:20 │ │ │ ├── key: (8,12,13,18) - │ │ │ ├── fd: ()-->(1-3), (8)-->(6,7,9), (12-14)-->(15), (18)-->(20), (14)==(18), (18)==(14) + │ │ │ ├── fd: ()-->(1-3,6,7), (8)-->(9), (12-14)-->(15), (18)-->(20), (14)==(18), (18)==(14) │ │ │ ├── inner-join (hash) │ │ │ │ ├── columns: li.customerid:12!null li.ordernumber:13!null li.productid:14!null li.quantity:15 p.productid:18!null cost:20 │ │ │ │ ├── multiplicity: left-rows(zero-or-one), right-rows(zero-or-more) @@ -2329,7 +2329,7 @@ project │ │ │ │ ├── left ordering: +1,+2 │ │ │ │ ├── right ordering: +6,+7 │ │ │ │ ├── key: (8) - │ │ │ │ ├── fd: ()-->(1-3), (8)-->(6,7,9) + │ │ │ │ ├── fd: ()-->(1-3,6,7), (8)-->(9) │ │ │ │ ├── scan customerorder [as=order0_] │ │ │ │ │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null │ │ │ │ │ ├── constraint: /1/2: [/'c111'/0 - /'c111'/0] diff --git a/pkg/sql/opt/xform/testdata/external/nova b/pkg/sql/opt/xform/testdata/external/nova index 0de68ba1491d..b8d964df75c8 100644 --- a/pkg/sql/opt/xform/testdata/external/nova +++ b/pkg/sql/opt/xform/testdata/external/nova @@ -161,20 +161,20 @@ project ├── columns: anon_1_flavors_created_at:14 anon_1_flavors_updated_at:15 anon_1_flavors_id:1!null anon_1_flavors_name:2!null anon_1_flavors_memory_mb:3!null anon_1_flavors_vcpus:4!null anon_1_flavors_root_gb:5 anon_1_flavors_ephemeral_gb:6 anon_1_flavors_flavorid:7!null anon_1_flavors_swap:8!null anon_1_flavors_rxtx_factor:9 anon_1_flavors_vcpu_weight:10 anon_1_flavors_disabled:11 anon_1_flavors_is_public:12 flavor_extra_specs_1_created_at:33 flavor_extra_specs_1_updated_at:34 flavor_extra_specs_1_id:29 flavor_extra_specs_1_key:30 flavor_extra_specs_1_value:31 flavor_extra_specs_1_flavor_id:32 ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) └── left-join (lookup flavor_extra_specs [as=flavor_extra_specs_1]) ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 value:31 flavor_extra_specs_1.flavor_id:32 flavor_extra_specs_1.created_at:33 flavor_extra_specs_1.updated_at:34 ├── key columns: [29] = [29] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15,27), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) ├── left-join (lookup flavor_extra_specs@flavor_extra_specs_flavor_id_key_idx [as=flavor_extra_specs_1]) │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 flavor_extra_specs_1.flavor_id:32 │ ├── key columns: [1] = [32] │ ├── immutable, has-placeholder │ ├── key: (29) - │ ├── fd: ()-->(1-12,14,15,27), (29)-->(30,32), (30,32)-->(29) + │ ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30), (30)-->(29) │ ├── limit │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ ├── cardinality: [0 - 1] @@ -945,20 +945,20 @@ project ├── columns: anon_1_instance_types_created_at:15 anon_1_instance_types_updated_at:16 anon_1_instance_types_deleted_at:14 anon_1_instance_types_deleted:13!null anon_1_instance_types_id:1!null anon_1_instance_types_name:2!null anon_1_instance_types_memory_mb:3!null anon_1_instance_types_vcpus:4!null anon_1_instance_types_root_gb:5 anon_1_instance_types_ephemeral_gb:6 anon_1_instance_types_flavorid:7 anon_1_instance_types_swap:8!null anon_1_instance_types_rxtx_factor:9 anon_1_instance_types_vcpu_weight:10 anon_1_instance_types_disabled:11 anon_1_instance_types_is_public:12 instance_type_extra_specs_1_created_at:38 instance_type_extra_specs_1_updated_at:39 instance_type_extra_specs_1_deleted_at:37 instance_type_extra_specs_1_deleted:36 instance_type_extra_specs_1_id:32 instance_type_extra_specs_1_key:33 instance_type_extra_specs_1_value:34 instance_type_extra_specs_1_instance_type_id:35 ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) └── left-join (lookup instance_type_extra_specs [as=instance_type_extra_specs_1]) ├── columns: instance_types.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 value:34 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 instance_type_extra_specs_1.deleted_at:37 instance_type_extra_specs_1.created_at:38 instance_type_extra_specs_1.updated_at:39 ├── key columns: [32] = [32] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16,30), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,30,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) ├── left-join (lookup instance_type_extra_specs@instance_type_extra_specs_instance_type_id_key_deleted_key [as=instance_type_extra_specs_1]) │ ├── columns: instance_types.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 │ ├── key columns: [1] = [35] │ ├── immutable, has-placeholder │ ├── key: (32) - │ ├── fd: ()-->(1-16,30), (32)-->(33,35,36), (33,35,36)~~>(32) + │ ├── fd: ()-->(1-16,30,35,36), (32)-->(33), (33,35,36)~~>(32) │ ├── limit │ │ ├── columns: instance_types.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ ├── cardinality: [0 - 1] @@ -1125,20 +1125,20 @@ project ├── columns: anon_1_instance_types_created_at:15 anon_1_instance_types_updated_at:16 anon_1_instance_types_deleted_at:14 anon_1_instance_types_deleted:13!null anon_1_instance_types_id:1!null anon_1_instance_types_name:2 anon_1_instance_types_memory_mb:3!null anon_1_instance_types_vcpus:4!null anon_1_instance_types_root_gb:5 anon_1_instance_types_ephemeral_gb:6 anon_1_instance_types_flavorid:7 anon_1_instance_types_swap:8!null anon_1_instance_types_rxtx_factor:9 anon_1_instance_types_vcpu_weight:10 anon_1_instance_types_disabled:11 anon_1_instance_types_is_public:12 instance_type_extra_specs_1_created_at:38 instance_type_extra_specs_1_updated_at:39 instance_type_extra_specs_1_deleted_at:37 instance_type_extra_specs_1_deleted:36 instance_type_extra_specs_1_id:32 instance_type_extra_specs_1_key:33 instance_type_extra_specs_1_value:34 instance_type_extra_specs_1_instance_type_id:35 ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) └── left-join (lookup instance_type_extra_specs [as=instance_type_extra_specs_1]) ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 value:34 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 instance_type_extra_specs_1.deleted_at:37 instance_type_extra_specs_1.created_at:38 instance_type_extra_specs_1.updated_at:39 ├── key columns: [32] = [32] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16,30), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,30,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) ├── left-join (lookup instance_type_extra_specs@instance_type_extra_specs_instance_type_id_key_deleted_key [as=instance_type_extra_specs_1]) │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 │ ├── key columns: [1] = [35] │ ├── immutable, has-placeholder │ ├── key: (32) - │ ├── fd: ()-->(1-16,30), (32)-->(33,35,36), (33,35,36)~~>(32) + │ ├── fd: ()-->(1-16,30,35,36), (32)-->(33), (33,35,36)~~>(32) │ ├── limit │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ ├── cardinality: [0 - 1] @@ -1290,20 +1290,20 @@ project ├── columns: anon_1_flavors_created_at:14 anon_1_flavors_updated_at:15 anon_1_flavors_id:1!null anon_1_flavors_name:2!null anon_1_flavors_memory_mb:3!null anon_1_flavors_vcpus:4!null anon_1_flavors_root_gb:5 anon_1_flavors_ephemeral_gb:6 anon_1_flavors_flavorid:7!null anon_1_flavors_swap:8!null anon_1_flavors_rxtx_factor:9 anon_1_flavors_vcpu_weight:10 anon_1_flavors_disabled:11 anon_1_flavors_is_public:12 flavor_extra_specs_1_created_at:33 flavor_extra_specs_1_updated_at:34 flavor_extra_specs_1_id:29 flavor_extra_specs_1_key:30 flavor_extra_specs_1_value:31 flavor_extra_specs_1_flavor_id:32 ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) └── left-join (lookup flavor_extra_specs [as=flavor_extra_specs_1]) ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 value:31 flavor_extra_specs_1.flavor_id:32 flavor_extra_specs_1.created_at:33 flavor_extra_specs_1.updated_at:34 ├── key columns: [29] = [29] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15,27), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) ├── left-join (lookup flavor_extra_specs@flavor_extra_specs_flavor_id_key_idx [as=flavor_extra_specs_1]) │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 flavor_extra_specs_1.flavor_id:32 │ ├── key columns: [1] = [32] │ ├── immutable, has-placeholder │ ├── key: (29) - │ ├── fd: ()-->(1-12,14,15,27), (29)-->(30,32), (30,32)-->(29) + │ ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30), (30)-->(29) │ ├── limit │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ ├── cardinality: [0 - 1] @@ -1447,20 +1447,20 @@ project ├── columns: anon_1_flavors_created_at:14 anon_1_flavors_updated_at:15 anon_1_flavors_id:1!null anon_1_flavors_name:2!null anon_1_flavors_memory_mb:3!null anon_1_flavors_vcpus:4!null anon_1_flavors_root_gb:5 anon_1_flavors_ephemeral_gb:6 anon_1_flavors_flavorid:7!null anon_1_flavors_swap:8!null anon_1_flavors_rxtx_factor:9 anon_1_flavors_vcpu_weight:10 anon_1_flavors_disabled:11 anon_1_flavors_is_public:12 flavor_extra_specs_1_created_at:33 flavor_extra_specs_1_updated_at:34 flavor_extra_specs_1_id:29 flavor_extra_specs_1_key:30 flavor_extra_specs_1_value:31 flavor_extra_specs_1_flavor_id:32 ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) └── left-join (lookup flavor_extra_specs [as=flavor_extra_specs_1]) ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 value:31 flavor_extra_specs_1.flavor_id:32 flavor_extra_specs_1.created_at:33 flavor_extra_specs_1.updated_at:34 ├── key columns: [29] = [29] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15,27), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) ├── left-join (lookup flavor_extra_specs@flavor_extra_specs_flavor_id_key_idx [as=flavor_extra_specs_1]) │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 flavor_extra_specs_1.flavor_id:32 │ ├── key columns: [1] = [32] │ ├── immutable, has-placeholder │ ├── key: (29) - │ ├── fd: ()-->(1-12,14,15,27), (29)-->(30,32), (30,32)-->(29) + │ ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30), (30)-->(29) │ ├── limit │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ ├── cardinality: [0 - 1] @@ -1995,20 +1995,20 @@ project ├── columns: anon_1_instance_types_created_at:15 anon_1_instance_types_updated_at:16 anon_1_instance_types_deleted_at:14 anon_1_instance_types_deleted:13!null anon_1_instance_types_id:1!null anon_1_instance_types_name:2 anon_1_instance_types_memory_mb:3!null anon_1_instance_types_vcpus:4!null anon_1_instance_types_root_gb:5 anon_1_instance_types_ephemeral_gb:6 anon_1_instance_types_flavorid:7!null anon_1_instance_types_swap:8!null anon_1_instance_types_rxtx_factor:9 anon_1_instance_types_vcpu_weight:10 anon_1_instance_types_disabled:11 anon_1_instance_types_is_public:12 instance_type_extra_specs_1_created_at:38 instance_type_extra_specs_1_updated_at:39 instance_type_extra_specs_1_deleted_at:37 instance_type_extra_specs_1_deleted:36 instance_type_extra_specs_1_id:32 instance_type_extra_specs_1_key:33 instance_type_extra_specs_1_value:34 instance_type_extra_specs_1_instance_type_id:35 ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) └── left-join (lookup instance_type_extra_specs [as=instance_type_extra_specs_1]) ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 value:34 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 instance_type_extra_specs_1.deleted_at:37 instance_type_extra_specs_1.created_at:38 instance_type_extra_specs_1.updated_at:39 ├── key columns: [32] = [32] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16,30), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,30,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) ├── left-join (lookup instance_type_extra_specs@instance_type_extra_specs_instance_type_id_key_deleted_key [as=instance_type_extra_specs_1]) │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 │ ├── key columns: [1] = [35] │ ├── immutable, has-placeholder │ ├── key: (32) - │ ├── fd: ()-->(1-16,30), (32)-->(33,35,36), (33,35,36)~~>(32) + │ ├── fd: ()-->(1-16,30,35,36), (32)-->(33), (33,35,36)~~>(32) │ ├── limit │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ ├── cardinality: [0 - 1] @@ -2973,20 +2973,20 @@ project ├── columns: anon_1_instance_types_created_at:15 anon_1_instance_types_updated_at:16 anon_1_instance_types_deleted_at:14 anon_1_instance_types_deleted:13!null anon_1_instance_types_id:1!null anon_1_instance_types_name:2 anon_1_instance_types_memory_mb:3!null anon_1_instance_types_vcpus:4!null anon_1_instance_types_root_gb:5 anon_1_instance_types_ephemeral_gb:6 anon_1_instance_types_flavorid:7!null anon_1_instance_types_swap:8!null anon_1_instance_types_rxtx_factor:9 anon_1_instance_types_vcpu_weight:10 anon_1_instance_types_disabled:11 anon_1_instance_types_is_public:12 instance_type_extra_specs_1_created_at:38 instance_type_extra_specs_1_updated_at:39 instance_type_extra_specs_1_deleted_at:37 instance_type_extra_specs_1_deleted:36 instance_type_extra_specs_1_id:32 instance_type_extra_specs_1_key:33 instance_type_extra_specs_1_value:34 instance_type_extra_specs_1_instance_type_id:35 ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) └── left-join (lookup instance_type_extra_specs [as=instance_type_extra_specs_1]) ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 value:34 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 instance_type_extra_specs_1.deleted_at:37 instance_type_extra_specs_1.created_at:38 instance_type_extra_specs_1.updated_at:39 ├── key columns: [32] = [32] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (32) - ├── fd: ()-->(1-16,30), (32)-->(33-39), (33,35,36)~~>(32,34,37-39) + ├── fd: ()-->(1-16,30,35), (32)-->(33,34,36-39), (33,35,36)~~>(32,34,37-39) ├── left-join (lookup instance_type_extra_specs@instance_type_extra_specs_instance_type_id_key_deleted_key [as=instance_type_extra_specs_1]) │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 instance_type_extra_specs_1.id:32 key:33 instance_type_extra_specs_1.instance_type_id:35 instance_type_extra_specs_1.deleted:36 │ ├── key columns: [1] = [35] │ ├── immutable, has-placeholder │ ├── key: (32) - │ ├── fd: ()-->(1-16,30), (32)-->(33,35,36), (33,35,36)~~>(32) + │ ├── fd: ()-->(1-16,30,35,36), (32)-->(33), (33,35,36)~~>(32) │ ├── limit │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ ├── cardinality: [0 - 1] @@ -3144,20 +3144,20 @@ project ├── columns: anon_1_flavors_created_at:14 anon_1_flavors_updated_at:15 anon_1_flavors_id:1!null anon_1_flavors_name:2!null anon_1_flavors_memory_mb:3!null anon_1_flavors_vcpus:4!null anon_1_flavors_root_gb:5 anon_1_flavors_ephemeral_gb:6 anon_1_flavors_flavorid:7!null anon_1_flavors_swap:8!null anon_1_flavors_rxtx_factor:9 anon_1_flavors_vcpu_weight:10 anon_1_flavors_disabled:11 anon_1_flavors_is_public:12 flavor_extra_specs_1_created_at:33 flavor_extra_specs_1_updated_at:34 flavor_extra_specs_1_id:29 flavor_extra_specs_1_key:30 flavor_extra_specs_1_value:31 flavor_extra_specs_1_flavor_id:32 ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) └── left-join (lookup flavor_extra_specs [as=flavor_extra_specs_1]) ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 value:31 flavor_extra_specs_1.flavor_id:32 flavor_extra_specs_1.created_at:33 flavor_extra_specs_1.updated_at:34 ├── key columns: [29] = [29] ├── lookup columns are key ├── immutable, has-placeholder ├── key: (29) - ├── fd: ()-->(1-12,14,15,27), (29)-->(30-34), (30,32)-->(29,31,33,34) + ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30,31,33,34), (30)-->(29,31,33,34) ├── left-join (lookup flavor_extra_specs@flavor_extra_specs_flavor_id_key_idx [as=flavor_extra_specs_1]) │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 flavor_extra_specs_1.id:29 key:30 flavor_extra_specs_1.flavor_id:32 │ ├── key columns: [1] = [32] │ ├── immutable, has-placeholder │ ├── key: (29) - │ ├── fd: ()-->(1-12,14,15,27), (29)-->(30,32), (30,32)-->(29) + │ ├── fd: ()-->(1-12,14,15,27,32), (29)-->(30), (30)-->(29) │ ├── limit │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ ├── cardinality: [0 - 1] From 8cff49b3abd5613ccd73f22439fb9c2f79550780 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Wed, 20 Oct 2021 19:00:34 -0700 Subject: [PATCH 016/205] opt: improve FDs for non-trivial equalities This change implements a minor improvement to FDs: extract a functional dependency from equalities of the form `x = `. The logic is the same with that creating FDs for a projection of that expression: the expression must be non-volatile and composite-insensitive. Release note: None --- pkg/sql/opt/memo/logical_props_builder.go | 18 +++ pkg/sql/opt/memo/testdata/logprops/join | 57 +++++++++ pkg/sql/opt/memo/testdata/logprops/select | 118 +++++++++++++++++++ pkg/sql/opt/norm/testdata/rules/join | 6 +- pkg/sql/opt/norm/testdata/rules/prune_cols | 2 +- pkg/sql/opt/norm/testdata/rules/side_effects | 8 +- pkg/sql/opt/optgen/exprgen/testdata/join | 2 +- 7 files changed, 202 insertions(+), 9 deletions(-) diff --git a/pkg/sql/opt/memo/logical_props_builder.go b/pkg/sql/opt/memo/logical_props_builder.go index 1367102adac8..8899044921db 100644 --- a/pkg/sql/opt/memo/logical_props_builder.go +++ b/pkg/sql/opt/memo/logical_props_builder.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/opt/props" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" ) @@ -1509,6 +1510,20 @@ func (b *logicalPropsBuilder) buildFiltersItemProps(item *FiltersItem, scalar *p // Filter conjunct of the form x = $1. This filter cannot generate // constraints, but still tell us that the column is constant. constCols.Add(leftVar.Col) + + default: + // We have an equality of the form + // x = . + // If the expression is non-volatile and is not composite sensitive, + // then x is functionally determined by the columns that appear in the + // expression. + if !scalar.VolatilitySet.HasVolatile() && + !CanBeCompositeSensitive(b.mem.Metadata(), eq.Right) { + outerCols := getOuterCols(eq.Right) + if !outerCols.Contains(leftVar.Col) { + scalar.FuncDeps.AddSynthesizedCol(getOuterCols(eq.Right), leftVar.Col) + } + } } } } @@ -1912,6 +1927,9 @@ func (b *logicalPropsBuilder) rejectNullCols(filters FiltersExpr) opt.ColSet { func (b *logicalPropsBuilder) addFiltersToFuncDep(filters FiltersExpr, fdset *props.FuncDepSet) { for i := range filters { filterProps := filters[i].ScalarProps() + if util.CrdbTestBuild && !filterProps.Populated { + panic(errors.AssertionFailedf("filter properties not populated")) + } fdset.AddFrom(&filterProps.FuncDeps) } diff --git a/pkg/sql/opt/memo/testdata/logprops/join b/pkg/sql/opt/memo/testdata/logprops/join index f1a9755e1035..801948b3ad2b 100644 --- a/pkg/sql/opt/memo/testdata/logprops/join +++ b/pkg/sql/opt/memo/testdata/logprops/join @@ -332,6 +332,63 @@ project ├── variable: y:2 [type=int] └── variable: u:7 [type=int] +# Left-join with single row left input: columns u and v should be constant. +# Note: we need norm here so that the AND gets normalized to multiple filter items. +norm +SELECT * FROM (SELECT * FROM xysd WHERE x=123) AS xysd1 LEFT JOIN uv ON y=u AND v=x+y +---- +project + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) u:7(int) v:8(int) + ├── immutable + ├── fd: ()-->(1-4,7,8) + ├── prune: (1-4,7,8) + ├── reject-nulls: (7,8) + └── left-join (hash) + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) u:7(int) v:8(int) column12:12(int) + ├── multiplicity: left-rows(one-or-more), right-rows(zero-or-one) + ├── immutable + ├── fd: ()-->(1-4,7,8,12) + ├── prune: (1,3,4) + ├── reject-nulls: (7,8) + ├── project + │ ├── columns: column12:12(int) x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) + │ ├── cardinality: [0 - 1] + │ ├── immutable + │ ├── key: () + │ ├── fd: ()-->(1-4,12) + │ ├── prune: (1-4,12) + │ ├── select + │ │ ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(1-4) + │ │ ├── prune: (2-4) + │ │ ├── scan xysd + │ │ │ ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) + │ │ │ ├── key: (1) + │ │ │ ├── fd: (1)-->(2-4), (3,4)~~>(1,2) + │ │ │ ├── prune: (1-4) + │ │ │ └── interesting orderings: (+1) (-3,+4,+1) + │ │ └── filters + │ │ └── eq [type=bool, outer=(1), constraints=(/1: [/123 - /123]; tight), fd=()-->(1)] + │ │ ├── variable: x:1 [type=int] + │ │ └── const: 123 [type=int] + │ └── projections + │ └── plus [as=column12:12, type=int, outer=(1,2), immutable] + │ ├── variable: x:1 [type=int] + │ └── variable: y:2 [type=int] + ├── scan uv + │ ├── columns: u:7(int) v:8(int!null) + │ ├── prune: (7,8) + │ └── unfiltered-cols: (7-11) + └── filters + ├── eq [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ]), fd=(2)==(7), (7)==(2)] + │ ├── variable: y:2 [type=int] + │ └── variable: u:7 [type=int] + └── eq [type=bool, outer=(8,12), constraints=(/8: (/NULL - ]; /12: (/NULL - ]), fd=(8)==(12), (12)==(8)] + ├── variable: column12:12 [type=int] + └── variable: v:8 [type=int] + # Left-join-apply. opt SELECT * FROM xysd WHERE (SELECT u FROM uv WHERE u=x) IS NULL diff --git a/pkg/sql/opt/memo/testdata/logprops/select b/pkg/sql/opt/memo/testdata/logprops/select index 18f72020dc34..4942259ef77e 100644 --- a/pkg/sql/opt/memo/testdata/logprops/select +++ b/pkg/sql/opt/memo/testdata/logprops/select @@ -10,6 +10,10 @@ exec-ddl CREATE TABLE ab (a INT, b INT) ---- +exec-ddl +CREATE TABLE abcde (a INT, b INT, c INT, d DECIMAL, e STRING) +---- + build SELECT * FROM xy WHERE x=1 ---- @@ -480,3 +484,117 @@ select └── eq [type=bool, outer=(1,2), constraints=(/1: (/NULL - ]; /2: (/NULL - ]), fd=(1)==(2), (2)==(1)] ├── variable: a:1 [type=int] └── variable: b:2 [type=int] + +# Here we have the FD (b,c)==>(a). +build +SELECT * FROM abcde WHERE a=b+c +---- +project + ├── columns: a:1(int!null) b:2(int) c:3(int) d:4(decimal) e:5(string) + ├── immutable + ├── fd: (2,3)-->(1) + ├── prune: (1-5) + └── select + ├── columns: a:1(int!null) b:2(int) c:3(int) d:4(decimal) e:5(string) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + ├── immutable + ├── key: (6) + ├── fd: (6)-->(1-5,7,8), (2,3)-->(1) + ├── prune: (4-8) + ├── interesting orderings: (+6) + ├── scan abcde + │ ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + │ ├── key: (6) + │ ├── fd: (6)-->(1-5,7,8) + │ ├── prune: (1-8) + │ └── interesting orderings: (+6) + └── filters + └── eq [type=bool, outer=(1-3), immutable, constraints=(/1: (/NULL - ]), fd=(2,3)-->(1)] + ├── variable: a:1 [type=int] + └── plus [type=int] + ├── variable: b:2 [type=int] + └── variable: c:3 [type=int] + +# No FD from the equality. +build +SELECT * FROM abcde WHERE a=a/2+b +---- +project + ├── columns: a:1(int!null) b:2(int) c:3(int) d:4(decimal) e:5(string) + ├── immutable + ├── prune: (1-5) + └── select + ├── columns: a:1(int!null) b:2(int) c:3(int) d:4(decimal) e:5(string) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + ├── immutable + ├── key: (6) + ├── fd: (6)-->(1-5,7,8) + ├── prune: (3-8) + ├── interesting orderings: (+6) + ├── scan abcde + │ ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + │ ├── key: (6) + │ ├── fd: (6)-->(1-5,7,8) + │ ├── prune: (1-8) + │ └── interesting orderings: (+6) + └── filters + └── eq [type=bool, outer=(1,2), immutable, constraints=(/1: (/NULL - ])] + ├── variable: a:1 [type=int] + └── plus [type=decimal] + ├── div [type=decimal] + │ ├── variable: a:1 [type=int] + │ └── const: 2 [type=int] + └── variable: b:2 [type=int] + +# (b)==>(e). +build +SELECT * FROM abcde WHERE e=b::string +---- +project + ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string!null) + ├── immutable + ├── fd: (2)-->(5) + ├── prune: (1-5) + └── select + ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string!null) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + ├── immutable + ├── key: (6) + ├── fd: (6)-->(1-5,7,8), (2)-->(5) + ├── prune: (1,3,4,6-8) + ├── interesting orderings: (+6) + ├── scan abcde + │ ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + │ ├── key: (6) + │ ├── fd: (6)-->(1-5,7,8) + │ ├── prune: (1-8) + │ └── interesting orderings: (+6) + └── filters + └── eq [type=bool, outer=(2,5), immutable, constraints=(/5: (/NULL - ]), fd=(2)-->(5)] + ├── variable: e:5 [type=string] + └── cast: STRING [type=string] + └── variable: b:2 [type=int] + +# We don't want (d)==>(e), it would not be correct (consider 1.0 and 1.00). +build +SELECT * FROM abcde WHERE e=d::string +---- +project + ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string!null) + ├── immutable + ├── prune: (1-5) + └── select + ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string!null) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + ├── immutable + ├── key: (6) + ├── fd: (6)-->(1-5,7,8) + ├── prune: (1-3,6-8) + ├── interesting orderings: (+6) + ├── scan abcde + │ ├── columns: a:1(int) b:2(int) c:3(int) d:4(decimal) e:5(string) rowid:6(int!null) crdb_internal_mvcc_timestamp:7(decimal) tableoid:8(oid) + │ ├── key: (6) + │ ├── fd: (6)-->(1-5,7,8) + │ ├── prune: (1-8) + │ └── interesting orderings: (+6) + └── filters + └── eq [type=bool, outer=(4,5), immutable, constraints=(/5: (/NULL - ])] + ├── variable: e:5 [type=string] + └── cast: STRING [type=string] + └── variable: d:4 [type=decimal] diff --git a/pkg/sql/opt/norm/testdata/rules/join b/pkg/sql/opt/norm/testdata/rules/join index 053fd9692d8c..66787616848b 100644 --- a/pkg/sql/opt/norm/testdata/rules/join +++ b/pkg/sql/opt/norm/testdata/rules/join @@ -908,7 +908,7 @@ project ├── columns: k:1!null i:2 f:3!null s:4 j:5 x:8!null y:9 "?column?":16 ├── immutable ├── key: (8) - ├── fd: (1)-->(2-5), (1,8)-->(9,16), (1)==(8), (8)==(1) + ├── fd: (1)-->(2-5), (1,8)-->(9,16), (1)==(8), (8)==(1), (8)-->(16) ├── scan a │ ├── columns: k:1!null i:2 f:3!null s:4 j:5 │ ├── key: (1) @@ -946,7 +946,7 @@ project │ └── "?column?":16 └── filters ├── k:1 = x:8 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)] - └── "?column?":16 = (x:8 * i:2) [outer=(2,8,16), immutable, constraints=(/16: (/NULL - ])] + └── "?column?":16 = (x:8 * i:2) [outer=(2,8,16), immutable, constraints=(/16: (/NULL - ]), fd=(2,8)-->(16)] # Ensure that we do not map filters for types with composite key encoding. norm expect-not=(PushFilterIntoJoinLeftAndRight,MapFilterIntoJoinLeft,MapFilterIntoJoinRight) @@ -3196,7 +3196,7 @@ project │ ├── key: (5) │ └── fd: (5)-->(6) └── filters - ├── v:6 = (x:1 + u:5) [outer=(1,5,6), immutable, constraints=(/6: (/NULL - ])] + ├── v:6 = (x:1 + u:5) [outer=(1,5,6), immutable, constraints=(/6: (/NULL - ]), fd=(1,5)-->(6)] └── column9:9 = u:5 [outer=(5,9), constraints=(/5: (/NULL - ]; /9: (/NULL - ]), fd=(5)==(9), (9)==(5)] # Cases with non-extractable equality. diff --git a/pkg/sql/opt/norm/testdata/rules/prune_cols b/pkg/sql/opt/norm/testdata/rules/prune_cols index f212e189a068..3a1961d388c5 100644 --- a/pkg/sql/opt/norm/testdata/rules/prune_cols +++ b/pkg/sql/opt/norm/testdata/rules/prune_cols @@ -455,7 +455,7 @@ project │ └── projections │ └── a.f:3 / 2.0 [as=f:7, outer=(3)] └── filters - └── f:7 = i:2::FLOAT8 [outer=(2,7), immutable, constraints=(/7: (/NULL - ])] + └── f:7 = i:2::FLOAT8 [outer=(2,7), immutable, constraints=(/7: (/NULL - ]), fd=(2)-->(7)] # Detect PruneSelectCols and PushSelectIntoProject dependency cycle. norm diff --git a/pkg/sql/opt/norm/testdata/rules/side_effects b/pkg/sql/opt/norm/testdata/rules/side_effects index ff509698606a..b00d8be00483 100644 --- a/pkg/sql/opt/norm/testdata/rules/side_effects +++ b/pkg/sql/opt/norm/testdata/rules/side_effects @@ -137,7 +137,7 @@ project └── select ├── columns: k:1!null i:2 f:3 s:4 j:5 x:8 y:9 ├── key: (1) - ├── fd: (1)-->(2-5,8,9), (8)-->(9) + ├── fd: (1)-->(2-5,8,9), (8)-->(9), (2,9)-->(1) ├── left-join (hash) │ ├── columns: k:1!null i:2 f:3 s:4 j:5 x:8 y:9 │ ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more) @@ -154,7 +154,7 @@ project │ └── filters │ └── x:8 = i:2 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)] └── filters - └── k:1 = CASE WHEN i:2 < 0 THEN 5 ELSE y:9 END [outer=(1,2,9), constraints=(/1: (/NULL - ])] + └── k:1 = CASE WHEN i:2 < 0 THEN 5 ELSE y:9 END [outer=(1,2,9), constraints=(/1: (/NULL - ]), fd=(2,9)-->(1)] # Don't decorrelate CASE WHEN branch if there are side effects. norm @@ -302,7 +302,7 @@ project ├── columns: k:1!null i:2 f:3 s:4 j:5 x:8 y:9 ├── immutable ├── key: (1) - ├── fd: (1)-->(2-5,8,9), (8)-->(9) + ├── fd: (1)-->(2-5,8,9), (8)-->(9), (9)-->(1) ├── left-join (hash) │ ├── columns: k:1!null i:2 f:3 s:4 j:5 x:8 y:9 │ ├── multiplicity: left-rows(exactly-one), right-rows(zero-or-more) @@ -319,4 +319,4 @@ project │ └── filters │ └── x:8 = i:2 [outer=(2,8), constraints=(/2: (/NULL - ]; /8: (/NULL - ]), fd=(2)==(8), (8)==(2)] └── filters - └── k:1 = IFERROR((1 / 0)::INT8, y:9) [outer=(1,9), immutable, constraints=(/1: (/NULL - ])] + └── k:1 = IFERROR((1 / 0)::INT8, y:9) [outer=(1,9), immutable, constraints=(/1: (/NULL - ]), fd=(9)-->(1)] diff --git a/pkg/sql/opt/optgen/exprgen/testdata/join b/pkg/sql/opt/optgen/exprgen/testdata/join index ee48f079ed7d..e320a5a855fe 100644 --- a/pkg/sql/opt/optgen/exprgen/testdata/join +++ b/pkg/sql/opt/optgen/exprgen/testdata/join @@ -149,7 +149,7 @@ inner-join-apply │ │ ├── cost: 1094.72 │ │ └── prune: (7-9) │ └── filters - │ └── eq [type=bool, outer=(1,7,8), immutable, constraints=(/1: (/NULL - ])] + │ └── eq [type=bool, outer=(1,7,8), immutable, constraints=(/1: (/NULL - ]), fd=(7,8)-->(1)] │ ├── variable: t.public.abc.a:1 [type=int] │ └── plus [type=int] │ ├── variable: t.public.def.d:7 [type=int] From b4e20c52a07d967940dce91660f5ce1c2a0008b9 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Mon, 25 Oct 2021 10:20:18 -0700 Subject: [PATCH 017/205] cli: extract statement diagnostics code This commit extracts the part of the statment-diag code that interacts with the system tables and moves it to sqlclientconn. Release note: None --- pkg/cli/clisqlclient/BUILD.bazel | 1 + pkg/cli/clisqlclient/statement_diag.go | 275 +++++++++++++++++++++++++ pkg/cli/statement_diag.go | 202 ++---------------- pkg/cli/statement_diag_test.go | 5 +- 4 files changed, 300 insertions(+), 183 deletions(-) create mode 100644 pkg/cli/clisqlclient/statement_diag.go diff --git a/pkg/cli/clisqlclient/BUILD.bazel b/pkg/cli/clisqlclient/BUILD.bazel index e8c6ac801fc0..41fe97e0f784 100644 --- a/pkg/cli/clisqlclient/BUILD.bazel +++ b/pkg/cli/clisqlclient/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "make_query.go", "parse_bool.go", "rows.go", + "statement_diag.go", "string_to_duration.go", "txn_shim.go", ], diff --git a/pkg/cli/clisqlclient/statement_diag.go b/pkg/cli/clisqlclient/statement_diag.go new file mode 100644 index 000000000000..4b1a9e9e3008 --- /dev/null +++ b/pkg/cli/clisqlclient/statement_diag.go @@ -0,0 +1,275 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package clisqlclient + +import ( + "database/sql/driver" + "io" + "os" + "time" + + "github.com/cockroachdb/errors" +) + +// StmtDiagBundleInfo contains information about a statement diagnostics bundle +// that was collected. +type StmtDiagBundleInfo struct { + ID int64 + // Statement is the SQL statement fingerprint. + Statement string + CollectedAt time.Time +} + +// StmtDiagListBundles retrieves information about all available statement +// diagnostics bundles. +func StmtDiagListBundles(conn Conn) ([]StmtDiagBundleInfo, error) { + result, err := stmtDiagListBundlesInternal(conn) + if err != nil { + return nil, errors.Wrap( + err, "failed to retrieve statement diagnostics bundles", + ) + } + return result, nil +} + +func stmtDiagListBundlesInternal(conn Conn) ([]StmtDiagBundleInfo, error) { + rows, err := conn.Query( + `SELECT id, statement_fingerprint, collected_at + FROM system.statement_diagnostics + WHERE error IS NULL + ORDER BY collected_at DESC`, + nil, /* args */ + ) + if err != nil { + return nil, err + } + var result []StmtDiagBundleInfo + vals := make([]driver.Value, 3) + for { + if err := rows.Next(vals); err == io.EOF { + break + } else if err != nil { + return nil, err + } + info := StmtDiagBundleInfo{ + ID: vals[0].(int64), + Statement: vals[1].(string), + CollectedAt: vals[2].(time.Time), + } + result = append(result, info) + } + if err := rows.Close(); err != nil { + return nil, err + } + return result, nil +} + +// StmtDiagActivationRequest contains information about a statement diagnostics +// activation request. +type StmtDiagActivationRequest struct { + ID int64 + // Statement is the SQL statement fingerprint. + Statement string + RequestedAt time.Time +} + +// StmtDiagListOutstandingRequests retrieves outstanding statement diagnostics +// activation requests. +func StmtDiagListOutstandingRequests(conn Conn) ([]StmtDiagActivationRequest, error) { + result, err := stmtDiagListOutstandingRequestsInternal(conn) + if err != nil { + return nil, errors.Wrap( + err, "failed to retrieve outstanding statement diagnostics activation requests", + ) + } + return result, nil +} + +func stmtDiagListOutstandingRequestsInternal(conn Conn) ([]StmtDiagActivationRequest, error) { + rows, err := conn.Query( + `SELECT id, statement_fingerprint, requested_at + FROM system.statement_diagnostics_requests + WHERE NOT completed + ORDER BY requested_at DESC`, + nil, /* args */ + ) + if err != nil { + return nil, err + } + var result []StmtDiagActivationRequest + vals := make([]driver.Value, 3) + for { + if err := rows.Next(vals); err == io.EOF { + break + } else if err != nil { + return nil, err + } + info := StmtDiagActivationRequest{ + ID: vals[0].(int64), + Statement: vals[1].(string), + RequestedAt: vals[2].(time.Time), + } + result = append(result, info) + } + if err := rows.Close(); err != nil { + return nil, err + } + return result, nil +} + +// StmtDiagDownloadBundle downloads the bundle with the given ID to a file. +func StmtDiagDownloadBundle(conn Conn, id int64, filename string) error { + if err := stmtDiagDownloadBundleInternal(conn, id, filename); err != nil { + return errors.Wrapf( + err, "failed to download statement diagnostics bundle %d to file '%s'", id, filename, + ) + } + return nil +} + +func stmtDiagDownloadBundleInternal(conn Conn, id int64, filename string) error { + // Retrieve the chunk IDs; these are stored in an INT ARRAY column. + rows, err := conn.Query( + "SELECT unnest(bundle_chunks) FROM system.statement_diagnostics WHERE id = $1", + []driver.Value{id}, + ) + if err != nil { + return err + } + var chunkIDs []int64 + vals := make([]driver.Value, 1) + for { + if err := rows.Next(vals); err == io.EOF { + break + } else if err != nil { + return err + } + chunkIDs = append(chunkIDs, vals[0].(int64)) + } + if err := rows.Close(); err != nil { + return err + } + + if len(chunkIDs) == 0 { + return errors.Newf("no statement diagnostics bundle with ID %d", id) + } + + // Create the file and write out the chunks. + out, err := os.Create(filename) + if err != nil { + return err + } + + for _, chunkID := range chunkIDs { + data, err := conn.QueryRow( + "SELECT data FROM system.statement_bundle_chunks WHERE id = $1", + []driver.Value{chunkID}, + ) + if err != nil { + _ = out.Close() + return err + } + if _, err := out.Write(data[0].([]byte)); err != nil { + _ = out.Close() + return err + } + } + + return out.Close() +} + +// StmtDiagDeleteBundle deletes a statement diagnostics bundle. +func StmtDiagDeleteBundle(conn Conn, id int64) error { + _, err := conn.QueryRow( + "SELECT 1 FROM system.statement_diagnostics WHERE id = $1", + []driver.Value{id}, + ) + if err != nil { + if err == io.EOF { + return errors.Newf("no statement diagnostics bundle with ID %d", id) + } + return err + } + return conn.ExecTxn(func(conn TxBoundConn) error { + // Delete the request metadata. + if err := conn.Exec( + "DELETE FROM system.statement_diagnostics_requests WHERE statement_diagnostics_id = $1", + []driver.Value{id}, + ); err != nil { + return err + } + // Delete the bundle chunks. + if err := conn.Exec( + `DELETE FROM system.statement_bundle_chunks + WHERE id IN ( + SELECT unnest(bundle_chunks) FROM system.statement_diagnostics WHERE id = $1 + )`, + []driver.Value{id}, + ); err != nil { + return err + } + // Finally, delete the diagnostics entry. + return conn.Exec( + "DELETE FROM system.statement_diagnostics WHERE id = $1", + []driver.Value{id}, + ) + }) +} + +// StmtDiagDeleteAllBundles deletes all statement diagnostics bundles. +func StmtDiagDeleteAllBundles(conn Conn) error { + return conn.ExecTxn(func(conn TxBoundConn) error { + // Delete the request metadata. + if err := conn.Exec( + "DELETE FROM system.statement_diagnostics_requests WHERE completed", + nil, + ); err != nil { + return err + } + // Delete all bundle chunks. + if err := conn.Exec( + `DELETE FROM system.statement_bundle_chunks WHERE true`, + nil, + ); err != nil { + return err + } + // Finally, delete the diagnostics entry. + return conn.Exec( + "DELETE FROM system.statement_diagnostics WHERE true", + nil, + ) + }) +} + +// StmtDiagCancelOutstandingRequest deletes an outstanding statement diagnostics +// activation request. +func StmtDiagCancelOutstandingRequest(conn Conn, id int64) error { + _, err := conn.QueryRow( + "DELETE FROM system.statement_diagnostics_requests WHERE id = $1 RETURNING id", + []driver.Value{id}, + ) + if err != nil { + if err == io.EOF { + return errors.Newf("no outstanding activation request with ID %d", id) + } + return err + } + return nil +} + +// StmtDiagCancelAllOutstandingRequests deletes all outstanding statement +// diagnostics activation requests. +func StmtDiagCancelAllOutstandingRequests(conn Conn) error { + return conn.Exec( + "DELETE FROM system.statement_diagnostics_requests WHERE NOT completed", + nil, + ) +} diff --git a/pkg/cli/statement_diag.go b/pkg/cli/statement_diag.go index e87dc6c7977e..6cf08cffbedb 100644 --- a/pkg/cli/statement_diag.go +++ b/pkg/cli/statement_diag.go @@ -12,13 +12,9 @@ package cli import ( "bytes" - "database/sql/driver" "fmt" - "io" - "os" "strconv" "text/tabwriter" - "time" "github.com/cockroachdb/cockroach/pkg/cli/clierrorplus" "github.com/cockroachdb/cockroach/pkg/cli/clisqlclient" @@ -54,82 +50,42 @@ func runStmtDiagList(cmd *cobra.Command, args []string) (resErr error) { defer func() { resErr = errors.CombineErrors(resErr, conn.Close()) }() // -- List bundles -- - - rows, err := conn.Query( - `SELECT id, statement_fingerprint, collected_at - FROM system.statement_diagnostics - WHERE error IS NULL - ORDER BY collected_at DESC`, - nil, /* args */ - ) + bundles, err := clisqlclient.StmtDiagListBundles(conn) if err != nil { return err } - vals := make([]driver.Value, 3) + var buf bytes.Buffer - w := tabwriter.NewWriter(&buf, 4, 0, 2, ' ', 0) - fmt.Fprint(w, " ID\tCollection time\tStatement\n") - num := 0 - for { - if err := rows.Next(vals); err == io.EOF { - break - } else if err != nil { - return err - } - id := vals[0].(int64) - stmt := vals[1].(string) - t := vals[2].(time.Time) - fmt.Fprintf(w, " %d\t%s\t%s\n", id, t.UTC().Format(timeFmt), stmt) - num++ - } - if err := rows.Close(); err != nil { - return err - } - if num == 0 { + if len(bundles) == 0 { fmt.Printf("No statement diagnostics bundles available.\n") } else { fmt.Printf("Statement diagnostics bundles:\n") + w := tabwriter.NewWriter(&buf, 4, 0, 2, ' ', 0) + fmt.Fprint(w, " ID\tCollection time\tStatement\n") + for _, b := range bundles { + fmt.Fprintf(w, " %d\t%s\t%s\n", b.ID, b.CollectedAt.UTC().Format(timeFmt), b.Statement) + } _ = w.Flush() // When we show a list of bundles, we want an extra blank line. fmt.Println(buf.String()) } // -- List outstanding activation requests -- - - rows, err = conn.Query( - `SELECT id, statement_fingerprint, requested_at - FROM system.statement_diagnostics_requests - WHERE NOT completed - ORDER BY requested_at DESC`, - nil, /* args */ - ) + reqs, err := clisqlclient.StmtDiagListOutstandingRequests(conn) if err != nil { return err } buf.Reset() - w = tabwriter.NewWriter(&buf, 4, 0, 2, ' ', 0) - fmt.Fprint(w, " ID\tActivation time\tStatement\n") - num = 0 - for { - if err := rows.Next(vals); err == io.EOF { - break - } else if err != nil { - return err - } - id := vals[0].(int64) - stmt := vals[1].(string) - t := vals[2].(time.Time) - fmt.Fprintf(w, " %d\t%s\t%s\n", id, t.UTC().Format(timeFmt), stmt) - num++ - } - if err := rows.Close(); err != nil { - return err - } - if num == 0 { + if len(reqs) == 0 { fmt.Printf("No outstanding activation requests.\n") } else { fmt.Printf("Outstanding activation requests:\n") + w := tabwriter.NewWriter(&buf, 4, 0, 2, ' ', 0) + fmt.Fprint(w, " ID\tActivation time\tStatement\n") + for _, r := range reqs { + fmt.Fprintf(w, " %d\t%s\t%s\n", r.ID, r.RequestedAt.UTC().Format(timeFmt), r.Statement) + } _ = w.Flush() fmt.Print(buf.String()) } @@ -159,54 +115,7 @@ func runStmtDiagDownload(cmd *cobra.Command, args []string) (resErr error) { } defer func() { resErr = errors.CombineErrors(resErr, conn.Close()) }() - // Retrieve the chunk IDs; these are stored in an INT ARRAY column. - rows, err := conn.Query( - "SELECT unnest(bundle_chunks) FROM system.statement_diagnostics WHERE id = $1", - []driver.Value{id}, - ) - if err != nil { - return err - } - var chunkIDs []int64 - vals := make([]driver.Value, 1) - for { - if err := rows.Next(vals); err == io.EOF { - break - } else if err != nil { - return err - } - chunkIDs = append(chunkIDs, vals[0].(int64)) - } - if err := rows.Close(); err != nil { - return err - } - - if len(chunkIDs) == 0 { - return errors.Newf("no statement diagnostics bundle with ID %d", id) - } - - // Create the file and write out the chunks. - out, err := os.Create(filename) - if err != nil { - return err - } - - for _, chunkID := range chunkIDs { - data, err := conn.QueryRow( - "SELECT data FROM system.statement_bundle_chunks WHERE id = $1", - []driver.Value{chunkID}, - ) - if err != nil { - _ = out.Close() - return err - } - if _, err := out.Write(data[0].([]byte)); err != nil { - _ = out.Close() - return err - } - } - - return out.Close() + return clisqlclient.StmtDiagDownloadBundle(conn, id, filename) } var stmtDiagDeleteCmd = &cobra.Command{ @@ -229,7 +138,7 @@ func runStmtDiagDelete(cmd *cobra.Command, args []string) (resErr error) { if len(args) > 0 { return errors.New("extra arguments with --all") } - return runStmtDiagDeleteAll(conn) + return clisqlclient.StmtDiagDeleteAllBundles(conn) } if len(args) != 1 { return fmt.Errorf("accepts 1 arg, received %d", len(args)) @@ -240,65 +149,7 @@ func runStmtDiagDelete(cmd *cobra.Command, args []string) (resErr error) { return errors.New("invalid id") } - _, err = conn.QueryRow( - "SELECT 1 FROM system.statement_diagnostics WHERE id = $1", - []driver.Value{id}, - ) - if err != nil { - if err == io.EOF { - return errors.Newf("no statement diagnostics bundle with ID %d", id) - } - return err - } - - return conn.ExecTxn(func(conn clisqlclient.TxBoundConn) error { - // Delete the request metadata. - if err := conn.Exec( - "DELETE FROM system.statement_diagnostics_requests WHERE statement_diagnostics_id = $1", - []driver.Value{id}, - ); err != nil { - return err - } - // Delete the bundle chunks. - if err := conn.Exec( - `DELETE FROM system.statement_bundle_chunks - WHERE id IN ( - SELECT unnest(bundle_chunks) FROM system.statement_diagnostics WHERE id = $1 - )`, - []driver.Value{id}, - ); err != nil { - return err - } - // Finally, delete the diagnostics entry. - return conn.Exec( - "DELETE FROM system.statement_diagnostics WHERE id = $1", - []driver.Value{id}, - ) - }) -} - -func runStmtDiagDeleteAll(conn clisqlclient.Conn) error { - return conn.ExecTxn(func(conn clisqlclient.TxBoundConn) error { - // Delete the request metadata. - if err := conn.Exec( - "DELETE FROM system.statement_diagnostics_requests WHERE completed", - nil, - ); err != nil { - return err - } - // Delete all bundle chunks. - if err := conn.Exec( - `DELETE FROM system.statement_bundle_chunks WHERE true`, - nil, - ); err != nil { - return err - } - // Finally, delete the diagnostics entry. - return conn.Exec( - "DELETE FROM system.statement_diagnostics WHERE true", - nil, - ) - }) + return clisqlclient.StmtDiagDeleteBundle(conn, id) } var stmtDiagCancelCmd = &cobra.Command{ @@ -321,10 +172,7 @@ func runStmtDiagCancel(cmd *cobra.Command, args []string) (resErr error) { if len(args) > 0 { return errors.New("extra arguments with --all") } - return conn.Exec( - "DELETE FROM system.statement_diagnostics_requests WHERE NOT completed", - nil, - ) + return clisqlclient.StmtDiagCancelAllOutstandingRequests(conn) } if len(args) != 1 { return fmt.Errorf("accepts 1 arg, received %d", len(args)) @@ -335,17 +183,7 @@ func runStmtDiagCancel(cmd *cobra.Command, args []string) (resErr error) { return errors.New("invalid id") } - _, err = conn.QueryRow( - "DELETE FROM system.statement_diagnostics_requests WHERE id = $1 RETURNING id", - []driver.Value{id}, - ) - if err != nil { - if err == io.EOF { - return errors.Newf("no outstanding activation requests with ID %d", id) - } - return err - } - return nil + return clisqlclient.StmtDiagCancelOutstandingRequest(conn, id) } var stmtDiagCmds = []*cobra.Command{ diff --git a/pkg/cli/statement_diag_test.go b/pkg/cli/statement_diag_test.go index 7cacca6049e1..0ec3ca835eb0 100644 --- a/pkg/cli/statement_diag_test.go +++ b/pkg/cli/statement_diag_test.go @@ -82,6 +82,7 @@ func Example_statement_diag() { c.RunWithArgs([]string{"statement-diag", "cancel", "--all", "5"}) c.RunWithArgs([]string{"statement-diag", "cancel", "4"}) c.RunWithArgs([]string{"statement-diag", "list"}) + c.RunWithArgs([]string{"statement-diag", "cancel", "123"}) c.RunWithArgs([]string{"statement-diag", "cancel", "--all"}) c.RunWithArgs([]string{"statement-diag", "list"}) @@ -99,7 +100,7 @@ func Example_statement_diag() { // 5 2010-01-02 03:04:11 UTC SELECT _ - _ // 4 2010-01-02 03:04:10 UTC SELECT _ + _ // statement-diag download 13 foo.zip - // ERROR: no statement diagnostics bundle with ID 13 + // ERROR: failed to download statement diagnostics bundle 13 to file 'foo.zip': no statement diagnostics bundle with ID 13 // statement-diag download 20 tempfile.zip // bundle data: chunk1chunk2chunk3 // statement-diag download xx @@ -145,6 +146,8 @@ func Example_statement_diag() { // ID Activation time Statement // 6 2010-01-02 03:04:12 UTC SELECT _ / _ // 5 2010-01-02 03:04:11 UTC SELECT _ - _ + // statement-diag cancel 123 + // ERROR: no outstanding activation request with ID 123 // statement-diag cancel --all // statement-diag list // No statement diagnostics bundles available. From 8708a7b894d97419117de6c9a28c80ce8bbf7297 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Mon, 25 Oct 2021 10:20:18 -0700 Subject: [PATCH 018/205] cli: improve statement-diag download Minor improvement to `statement-diag download`: make the filename optional, for consistency with the SQL shell equivalent. Release note: None --- pkg/cli/statement_diag.go | 27 ++++++++++++++++++--------- pkg/cli/statement_diag_test.go | 12 ++++++------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/pkg/cli/statement_diag.go b/pkg/cli/statement_diag.go index 6cf08cffbedb..6100534dfbcc 100644 --- a/pkg/cli/statement_diag.go +++ b/pkg/cli/statement_diag.go @@ -23,7 +23,7 @@ import ( ) var stmtDiagCmd = &cobra.Command{ - Use: "statement-diag [command]", + Use: "statement-diag [command] [options]", Short: "commands for managing statement diagnostics bundles", Long: `This set of commands can be used to manage and download statement diagnostic bundles, and to cancel outstanding diagnostics activation requests. Statement @@ -32,7 +32,7 @@ diagnostics can be activated from the UI or using EXPLAIN ANALYZE (DEBUG).`, } var stmtDiagListCmd = &cobra.Command{ - Use: "list [options]", + Use: "list", Short: "list available bundles and outstanding activation requests", Long: `List statement diagnostics that are available for download and outstanding diagnostics activation requests.`, @@ -94,20 +94,25 @@ func runStmtDiagList(cmd *cobra.Command, args []string) (resErr error) { } var stmtDiagDownloadCmd = &cobra.Command{ - Use: "download [options]", + Use: "download []", Short: "download statement diagnostics bundle into a zip file", Long: `Download statement diagnostics bundle into a zip file, using an ID returned by the list command.`, - Args: cobra.ExactArgs(2), + Args: cobra.RangeArgs(1, 2), RunE: clierrorplus.MaybeDecorateError(runStmtDiagDownload), } func runStmtDiagDownload(cmd *cobra.Command, args []string) (resErr error) { id, err := strconv.ParseInt(args[0], 10, 64) if err != nil || id < 0 { - return errors.New("invalid bundle id") + return errors.New("invalid bundle ID") + } + var filename string + if len(args) > 1 { + filename = args[1] + } else { + filename = fmt.Sprintf("stmt-bundle-%d.zip", id) } - filename := args[1] conn, err := makeSQLClient("cockroach statement-diag", useSystemDb) if err != nil { @@ -115,7 +120,11 @@ func runStmtDiagDownload(cmd *cobra.Command, args []string) (resErr error) { } defer func() { resErr = errors.CombineErrors(resErr, conn.Close()) }() - return clisqlclient.StmtDiagDownloadBundle(conn, id, filename) + if err := clisqlclient.StmtDiagDownloadBundle(conn, id, filename); err != nil { + return err + } + fmt.Printf("Bundle saved to %q\n", filename) + return nil } var stmtDiagDeleteCmd = &cobra.Command{ @@ -146,7 +155,7 @@ func runStmtDiagDelete(cmd *cobra.Command, args []string) (resErr error) { id, err := strconv.ParseInt(args[0], 10, 64) if err != nil || id < 0 { - return errors.New("invalid id") + return errors.New("invalid ID") } return clisqlclient.StmtDiagDeleteBundle(conn, id) @@ -180,7 +189,7 @@ func runStmtDiagCancel(cmd *cobra.Command, args []string) (resErr error) { id, err := strconv.ParseInt(args[0], 10, 64) if err != nil || id < 0 { - return errors.New("invalid id") + return errors.New("invalid ID") } return clisqlclient.StmtDiagCancelOutstandingRequest(conn, id) diff --git a/pkg/cli/statement_diag_test.go b/pkg/cli/statement_diag_test.go index 0ec3ca835eb0..1b501309eb83 100644 --- a/pkg/cli/statement_diag_test.go +++ b/pkg/cli/statement_diag_test.go @@ -48,7 +48,7 @@ func Example_statement_diag() { } } c.RunWithArgs([]string{"statement-diag", "list"}) - c.RunWithArgs([]string{"statement-diag", "download", "13", "foo.zip"}) + c.RunWithArgs([]string{"statement-diag", "download", "13"}) tmpfile, err := ioutil.TempFile("", "bundle-*.zip") if err != nil { log.Fatalf(context.Background(), "Couldn't execute sql: %s", err) @@ -99,18 +99,18 @@ func Example_statement_diag() { // 6 2010-01-02 03:04:12 UTC SELECT _ / _ // 5 2010-01-02 03:04:11 UTC SELECT _ - _ // 4 2010-01-02 03:04:10 UTC SELECT _ + _ - // statement-diag download 13 foo.zip - // ERROR: failed to download statement diagnostics bundle 13 to file 'foo.zip': no statement diagnostics bundle with ID 13 + // statement-diag download 13 + // ERROR: failed to download statement diagnostics bundle 13 to 'stmt-bundle-13.zip': no statement diagnostics bundle with ID 13 // statement-diag download 20 tempfile.zip // bundle data: chunk1chunk2chunk3 // statement-diag download xx - // ERROR: accepts 2 arg(s), received 1 + // ERROR: invalid bundle ID // statement-diag delete --all 20 // ERROR: extra arguments with --all // statement-diag delete 20 30 // ERROR: accepts at most 1 arg(s), received 2 // statement-diag delete xx - // ERROR: invalid id + // ERROR: invalid ID // statement-diag delete 13 // ERROR: no statement diagnostics bundle with ID 13 // statement-diag delete 10 @@ -134,7 +134,7 @@ func Example_statement_diag() { // 5 2010-01-02 03:04:11 UTC SELECT _ - _ // 4 2010-01-02 03:04:10 UTC SELECT _ + _ // statement-diag cancel xx - // ERROR: invalid id + // ERROR: invalid ID // statement-diag cancel 5 6 // ERROR: accepts at most 1 arg(s), received 2 // statement-diag cancel --all 5 From d45fc5ab4bdca8f2deba057f7e8bb2e63e1c2dc4 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Mon, 25 Oct 2021 10:20:18 -0700 Subject: [PATCH 019/205] cli: simplify invalid syntax paths in SQL shell This change simplifies most invalid syntax paths while improving the error message in a few cases. Release note: None --- pkg/cli/clisqlshell/sql.go | 50 ++++++++++--------- .../interactive_tests/test_demo_node_cmds.tcl | 2 +- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/pkg/cli/clisqlshell/sql.go b/pkg/cli/clisqlshell/sql.go index 733701f4f0b6..47114778ad32 100644 --- a/pkg/cli/clisqlshell/sql.go +++ b/pkg/cli/clisqlshell/sql.go @@ -274,7 +274,11 @@ func (c *cliState) addHistory(line string) { } } -func (c *cliState) invalidSyntax( +func (c *cliState) invalidSyntax(nextState cliStateEnum) cliStateEnum { + return c.invalidSyntaxf(nextState, `%s. Try \? for help.`, c.lastInputLine) +} + +func (c *cliState) invalidSyntaxf( nextState cliStateEnum, format string, args ...interface{}, ) cliStateEnum { fmt.Fprint(c.iCtx.stderr, "invalid syntax: ") @@ -284,10 +288,6 @@ func (c *cliState) invalidSyntax( return nextState } -func (c *cliState) invalidOptSet(nextState cliStateEnum, args []string) cliStateEnum { - return c.invalidSyntax(nextState, `\set %s. Try \? for help.`, strings.Join(args, " ")) -} - func (c *cliState) invalidOptionChange(nextState cliStateEnum, opt string) cliStateEnum { c.exitErr = errors.Newf("cannot change option during multi-line editing: %s\n", opt) fmt.Fprintln(c.iCtx.stderr, c.exitErr) @@ -481,7 +481,7 @@ func (c *cliState) handleSet(args []string, nextState, errState cliStateEnum) cl opt, ok := options[args[0]] if !ok { - return c.invalidOptSet(errState, args) + return c.invalidSyntax(errState) } if len(c.partialLines) > 0 && !opt.validDuringMultilineEntry { return c.invalidOptionChange(errState, args[0]) @@ -495,7 +495,7 @@ func (c *cliState) handleSet(args []string, nextState, errState cliStateEnum) cl case 2: val = args[1] default: - return c.invalidOptSet(errState, args) + return c.invalidSyntax(errState) } // Run the command. @@ -503,7 +503,7 @@ func (c *cliState) handleSet(args []string, nextState, errState cliStateEnum) cl if !opt.isBoolean { err = opt.set(c, val) } else if b, e := clisqlclient.ParseBool(val); e != nil { - return c.invalidOptSet(errState, args) + return c.invalidSyntax(errState) } else if b { err = opt.set(c, "true") } else { @@ -522,11 +522,11 @@ func (c *cliState) handleSet(args []string, nextState, errState cliStateEnum) cl // handleUnset supports the \unset client-side command. func (c *cliState) handleUnset(args []string, nextState, errState cliStateEnum) cliStateEnum { if len(args) != 1 { - return c.invalidSyntax(errState, `\unset %s. Try \? for help.`, strings.Join(args, " ")) + return c.invalidSyntax(errState) } opt, ok := options[args[0]] if !ok { - return c.invalidSyntax(errState, `\unset %s. Try \? for help.`, strings.Join(args, " ")) + return c.invalidSyntax(errState) } if len(c.partialLines) > 0 && !opt.validDuringMultilineEntry { return c.invalidOptionChange(errState, args[0]) @@ -548,7 +548,7 @@ func isEndOfStatement(lastTok int) bool { func (c *cliState) handleDemo(cmd []string, nextState, errState cliStateEnum) cliStateEnum { // A demo cluster signifies the presence of `cockroach demo`. if c.sqlCtx.DemoCluster == nil { - return c.invalidSyntax(errState, `\demo can only be run with cockroach demo`) + return c.invalidSyntaxf(errState, `\demo can only be run with cockroach demo`) } // The \demo command has one of three patterns: @@ -564,7 +564,7 @@ func (c *cliState) handleDemo(cmd []string, nextState, errState cliStateEnum) cl } if len(cmd) != 2 { - return c.invalidSyntax(errState, `\demo expects 2 parameters, but passed %v`, len(cmd)) + return c.invalidSyntax(errState) } // Special case the add command it takes a string instead of a node number. @@ -598,10 +598,10 @@ func (c *cliState) handleDemoNodeCommands( ) cliStateEnum { nodeID, err := strconv.ParseInt(cmd[1], 10, 32) if err != nil { - return c.invalidSyntax( + return c.invalidSyntaxf( errState, "%s", - errors.Wrapf(err, "cannot convert %s to string", cmd[1]), + errors.Wrapf(err, "%q is not a valid node ID", cmd[1]), ) } @@ -633,7 +633,7 @@ func (c *cliState) handleDemoNodeCommands( fmt.Fprintf(c.iCtx.stdout, "node %d has been decommissioned\n", nodeID) return nextState } - return c.invalidSyntax(errState, `command not recognized: %s`, cmd[0]) + return c.invalidSyntax(errState) } // handleHelp prints SQL help. @@ -1163,7 +1163,7 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu c.concatLines = fmt.Sprintf(`SELECT * FROM [SHOW USERS] WHERE username = %s`, lexbase.EscapeSQLString(cmd[1])) return cliRunStatement } - return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + return c.invalidSyntax(errState) case `\d`: if len(cmd) == 1 { @@ -1173,7 +1173,7 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu c.concatLines = `SHOW COLUMNS FROM ` + cmd[1] return cliRunStatement } - return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + return c.invalidSyntax(errState) case `\dd`: c.concatLines = `SHOW CONSTRAINTS FROM ` + cmd[1] + ` WITH COMMENT` return cliRunStatement @@ -1190,14 +1190,14 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu case 2: b, err := clisqlclient.ParseBool(cmd[1]) if err != nil { - return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + return c.invalidSyntax(errState) } else if b { format = clisqlexec.TableDisplayRecords } else { format = clisqlexec.TableDisplayTable } default: - return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + return c.invalidSyntax(errState) } c.sqlExecCtx.TableDisplayFormat = format return loopState @@ -1210,7 +1210,7 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu // Unrecognized command for now, but we want to be helpful. fmt.Fprint(c.iCtx.stderr, "Suggestion: use the SQL SHOW statement to inspect your schema.\n") } - return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + return c.invalidSyntax(errState) } return loopState @@ -1375,7 +1375,7 @@ func (c *cliState) runInclude( cmd []string, contState, errState cliStateEnum, relative bool, ) (resState cliStateEnum) { if len(cmd) != 1 { - return c.invalidSyntax(errState, `%s. Try \? for help.`, c.lastInputLine) + return c.invalidSyntax(errState) } if c.levels >= maxRecursionLevels { @@ -1385,7 +1385,7 @@ func (c *cliState) runInclude( } if len(c.partialLines) > 0 { - return c.invalidSyntax(errState, `cannot use \i during multi-line entry.`) + return c.invalidSyntax(errState) } filename := cmd[0] @@ -1532,8 +1532,10 @@ func (c *cliState) doCheckStatement(startState, contState, execState cliStateEnu fmt.Fprintln(c.iCtx.stdout, helpText) } - _ = c.invalidSyntax(cliStart, "statement ignored: %v", - clierror.NewFormattedError(err, false /*showSeverity*/, false /*verbose*/)) + _ = c.invalidSyntaxf( + cliStart, "statement ignored: %v", + clierror.NewFormattedError(err, false /*showSeverity*/, false /*verbose*/), + ) // Stop here if exiterr is set. if c.iCtx.errExit { diff --git a/pkg/cli/interactive_tests/test_demo_node_cmds.tcl b/pkg/cli/interactive_tests/test_demo_node_cmds.tcl index ed7dad4c49ea..63cf6a98310d 100644 --- a/pkg/cli/interactive_tests/test_demo_node_cmds.tcl +++ b/pkg/cli/interactive_tests/test_demo_node_cmds.tcl @@ -11,7 +11,7 @@ eexpect "movr>" # Wrong number of args send "\\demo node\r" -eexpect "\\demo expects 2 parameters" +eexpect "invalid syntax: \\\\demo node. Try \\\\? for help." # Cannot shutdown node 1 send "\\demo shutdown 1\r" From db5ec499bd2667c8bcfb2d680f0cdb1a426113b1 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Mon, 25 Oct 2021 10:20:18 -0700 Subject: [PATCH 020/205] cli: add \statement-diag SQL shell command This commit adds a `\statement-diag` command to the SQL shell which can be used to list and download statement bundles directly from the shell. This command should be useful for multitenant (where the possibilities of downloading the bundle are more limited), as well as running demo with a `make buildshort` binary. The output of `EXPLAIN ANALYZE (DEBUG)` includes the exact syntax. Informs #70931. Release note (cli change): the SQL shell now supports a `\statement-diag` command for listing and downloading statement diagnostics bundles. --- pkg/cli/clisqlclient/statement_diag.go | 2 +- pkg/cli/clisqlshell/BUILD.bazel | 1 + pkg/cli/clisqlshell/sql.go | 7 ++ pkg/cli/clisqlshell/statement_diag.go | 99 +++++++++++++++++++ .../test_explain_analyze_debug.tcl | 16 +++ pkg/sql/explain_bundle.go | 3 +- 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 pkg/cli/clisqlshell/statement_diag.go diff --git a/pkg/cli/clisqlclient/statement_diag.go b/pkg/cli/clisqlclient/statement_diag.go index 4b1a9e9e3008..2a4fa7e43444 100644 --- a/pkg/cli/clisqlclient/statement_diag.go +++ b/pkg/cli/clisqlclient/statement_diag.go @@ -129,7 +129,7 @@ func stmtDiagListOutstandingRequestsInternal(conn Conn) ([]StmtDiagActivationReq func StmtDiagDownloadBundle(conn Conn, id int64, filename string) error { if err := stmtDiagDownloadBundleInternal(conn, id, filename); err != nil { return errors.Wrapf( - err, "failed to download statement diagnostics bundle %d to file '%s'", id, filename, + err, "failed to download statement diagnostics bundle %d to '%s'", id, filename, ) } return nil diff --git a/pkg/cli/clisqlshell/BUILD.bazel b/pkg/cli/clisqlshell/BUILD.bazel index 28fe82dcb1dd..622e64023d37 100644 --- a/pkg/cli/clisqlshell/BUILD.bazel +++ b/pkg/cli/clisqlshell/BUILD.bazel @@ -7,6 +7,7 @@ go_library( "context.go", "doc.go", "sql.go", + "statement_diag.go", "statements_value.go", ], importpath = "github.com/cockroachdb/cockroach/pkg/cli/clisqlshell", diff --git a/pkg/cli/clisqlshell/sql.go b/pkg/cli/clisqlshell/sql.go index 47114778ad32..4f2268a55b10 100644 --- a/pkg/cli/clisqlshell/sql.go +++ b/pkg/cli/clisqlshell/sql.go @@ -88,6 +88,10 @@ Configuration \set [NAME] set a client-side flag or (without argument) print the current settings. \unset NAME unset a flag. +Statement diagnostics + \statement-diag list list available bundles. + \statement-diag download [] download bundle. + %s More documentation about our SQL dialect and the CLI shell is available online: %s @@ -1205,6 +1209,9 @@ func (c *cliState) doHandleCliCmd(loopState, nextState cliStateEnum) cliStateEnu case `\demo`: return c.handleDemo(cmd[1:], loopState, errState) + case `\statement-diag`: + return c.handleStatementDiag(cmd[1:], loopState, errState) + default: if strings.HasPrefix(cmd[0], `\d`) { // Unrecognized command for now, but we want to be helpful. diff --git a/pkg/cli/clisqlshell/statement_diag.go b/pkg/cli/clisqlshell/statement_diag.go new file mode 100644 index 000000000000..949c56dfdae8 --- /dev/null +++ b/pkg/cli/clisqlshell/statement_diag.go @@ -0,0 +1,99 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package clisqlshell + +import ( + "bytes" + "fmt" + "strconv" + "text/tabwriter" + + "github.com/cockroachdb/cockroach/pkg/cli/clisqlclient" + "github.com/cockroachdb/errors" +) + +// handleStatementDiag handles the `\statement-diag` command. +func (c *cliState) handleStatementDiag( + args []string, loopState, errState cliStateEnum, +) (resState cliStateEnum) { + var cmd string + if len(args) > 0 { + cmd = args[0] + args = args[1:] + } + + var cmdErr error + switch cmd { + case "list": + if len(args) > 0 { + return c.invalidSyntax(errState) + } + cmdErr = c.statementDiagList() + + case "download": + if len(args) < 1 || len(args) > 2 { + return c.invalidSyntax(errState) + } + id, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return c.invalidSyntaxf( + errState, "%s", errors.Wrapf(err, "%q is not a valid bundle ID", args[1]), + ) + } + var filename string + if len(args) > 1 { + filename = args[1] + } else { + filename = fmt.Sprintf("stmt-bundle-%d.zip", id) + } + cmdErr = clisqlclient.StmtDiagDownloadBundle(c.conn, id, filename) + if cmdErr == nil { + fmt.Fprintf(c.iCtx.stdout, "Bundle saved to %q\n", filename) + } + + default: + return c.invalidSyntax(errState) + } + + if cmdErr != nil { + fmt.Fprintln(c.iCtx.stderr, cmdErr) + c.exitErr = cmdErr + return errState + } + return loopState +} + +func (c *cliState) statementDiagList() error { + const timeFmt = "2006-01-02 15:04:05 MST" + + // -- List bundles -- + bundles, err := clisqlclient.StmtDiagListBundles(c.conn) + if err != nil { + return err + } + + if len(bundles) == 0 { + fmt.Fprintf(c.iCtx.stdout, "No statement diagnostics bundles available.\n") + } else { + var buf bytes.Buffer + fmt.Fprintf(c.iCtx.stdout, "Statement diagnostics bundles:\n") + w := tabwriter.NewWriter(&buf, 4, 0, 2, ' ', 0) + fmt.Fprint(w, " ID\tCollection time\tStatement\n") + for _, b := range bundles { + fmt.Fprintf(w, " %d\t%s\t%s\n", b.ID, b.CollectedAt.UTC().Format(timeFmt), b.Statement) + } + _ = w.Flush() + _, _ = buf.WriteTo(c.iCtx.stdout) + } + fmt.Fprintln(c.iCtx.stdout) + + return nil +} diff --git a/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl b/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl index 2c61b0740874..683ad827fc1d 100644 --- a/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl +++ b/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl @@ -2,6 +2,13 @@ source [file join [file dirname $argv0] common.tcl] +proc file_exists {filepath} { + if {! [ file exist $filepath]} { + report "MISSING EXPECTED FILE: $filepath" + exit 1 + } +} + start_test "Ensure that EXPLAIN ANALYZE (DEBUG) works as expected in the sql shell" start_server $argv @@ -13,6 +20,10 @@ eexpect root@ send "EXPLAIN ANALYZE (DEBUG) SELECT 1;\r" eexpect "Statement diagnostics bundle generated." +expect -re "SQL shell: \\\\statement-diag download (\\d+)" { + set id $expect_out(1,string) +} + expect { "warning: pq: unexpected DataRow in simple query execution" { puts "Error: unexpected DataRow in simple query execution" @@ -26,6 +37,11 @@ expect { } } +send "\\statement-diag download $id\r" +eexpect "Bundle saved to" + +file_exists "stmt-bundle-$id.zip" + interrupt eexpect eof diff --git a/pkg/sql/explain_bundle.go b/pkg/sql/explain_bundle.go index 3923133a341b..e15c263786a8 100644 --- a/pkg/sql/explain_bundle.go +++ b/pkg/sql/explain_bundle.go @@ -58,9 +58,10 @@ func setExplainBundleResult( text = []string{ "Statement diagnostics bundle generated. Download from the Admin UI (Advanced", "Debug -> Statement Diagnostics History), via the direct link below, or using", - "the command line.", + "the SQL shell or command line.", fmt.Sprintf("Admin UI: %s", execCfg.AdminURL()), fmt.Sprintf("Direct link: %s/_admin/v1/stmtbundle/%d", execCfg.AdminURL(), bundle.diagID), + fmt.Sprintf("SQL shell: \\statement-diag download %d", bundle.diagID), "Command line: cockroach statement-diag list / download", } } From 87f79da6ab14a27b6f4faa9e9a47bc88671686f7 Mon Sep 17 00:00:00 2001 From: Radu Berinde Date: Mon, 25 Oct 2021 10:20:18 -0700 Subject: [PATCH 021/205] sql: enable EXPLAIN ANALYZE (DEBUG) for tenants EXPLAIN ANALYZE (DEBUG) was disabled for tenants because the returned URL was unusable. We can now download bundles through the SQL shell, so we can reenable EXPLAIN ANALYZE (DEBUG). For non-system tenants, we hide the URL and only show the SQL shell and cli options. As part of this change, we also add tenant support to the interactive cli test infrastructure and we implement the `--background` and `--pid-file` flags for `mt start-sql`. Fixes #70931. Release note (sql change): EXPLAIN ANALYZE (DEBUG) can now be used by tenants. --- .../testdata/logic_test/tenant_unsupported | 3 -- pkg/cli/flags.go | 5 +++ pkg/cli/interactive_tests/common.tcl | 41 +++++++++++++++++++ .../test_explain_analyze_debug.tcl | 41 +++++++++++++++++++ pkg/cli/mt_start_sql.go | 32 +++++++++++++++ pkg/sql/conn_executor_exec.go | 4 -- pkg/sql/explain_bundle.go | 15 ++++++- .../testdata/logic_test/explain_analyze | 5 +++ .../logic_test/explain_analyze_system_tenant | 12 ------ 9 files changed, 137 insertions(+), 21 deletions(-) delete mode 100644 pkg/sql/logictest/testdata/logic_test/explain_analyze_system_tenant diff --git a/pkg/ccl/logictestccl/testdata/logic_test/tenant_unsupported b/pkg/ccl/logictestccl/testdata/logic_test/tenant_unsupported index 96fc99c35ee7..3804d7e76d01 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/tenant_unsupported +++ b/pkg/ccl/logictestccl/testdata/logic_test/tenant_unsupported @@ -65,6 +65,3 @@ ALTER TABLE kv EXPERIMENTAL_RELOCATE LEASE VALUES (1, 'k') statement error operation is unsupported in multi-tenancy mode SELECT crdb_internal.check_consistency(true, '', '') - -statement error operation is unsupported in multi-tenancy mode -EXPLAIN ANALYZE (DEBUG) SELECT * FROM kv diff --git a/pkg/cli/flags.go b/pkg/cli/flags.go index 27457ddfa60f..62c192a66e93 100644 --- a/pkg/cli/flags.go +++ b/pkg/cli/flags.go @@ -953,6 +953,7 @@ func init() { varFlag(f, addrSetter{&serverAdvertiseAddr, &serverAdvertisePort}, cliflags.AdvertiseAddr) varFlag(f, &serverCfg.Stores, cliflags.Store) + stringFlag(f, &startCtx.pidFile, cliflags.PIDFile) stringFlag(f, &startCtx.geoLibsDir, cliflags.GeoLibsDir) stringSliceFlag(f, &serverCfg.SQLConfig.TenantKVAddrs, cliflags.KVAddrs) @@ -969,6 +970,10 @@ func init() { // refers to becomes known. varFlag(f, diskTempStorageSizeValue, cliflags.SQLTempStorage) stringFlag(f, &startCtx.tempDir, cliflags.TempDir) + + if backgroundFlagDefined { + boolFlag(f, &startBackground, cliflags.Background) + } } // Multi-tenancy proxy command flags. diff --git a/pkg/cli/interactive_tests/common.tcl b/pkg/cli/interactive_tests/common.tcl index e21260cf910e..bc50d6d7d05d 100644 --- a/pkg/cli/interactive_tests/common.tcl +++ b/pkg/cli/interactive_tests/common.tcl @@ -138,3 +138,44 @@ proc force_stop_server {argv} { system "kill -KILL `cat server_pid`" report "END FORCE STOP SERVER" } + +proc tenant_port {tenant_id} { + return [expr 40000 + $tenant_id] +} + +proc start_tenant {tenant_id argv} { + set port [tenant_port $tenant_id] + set http_port [expr 8080 + $tenant_id] + set pidfile tenant[set tenant_id]_pid + report "BEGIN START TENANT $tenant_id" + # Create tenant (if it doesn't exist). + system "$argv sql -e 'select case when (select 1 FROM system.tenants WHERE id=$tenant_id) is null then crdb_internal.create_tenant($tenant_id) else $tenant_id end'" + system "$argv mt start-sql --insecure --tenant-id $tenant_id --sql-addr 127.0.0.1:$port --http-addr 127.0.0.1:$http_port --pid-file=$pidfile --background -s=path=logs/tenant$tenant_id >>logs/expect-cmd.log 2>&1; + $argv sql --port $port --insecure -e 'select 1'" + report "START TENANT $tenant_id DONE" +} + +proc stop_tenant {tenant_id argv} { + report "BEGIN STOP TENANT $tenant_id" + set pidfile tenant[set tenant_id]_pid + # Trigger a normal shutdown. + # If after 30 seconds the server hasn't shut down, kill the process and trigger an error. + # Note: kill -CONT tests whether the PID exists (SIGCONT is a no-op for the process). + system "kill -TERM `cat $pidfile` 2>/dev/null; + for i in `seq 1 30`; do + kill -CONT `cat $pidfile` 2>/dev/null || exit 0 + echo still waiting + sleep 1 + done + echo 'server still running?' + # Send an unclean shutdown signal to trigger a stack trace dump. + kill -ABRT `cat $pidfile` 2>/dev/null + # Sleep to increase the probability that the stack trace actually + # makes it to disk before we force-kill the process. + sleep 1 + kill -KILL `cat $pidfile` 2>/dev/null + exit 1" + + report "END STOP TENANT $tenant_id" +} + diff --git a/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl b/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl index 683ad827fc1d..33de502e65ca 100644 --- a/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl +++ b/pkg/cli/interactive_tests/test_explain_analyze_debug.tcl @@ -47,4 +47,45 @@ eexpect eof end_test +start_test "Ensure that EXPLAIN ANALYZE (DEBUG) works for a tenant" + +start_tenant 5 $argv + +spawn $argv sql --port [tenant_port 5] + +set client_spawn_id $spawn_id +eexpect root@ + + +send "EXPLAIN ANALYZE (DEBUG) SELECT 1;\r" +eexpect "Statement diagnostics bundle generated." +expect -re "SQL shell: \\\\statement-diag download (\\d+)" { + set id $expect_out(1,string) +} + +expect { + "warning: pq: unexpected DataRow in simple query execution" { + puts "Error: unexpected DataRow in simple query execution" + exit 1 + } + "connection lost" { + puts "Error: connection lost" + exit 1 + } + "root@" { + } +} + +send "\\statement-diag download $id\r" +eexpect "Bundle saved to" + +file_exists "stmt-bundle-$id.zip" + +interrupt +eexpect eof + +stop_tenant 5 $argv + +end_test + stop_server $argv diff --git a/pkg/cli/mt_start_sql.go b/pkg/cli/mt_start_sql.go index 3d1b98517fa0..46c4806dbffe 100644 --- a/pkg/cli/mt_start_sql.go +++ b/pkg/cli/mt_start_sql.go @@ -12,6 +12,8 @@ package cli import ( "context" + "fmt" + "io/ioutil" "os" "os/signal" @@ -20,6 +22,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/server" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/sdnotify" "github.com/cockroachdb/errors" "github.com/spf13/cobra" ) @@ -59,6 +62,16 @@ well unless it can be verified using a trusted root certificate store. That is, } func runStartSQL(cmd *cobra.Command, args []string) error { + // First things first: if the user wants background processing, + // relinquish the terminal ASAP by forking and exiting. + // + // If executing in the background, the function returns ok == true in + // the parent process (regardless of err) and the parent exits at + // this point. + if ok, err := maybeRerunBackground(); ok { + return err + } + ctx := context.Background() const clusterName = "" @@ -104,6 +117,25 @@ func runStartSQL(cmd *cobra.Command, args []string) error { if err != nil { return err } + // If another process was waiting on the PID (e.g. using a FIFO), + // this is when we can tell them the node has started listening. + if startCtx.pidFile != "" { + log.Ops.Infof(ctx, "PID file: %s", startCtx.pidFile) + if err := ioutil.WriteFile(startCtx.pidFile, []byte(fmt.Sprintf("%d\n", os.Getpid())), 0644); err != nil { + log.Ops.Errorf(ctx, "failed writing the PID: %v", err) + } + } + + // Ensure the configuration logging is written to disk in case a + // process is waiting for the sdnotify readiness to read important + // information from there. + log.Flush() + + // Signal readiness. This unblocks the process when running with + // --background or under systemd. + if err := sdnotify.Ready(); err != nil { + log.Ops.Errorf(ctx, "failed to signal readiness using systemd protocol: %s", err) + } // Start up the diagnostics reporting loop. // We don't do this in (*server.SQLServer).preStart() because we don't diff --git a/pkg/sql/conn_executor_exec.go b/pkg/sql/conn_executor_exec.go index 5aca568495d0..cc8146d786f9 100644 --- a/pkg/sql/conn_executor_exec.go +++ b/pkg/sql/conn_executor_exec.go @@ -44,7 +44,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/cancelchecker" "github.com/cockroachdb/cockroach/pkg/util/duration" - "github.com/cockroachdb/cockroach/pkg/util/errorutil" "github.com/cockroachdb/cockroach/pkg/util/fsm" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/log" @@ -375,9 +374,6 @@ func (ex *connExecutor) execStmtInOpenState( if e, ok := ast.(*tree.ExplainAnalyze); ok { switch e.Mode { case tree.ExplainDebug: - if !p.ExecCfg().Codec.ForSystemTenant() { - return makeErrEvent(errorutil.UnsupportedWithMultiTenancy(70931)) - } telemetry.Inc(sqltelemetry.ExplainAnalyzeDebugUseCounter) ih.SetOutputMode(explainAnalyzeDebugOutput, explain.Flags{}) diff --git a/pkg/sql/explain_bundle.go b/pkg/sql/explain_bundle.go index e15c263786a8..62473f85b7e4 100644 --- a/pkg/sql/explain_bundle.go +++ b/pkg/sql/explain_bundle.go @@ -54,7 +54,7 @@ func setExplainBundleResult( // changing the executor logic (e.g. an implicit transaction could have // committed already). Just show the error in the result. text = []string{fmt.Sprintf("Error generating bundle: %v", bundle.collectionErr)} - } else { + } else if execCfg.Codec.ForSystemTenant() { text = []string{ "Statement diagnostics bundle generated. Download from the Admin UI (Advanced", "Debug -> Statement Diagnostics History), via the direct link below, or using", @@ -62,7 +62,18 @@ func setExplainBundleResult( fmt.Sprintf("Admin UI: %s", execCfg.AdminURL()), fmt.Sprintf("Direct link: %s/_admin/v1/stmtbundle/%d", execCfg.AdminURL(), bundle.diagID), fmt.Sprintf("SQL shell: \\statement-diag download %d", bundle.diagID), - "Command line: cockroach statement-diag list / download", + fmt.Sprintf("Command line: cockroach statement-diag download %d", bundle.diagID), + } + } else { + // Non-system tenants can't directly access the AdminUI. + // TODO(radu): update the message when Serverless provides a way to download + // the bundle (preferably using a more general mechanism so as not to bake + // in Serverless specifics). + text = []string{ + "Statement diagnostics bundle generated. Download using the SQL shell or command", + "line.", + fmt.Sprintf("SQL shell: \\statement-diag download %d", bundle.diagID), + fmt.Sprintf("Command line: cockroach statement-diag download %d", bundle.diagID), } } diff --git a/pkg/sql/logictest/testdata/logic_test/explain_analyze b/pkg/sql/logictest/testdata/logic_test/explain_analyze index 725e5f6b457b..69723749fdee 100644 --- a/pkg/sql/logictest/testdata/logic_test/explain_analyze +++ b/pkg/sql/logictest/testdata/logic_test/explain_analyze @@ -94,3 +94,8 @@ CREATE TABLE c (c INT8 PRIMARY KEY, p INT8 REFERENCES p (p)) query error pgcode 23503 insert on table \"c\" violates foreign key constraint \"c_p_fkey\" EXPLAIN ANALYZE (DISTSQL) INSERT INTO c SELECT x, x + 1 FROM (VALUES (1), (2)) AS v (x) + +# Regression test for the vectorized engine not playing nicely with +# LocalPlanNodes (#62261). +query error pgcode 23503 insert on table \"c\" violates foreign key constraint \"c_p_fkey\" +EXPLAIN ANALYZE (DEBUG) INSERT INTO c SELECT x, x + 1 FROM (VALUES (1), (2)) AS v (x) diff --git a/pkg/sql/logictest/testdata/logic_test/explain_analyze_system_tenant b/pkg/sql/logictest/testdata/logic_test/explain_analyze_system_tenant deleted file mode 100644 index 93137145a2c3..000000000000 --- a/pkg/sql/logictest/testdata/logic_test/explain_analyze_system_tenant +++ /dev/null @@ -1,12 +0,0 @@ -# LogicTest: !3node-tenant -# EXPLAIN ANALYZE logic tests that are only meant to work for the system tenant. - -# Regression test for #45099 (not running postqueries with EXPLAIN ANALYZE). -statement ok -CREATE TABLE p (p INT8 PRIMARY KEY); -CREATE TABLE c (c INT8 PRIMARY KEY, p INT8 REFERENCES p (p)) - -# Regression test for the vectorized engine not playing nicely with -# LocalPlanNodes (#62261). -query error pgcode 23503 insert on table \"c\" violates foreign key constraint \"c_p_fkey\" -EXPLAIN ANALYZE (DEBUG) INSERT INTO c SELECT x, x + 1 FROM (VALUES (1), (2)) AS v (x) From 63510fca52b897f055e29dc1e30aff1b372ca2e7 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Mon, 25 Oct 2021 11:50:09 -0500 Subject: [PATCH 022/205] dev: fix bad output if `bazel query` fails You could see this by passing a non-existent path to `bazel test`, like `dev test pkg/cmd/foo`. This would print out an unhelpful message like `Exit status 7`. This makes the message a little more comprehensible. Release note: None --- pkg/cmd/dev/io/exec/exec.go | 3 +-- pkg/cmd/dev/test.go | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/dev/io/exec/exec.go b/pkg/cmd/dev/io/exec/exec.go index 0b3380883f36..4667119e3320 100644 --- a/pkg/cmd/dev/io/exec/exec.go +++ b/pkg/cmd/dev/io/exec/exec.go @@ -15,7 +15,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "log" "os" "os/exec" @@ -169,7 +168,7 @@ func (e *Exec) commandContextImpl( cmd := exec.CommandContext(ctx, name, args...) if silent { cmd.Stdout = &buffer - cmd.Stderr = ioutil.Discard + cmd.Stderr = nil } else { cmd.Stdout = io.MultiWriter(e.stdout, &buffer) cmd.Stderr = e.stderr diff --git a/pkg/cmd/dev/test.go b/pkg/cmd/dev/test.go index 9bb9a09b2b8d..ad50fe405eed 100644 --- a/pkg/cmd/dev/test.go +++ b/pkg/cmd/dev/test.go @@ -11,11 +11,15 @@ package main import ( + "context" + "errors" "fmt" + "os/exec" "path/filepath" "strings" "time" + "github.com/alessio/shellescape" "github.com/spf13/cobra" ) @@ -126,7 +130,8 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { // where we define `Stringer` separately for the `RemoteOffset` // type. { - out, err := d.exec.CommandContextSilent(ctx, "bazel", "query", fmt.Sprintf("kind(go_test, //%s)", pkg)) + queryArgs := []string{fmt.Sprintf("kind(go_test, //%s)", pkg)} + out, err := d.getQueryOutput(ctx, queryArgs...) if err != nil { return err } @@ -136,7 +141,8 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { } else if strings.Contains(pkg, ":") { testTargets = append(testTargets, pkg) } else { - out, err := d.exec.CommandContextSilent(ctx, "bazel", "query", fmt.Sprintf("kind(go_test, //%s:all)", pkg)) + queryArgs := []string{fmt.Sprintf("kind(go_test, //%s:all)", pkg)} + out, err := d.getQueryOutput(ctx, queryArgs...) if err != nil { return err } @@ -213,3 +219,25 @@ func getDirectoryFromTarget(target string) string { } return target[:colon] } + +// getQueryOutput runs `bazel query` w/ the given arguments, but returns +// a more informative error if the query fails. +func (d *dev) getQueryOutput(ctx context.Context, args ...string) ([]byte, error) { + queryArgs := []string{"query"} + queryArgs = append(queryArgs, args...) + stdoutBytes, err := d.exec.CommandContextSilent(ctx, "bazel", queryArgs...) + if err == nil { + return stdoutBytes, err + } + var cmderr *exec.ExitError + var stdout, stderr string + if len(stdoutBytes) > 0 { + stdout = fmt.Sprintf("stdout: \"%s\" ", string(stdoutBytes)) + } + if errors.As(err, &cmderr) && len(cmderr.Stderr) > 0 { + stderr = fmt.Sprintf("stderr: \"%s\" ", strings.TrimSpace(string(cmderr.Stderr))) + } + return nil, fmt.Errorf("failed to run `bazel %s` %s%s(%w)", + shellescape.QuoteCommand(queryArgs), stdout, stderr, err) + +} From 24373a3bf4294efc3351b44e560cdcf4331089f9 Mon Sep 17 00:00:00 2001 From: Santamaura Date: Mon, 25 Oct 2021 10:47:09 -0700 Subject: [PATCH 023/205] ui: fix zoomed in metrics charts disappear after switching between metrics dashboards This PR fixes the issue where a zoomed in metrics chart or custom daterange causes the graphs to disappear after switching tabs. This was a rendering issue caused by the way lifecycle is managed; when a tab is switched the component unmounts and the uplot graphs are destroyed. On switching back, when the component updates there is a check to see if data exists and also if data is the same as previous data in order to not cause a graph update or re-render every time there is a component update. If the conditions are met then there is an early exit. The issue is when there is a zoomed in metrics chart or custom date range, it is a fixed time window. Thus when the component is updating for these cases it will always exit early, even if no uplot exists which results in a blank area. To resolve this, a check is paired with the same data check to make sure that a uplot already exists which allows the previously mentioned issue to not occur. Release note: None --- .../src/views/cluster/components/linegraph/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx index a87cbcd883bc..5285704f2c00 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx @@ -445,9 +445,8 @@ export class LineGraph extends React.Component { componentDidUpdate(prevProps: Readonly) { if ( - !this.props.data || - !this.props.data.results || - prevProps.data === this.props.data + !this.props.data?.results || + (prevProps.data === this.props.data && this.u !== undefined) ) { return; } From 3eeccf56463fd8a10cf153b86891b951680a258a Mon Sep 17 00:00:00 2001 From: Nathan VanBenschoten Date: Mon, 25 Oct 2021 13:54:51 -0400 Subject: [PATCH 024/205] kv: reject requests from merge txn to RHS of merge after subsumption Closes #71901. This commit causes batch requests from the transaction coordinating a range merge and sent to the right-hand side of the merge after the RHS has been subsumed to be rejected instead of waiting for the merge to complete and causing a deadlock. We don't expect this situation in almost any real case, but we do see a rare situation in #71901 where this can happen. It's better to throw an error up the stack and cause the merge to roll back than to stall the merge. This doesn't completely fix #71901 in that some range merges issued to the range on the left of the `system.descriptors` range may still see errors (but no longer deadlocks), but it does seem to deflake `TestApplier`. I don't quite understand why, but it's pretty clear that before this change, it was failing roughly every 200k iterations and after it, I haven't seen a failure in over 2M iterations. Because of that, I'm going to close the issue. Release note (bug fix): fixed a rare condition that could cause a range merge to get stuck waiting on itself. The symptom of this deadlock was a goroutine stuck in `handleMergeInProgressError` for tens of minutes. --- pkg/kv/kvserver/replica.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/kv/kvserver/replica.go b/pkg/kv/kvserver/replica.go index c44ccb248b6b..798f5a9f59b0 100644 --- a/pkg/kv/kvserver/replica.go +++ b/pkg/kv/kvserver/replica.go @@ -1471,6 +1471,8 @@ func (r *Replica) shouldWaitForPendingMergeRLocked( return nil } } + return errors.Errorf("merge transaction attempting to issue "+ + "batch on right-hand side range after subsumption: %s", ba.Summary()) } // Otherwise, the request must wait. We can't wait for the merge to complete From bd70c9e90392575ef328e6ab1f053198881aeb22 Mon Sep 17 00:00:00 2001 From: Andrei Matei Date: Mon, 25 Oct 2021 14:15:32 -0400 Subject: [PATCH 025/205] jobs: don't include job id in span name Make it a tag instead. Span names are not supposed to have high cardinality. Release note: None --- pkg/jobs/BUILD.bazel | 1 + pkg/jobs/adopt.go | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/jobs/BUILD.bazel b/pkg/jobs/BUILD.bazel index c5f895520785..ea998967bb30 100644 --- a/pkg/jobs/BUILD.bazel +++ b/pkg/jobs/BUILD.bazel @@ -62,6 +62,7 @@ go_library( "@com_github_gogo_protobuf//types", "@com_github_gorhill_cronexpr//:cronexpr", "@com_github_prometheus_client_model//go", + "@io_opentelemetry_go_otel//attribute", ], ) diff --git a/pkg/jobs/adopt.go b/pkg/jobs/adopt.go index e94784c495a0..130707bac221 100644 --- a/pkg/jobs/adopt.go +++ b/pkg/jobs/adopt.go @@ -12,7 +12,6 @@ package jobs import ( "context" - "fmt" "strconv" "sync" @@ -27,6 +26,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/tracing" "github.com/cockroachdb/errors" + "go.opentelemetry.io/otel/attribute" ) const ( @@ -382,8 +382,6 @@ func (r *Registry) runJob( // Bookkeeping. execCtx, cleanup := r.execCtx("resume-"+taskName, username) defer cleanup() - spanName := fmt.Sprintf(`%s-%d`, typ, job.ID()) - var span *tracing.Span // Create a new root span to trace the execution of the current instance of // `job`. Creating a root span allows us to track all the spans linked to this @@ -397,7 +395,8 @@ func (r *Registry) runJob( // TODO(ajwerner): Move this writing up the trace ID down into // stepThroughStateMachine where we're already often (and soon with // exponential backoff, always) updating the job in that call. - ctx, span = r.ac.Tracer.StartSpanCtx(ctx, spanName, spanOptions...) + ctx, span := r.ac.Tracer.StartSpanCtx(ctx, typ.String(), spanOptions...) + span.SetTag("job-id", attribute.Int64Value(int64(job.ID()))) defer span.Finish() if span.TraceID() != 0 { if err := job.Update(ctx, nil /* txn */, func(txn *kv.Txn, md JobMetadata, From fc18f45ccb1656e2ef78f8fb37119d2868a4c419 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Thu, 21 Oct 2021 14:23:35 -0700 Subject: [PATCH 026/205] opt/xform: make merge join have smaller table on the right side A PR that is currently in-flight will change the implementation of the vectorized merge joiner so that it processes the left input always in a streaming fashion whereas the right input might be buffered (partially) in some cases. To account for this difference this commit changes the cost model by introducing a small factor to the right row count which ensures that the smaller right side is preferred to the symmetric join. This commit also handles the case of partial joins in a similar manner to how we handle the hash join. Release note: None --- pkg/sql/opt/exec/execbuilder/relational.go | 31 +- .../execbuilder/testdata/distsql_merge_join | 162 +++--- .../execbuilder/testdata/distsql_single_flow | 32 +- pkg/sql/opt/exec/execbuilder/testdata/fk | 28 +- pkg/sql/opt/exec/execbuilder/testdata/join | 28 +- .../opt/exec/execbuilder/testdata/tpch_vec | 18 +- .../exec/execbuilder/testdata/vectorize_local | 4 +- pkg/sql/opt/memo/testdata/stats/lookup-join | 16 +- .../opt/memo/testdata/stats_quality/tpch/q02 | 4 +- .../opt/memo/testdata/stats_quality/tpch/q03 | 6 +- .../opt/memo/testdata/stats_quality/tpch/q04 | 4 +- .../opt/memo/testdata/stats_quality/tpch/q05 | 10 +- .../opt/memo/testdata/stats_quality/tpch/q08 | 8 +- .../opt/memo/testdata/stats_quality/tpch/q09 | 64 +- .../opt/memo/testdata/stats_quality/tpch/q10 | 14 +- .../opt/memo/testdata/stats_quality/tpch/q11 | 12 +- .../opt/memo/testdata/stats_quality/tpch/q13 | 12 +- .../opt/memo/testdata/stats_quality/tpch/q14 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q15 | 20 +- .../opt/memo/testdata/stats_quality/tpch/q17 | 4 +- .../opt/memo/testdata/stats_quality/tpch/q18 | 72 +-- .../opt/memo/testdata/stats_quality/tpch/q20 | 8 +- .../opt/memo/testdata/stats_quality/tpch/q22 | 18 +- pkg/sql/opt/norm/testdata/rules/combo | 8 +- pkg/sql/opt/norm/testdata/rules/inline | 8 +- .../opt/opbench/testdata/tpch-merge-join.csv | 546 +++++++++--------- pkg/sql/opt/xform/coster.go | 15 +- pkg/sql/opt/xform/testdata/coster/join | 4 +- pkg/sql/opt/xform/testdata/external/hibernate | 38 +- pkg/sql/opt/xform/testdata/external/pgjdbc | 22 +- pkg/sql/opt/xform/testdata/external/tpcc | 28 +- pkg/sql/opt/xform/testdata/external/tpch | 24 +- .../opt/xform/testdata/external/tpch-no-stats | 12 +- pkg/sql/opt/xform/testdata/external/trading | 20 +- .../xform/testdata/external/trading-mutation | 20 +- pkg/sql/opt/xform/testdata/rules/groupby | 10 +- pkg/sql/opt/xform/testdata/rules/join | 118 ++-- pkg/sql/opt/xform/testdata/rules/join_order | 12 +- .../pgwire/testdata/pgtest/row_description | 6 +- 39 files changed, 752 insertions(+), 716 deletions(-) diff --git a/pkg/sql/opt/exec/execbuilder/relational.go b/pkg/sql/opt/exec/execbuilder/relational.go index 4a807483fa26..24597ddd7373 100644 --- a/pkg/sql/opt/exec/execbuilder/relational.go +++ b/pkg/sql/opt/exec/execbuilder/relational.go @@ -1161,19 +1161,40 @@ func (b *Builder) buildMergeJoin(join *memo.MergeJoinExpr) (execPlan, error) { } joinType := joinOpToJoinType(join.JoinType) + leftExpr, rightExpr := join.Left, join.Right + leftEq, rightEq := join.LeftEq, join.RightEq + + if joinType == descpb.LeftSemiJoin || joinType == descpb.LeftAntiJoin { + // We have a partial join, and we want to make sure that the relation + // with smaller cardinality is on the right side. Note that we assumed + // it during the costing. + // TODO(raduberinde): we might also need to look at memo.JoinFlags when + // choosing a side. + leftRowCount := leftExpr.Relational().Stats.RowCount + rightRowCount := rightExpr.Relational().Stats.RowCount + if leftRowCount < rightRowCount { + if joinType == descpb.LeftSemiJoin { + joinType = descpb.RightSemiJoin + } else { + joinType = descpb.RightAntiJoin + } + leftExpr, rightExpr = rightExpr, leftExpr + leftEq, rightEq = rightEq, leftEq + } + } left, right, onExpr, outputCols, err := b.initJoinBuild( - join.Left, join.Right, join.On, joinType, + leftExpr, rightExpr, join.On, joinType, ) if err != nil { return execPlan{}, err } - leftOrd := left.sqlOrdering(join.LeftEq) - rightOrd := right.sqlOrdering(join.RightEq) + leftOrd := left.sqlOrdering(leftEq) + rightOrd := right.sqlOrdering(rightEq) ep := execPlan{outputCols: outputCols} reqOrd := ep.reqOrdering(join) - leftEqColsAreKey := join.Left.Relational().FuncDeps.ColsAreStrictKey(join.LeftEq.ColSet()) - rightEqColsAreKey := join.Right.Relational().FuncDeps.ColsAreStrictKey(join.RightEq.ColSet()) + leftEqColsAreKey := leftExpr.Relational().FuncDeps.ColsAreStrictKey(leftEq.ColSet()) + rightEqColsAreKey := rightExpr.Relational().FuncDeps.ColsAreStrictKey(rightEq.ColSet()) ep.root, err = b.factory.ConstructMergeJoin( joinType, left.root, right.root, diff --git a/pkg/sql/opt/exec/execbuilder/testdata/distsql_merge_join b/pkg/sql/opt/exec/execbuilder/testdata/distsql_merge_join index 4bf05217d8c3..0a25ed0bd24f 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/distsql_merge_join +++ b/pkg/sql/opt/exec/execbuilder/testdata/distsql_merge_join @@ -165,26 +165,26 @@ vectorized: true │ estimated row count: 30 (missing stats) │ └── • merge join (inner) - │ columns: (pid1, pa1, pid1, cid1, ca1) + │ columns: (pid1, cid1, ca1, pid1, pa1) │ estimated row count: 30 (missing stats) │ equality: (pid1) = (pid1) - │ left cols are key + │ right cols are key │ merge ordering: +"(pid1=pid1)" │ ├── • scan - │ columns: (pid1, pa1) + │ columns: (pid1, cid1, ca1) │ ordering: +pid1 - │ estimated row count: 3 (missing stats) - │ table: parent1@parent1_pkey - │ spans: /3-/5/# - │ parallel + │ estimated row count: 30 (missing stats) + │ table: child1@child1_pkey + │ spans: /3-/6 │ └── • scan - columns: (pid1, cid1, ca1) + columns: (pid1, pa1) ordering: +pid1 - estimated row count: 30 (missing stats) - table: child1@child1_pkey - spans: /3-/6 + estimated row count: 3 (missing stats) + table: parent1@parent1_pkey + spans: /3-/5/# + parallel query T EXPLAIN (DISTSQL) SELECT * FROM parent1 JOIN child1 USING(pid1) WHERE pid1 >= 3 AND pid1 <= 5 @@ -194,19 +194,19 @@ vectorized: true · • merge join │ equality: (pid1) = (pid1) -│ left cols are key +│ right cols are key │ ├── • scan │ missing stats -│ table: parent1@parent1_pkey +│ table: child1@child1_pkey │ spans: [/3 - /5] │ └── • scan missing stats - table: child1@child1_pkey + table: parent1@parent1_pkey spans: [/3 - /5] · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyckdGL00AQxt_9K4bxpdGVZBPrQ6CQw4uao5eeTUVB87Bmx95CbjfubkAp_d-liXCXo1V6b5lv5zfffJMdup8tpph_uVleFCXMLotqU31cBlDly_ztBl7Au_XqGjphSXsOV6uihOZWtZLDp6oo38OsU5IH8PlDvs7HAr71UZTQApIALsrLh2KzgHmADLWRVIo7cph-RY41w86ahpwz9iDthoZC_sI0Yqh01_uDXDNsjCVMd-iVbwlT3IjvLa1JSLJhhAwleaHaYezflbPOqjthfyPDqhPapRAmr8J5-BzrPUPT-_vhzostYcr37GkL8OkC45mO-7856R6fdL837bWxkizJiWF9IP_XciTCNdktXRmlyYbxNEJLP_ws4y-DhVXb2_ETGa56n0LGWRaz7DXL5ifDJOecck2uM9rR41BHJ0eHJCS3NF7Gmd42dGNNM9iM5WrgBkGS8-NrPBaFHp6Gf_0Q5mfA8WM4_iecTOBoX--f_QkAAP__lvUemA== +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyckU9v00AQxe98itFwiWGR_xEOK0VyRQ24Sp0SB4EEPizeIV3J3TW7awkU5buj2EitqwSU3jxv5zdv3niH7meLHPMvN8uLooTZZVFtqo_LAKp8mb_dwAt4t15dQycsaR_D1aoooblVrYzhU1WU72HWKRkH8PlDvs7HAr71UZTSAtIALsrLh2KzgHmADLWRVIo7csi_Yow1w86ahpwz9iDthoZC_kIeMVS66_1Brhk2xhLyHXrlW0KOG_G9pTUJSTaMkKEkL1Q7jB23zDqr7oT9jQyrTmjHIUxfhW-w3jM0vb-f7LzYEvJ4z57mHk_d_x7suP08fH5ygeTkAve-vTZWkiU58awP5P9ajqS4JrulK6M02TCZpmjph59l8ctgYdX2dvxEhqvec8hes2zOsoRl6ckw6TnXXJPrjHb0ONTRydEhCcktjZdxprcN3VjTDDZjuRq4QZDk_PiajEWhh6fhdz-E4zPg5DGc_BNOJ3C0r_fP_gQAAP__qbkemg== # Swap parent1 and child1 tables. query T @@ -277,23 +277,23 @@ vectorized: true │ ordering: +pid1 │ estimated row count: 30 (missing stats) │ equality: (pid1) = (pid1) -│ left cols are key +│ right cols are key │ merge ordering: +"(pid1=pid1)" │ ├── • scan -│ columns: (pid1, pa1) +│ columns: (pid1, cid1, ca1) │ ordering: +pid1 -│ estimated row count: 3 (missing stats) -│ table: parent1@parent1_pkey -│ spans: /29-/31/# -│ parallel +│ estimated row count: 30 (missing stats) +│ table: child1@child1_pkey +│ spans: /29-/32 │ └── • scan - columns: (pid1, cid1, ca1) + columns: (pid1, pa1) ordering: +pid1 - estimated row count: 30 (missing stats) - table: child1@child1_pkey - spans: /29-/32 + estimated row count: 3 (missing stats) + table: parent1@parent1_pkey + spans: /29-/31/# + parallel query T EXPLAIN (DISTSQL) SELECT * FROM parent1 JOIN child1 ON parent1.pid1 = child1.pid1 WHERE parent1.pid1 >= 29 AND parent1.pid1 <= 31 ORDER BY parent1.pid1 @@ -303,19 +303,19 @@ vectorized: true · • merge join │ equality: (pid1) = (pid1) -│ left cols are key +│ right cols are key │ ├── • scan │ missing stats -│ table: parent1@parent1_pkey +│ table: child1@child1_pkey │ spans: [/29 - /31] │ └── • scan missing stats - table: child1@child1_pkey + table: parent1@parent1_pkey spans: [/29 - /31] · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJycUVFv0zAQfudXnI6XFgyJHXhYpEoZNIhMXTLSSoAgDyE-OkuZHWxHAlX976jJ0MjUgrY33_fdd9995x26Hy3GmH66Wp1nOcyW2Xqz_rCawzpdpW838AzelcUldLUl7TlcFFkOzbVqJYci_wO_7JTksLglxurj-7RMYTbp-NqHYUQLEGdzOM-Xx9hmARGfQ1Eu0xLefJ44IENtJOX1DTmMvyDHimFnTUPOGXuAdkNDJn9iHDJUuuv9Aa4YNsYSxjv0yreEMW7qby2VVEuyQYgMJflatcPYW8uks-qmtr-Q4bqrtYshEGcvgogHT7HaMzS9vxvvfL0ljPmePW4FPl1hPOSpDcRJf3HS_86218ZKsiQnltVB-b-WIyEuyW7pwihNNhDTEC1997OEP58vrNpej09kWPQ-hoSzRLAkYskrlrw-mSd6yD1Lcp3Rju7nOjo5PIQhuaXxOM70tqEra5rBZiyLQTcAkpwfWTEWmR6o4cP_FvMHiMV9sfinOJqIw321f_I7AAD___2CMwk= +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJycUVFv0zAQfudXnI6XFgyJHXhYpEoZNIhMXTLSSoAgDyE-OkuZHWxHAlX976jJ0MjUgrY33_fdd9995x26Hy3GmH66Wp1nOcyW2Xqz_rCawzpdpW838AzelcUldLUl7TlcFFkOzbVqJYci_wO_7JTksLglxurj-7RMYTbp-NqHYUQLEGdzOM-Xx9hmARGfQ1Eu0xLefJ44IENtJOX1DTmMvyDHimFnTUPOGXuAdkNDJn9iHDJUuuv9Aa4YNsYSxjv0yreEMW7qby2VVEuyQYgMJflatcPYMUXSWXVT21_IcN3V2sUQiLMXQSSw2jM0vb-b7Xy9JYz5nj3On0_9byOfWoAHT0-uIE6ucOfca2MlWZIT1-qg_F_LkRyXZLd0YZQmG4hpjpa--1nCn88XVm2vxycyLHofQ_KKJa9ZwlkiWBKdzBM95KQluc5oR_dzHZ0cHsKQ3NJ4HGd629CVNc1gM5bFoBsASc6PrBiLTA_U8Od_i_kDxOK-WPxTHE3E4b7aP_kdAAD__w1uMwk= # Parent-child where pid1 <= 15 have one joined row and pid1 > 15 have no # joined rows (since child2 only has 15 rows up to pid1 = 15). @@ -392,27 +392,27 @@ vectorized: true │ estimated row count: 40 (missing stats) │ └── • merge join (inner) - │ columns: (pid1, pa1, pid1, cid2, cid3, ca2) + │ columns: (pid1, cid2, cid3, ca2, pid1, pa1) │ ordering: +pid1 │ estimated row count: 40 (missing stats) │ equality: (pid1) = (pid1) - │ left cols are key + │ right cols are key │ merge ordering: +"(pid1=pid1)" │ ├── • scan - │ columns: (pid1, pa1) + │ columns: (pid1, cid2, cid3, ca2) │ ordering: +pid1 - │ estimated row count: 4 (missing stats) - │ table: parent1@parent1_pkey - │ spans: /1/0 /11/0 /21/0 /31/0 - │ parallel + │ estimated row count: 40 (missing stats) + │ table: child2@child2_pkey + │ spans: /1-/2 /11-/12 /21-/22 /31-/32 │ └── • scan - columns: (pid1, cid2, cid3, ca2) + columns: (pid1, pa1) ordering: +pid1 - estimated row count: 40 (missing stats) - table: child2@child2_pkey - spans: /1-/2 /11-/12 /21-/22 /31-/32 + estimated row count: 4 (missing stats) + table: parent1@parent1_pkey + spans: /1/0 /11/0 /21/0 /31/0 + parallel query T EXPLAIN (DISTSQL) SELECT * FROM parent1 JOIN child2 USING(pid1) WHERE pid1 IN (1, 11, 21, 31) ORDER BY pid1 @@ -422,19 +422,19 @@ vectorized: true · • merge join │ equality: (pid1) = (pid1) -│ left cols are key +│ right cols are key │ ├── • scan │ missing stats -│ table: parent1@parent1_pkey +│ table: child2@child2_pkey │ spans: [/1 - /1] [/11 - /11] [/21 - /21] [/31 - /31] │ └── • scan missing stats - table: child2@child2_pkey + table: parent1@parent1_pkey spans: [/1 - /1] [/11 - /11] [/21 - /21] [/31 - /31] · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyckc-O0zAQxu88xWhOLRgldoFDJKQAGyCrbrMkRYBQDiYe2khZO9iOBKr67igO0pJVFyg3z59v5veND-i-dZhg9vF6_SLfwOIir7bVu_USqmydvdrCQ3hdFlfQS0vac7gs8g00-7ZTAt5X-eYNLPpW8SV8eJuVGYxvGMdwBpwzEJzBii-hKC-yEl5-Cg3IUBtFG3lDDpPPyLFm2FvTkHPGjqlDaMjVd0xihq3uBz-ma4aNsYTJAX3rO8IEt_JLRyVJRTaKkaEiL9sujP2FnPa2vZH2BzKseqldAhGPYpBawQqM35N1WB8ZmsHfrnFe7ggTfmT_h8LnKNPBTpE8jsS_oYh7UW4JBm2sIktqtr0elX9rOeHniuyOLk2ryUZi7qejr36R8kfL57bd7acnMiwGn0DKWSpY-oSlT1n67F4_q3NOW5LrjXZ019fJyfFohtSOpuM4M9iGrq1pwpopLIIuJBQ5P1XFFOQ6lMLf_y7mZ4jFXbH4o3g1E8fH-vjgZwAAAP__o_gmnA== +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyckc-O0zAQxu88xWhOLRgldoBDJKQAGyCrbrMkRYBQDiYe2khZO9iOBKr67igO0tJVFyg3z59v5veN9-i-9Zhi_vF69aJYw-KiqDf1u9US6nyVv9rAQ3hdlVcwSEvac7gsizW0u65XAt7XxfoNLIZO8SV8eJtXOUxvmMZwBpwzEJxBwpdQVhd5BS8_hQZkqI2itbwhh-ln5NgwHKxpyTljp9Q-NBTqO6Yxw04Po5_SDcPWWMJ0j77zPWGKG_mlp4qkIhvFyFCRl10fxs6U2WC7G2l_IMN6kNqlEPHHkQCpFSRg_I6sw-bA0Iz-do3zckuY8gP7PxR-jPLreqdYovjfUMS9KLcEozZWkSV1tL2ZlH9rOeHniuyWLk2nyUbi2E9PX_0i44-Wz2233c1PZFiOPoXsKcuesUywLGHZk3v9JOectiI3GO3orq-Tk-PJDKktzcdxZrQtXVvThjVzWAZdSChyfq6KOSh0KIW__13MzxCLu2LxR3FyJI4PzeHBzwAAAP__d_wmng== # Parent-grandchild. # We add the pa1 > 0 condition so a lookup join is not considered to be a better plan. @@ -454,31 +454,31 @@ vectorized: true │ estimated row count: 1,000 (missing stats) │ └── • merge join (inner) - │ columns: (pid1, pa1, pid1, cid2, cid3, gcid2, gca2) + │ columns: (pid1, cid2, cid3, gcid2, gca2, pid1, pa1) │ estimated row count: 1,000 (missing stats) │ equality: (pid1) = (pid1) - │ left cols are key + │ right cols are key │ merge ordering: +"(pid1=pid1)" │ - ├── • filter - │ │ columns: (pid1, pa1) - │ │ ordering: +pid1 - │ │ estimated row count: 342 (missing stats) - │ │ filter: ((((pid1 >= 11) AND (pid1 <= 13)) OR ((pid1 >= 19) AND (pid1 <= 21))) OR ((pid1 >= 31) AND (pid1 <= 33))) OR (pa1 > 0) - │ │ - │ └── • scan - │ columns: (pid1, pa1) - │ ordering: +pid1 - │ estimated row count: 1,000 (missing stats) - │ table: parent1@parent1_pkey - │ spans: FULL SCAN + ├── • scan + │ columns: (pid1, cid2, cid3, gcid2, gca2) + │ ordering: +pid1 + │ estimated row count: 1,000 (missing stats) + │ table: grandchild2@grandchild2_pkey + │ spans: FULL SCAN │ - └── • scan - columns: (pid1, cid2, cid3, gcid2, gca2) - ordering: +pid1 - estimated row count: 1,000 (missing stats) - table: grandchild2@grandchild2_pkey - spans: FULL SCAN + └── • filter + │ columns: (pid1, pa1) + │ ordering: +pid1 + │ estimated row count: 342 (missing stats) + │ filter: ((((pid1 >= 11) AND (pid1 <= 13)) OR ((pid1 >= 19) AND (pid1 <= 21))) OR ((pid1 >= 31) AND (pid1 <= 33))) OR (pa1 > 0) + │ + └── • scan + columns: (pid1, pa1) + ordering: +pid1 + estimated row count: 1,000 (missing stats) + table: parent1@parent1_pkey + spans: FULL SCAN query T EXPLAIN (DISTSQL) @@ -493,22 +493,22 @@ vectorized: true · • merge join │ equality: (pid1) = (pid1) -│ left cols are key +│ right cols are key │ -├── • filter -│ │ filter: ((((pid1 >= 11) AND (pid1 <= 13)) OR ((pid1 >= 19) AND (pid1 <= 21))) OR ((pid1 >= 31) AND (pid1 <= 33))) OR (pa1 > 0) -│ │ -│ └── • scan -│ missing stats -│ table: parent1@parent1_pkey -│ spans: FULL SCAN +├── • scan +│ missing stats +│ table: grandchild2@grandchild2_pkey +│ spans: FULL SCAN │ -└── • scan - missing stats - table: grandchild2@grandchild2_pkey - spans: FULL SCAN +└── • filter + │ filter: ((((pid1 >= 11) AND (pid1 <= 13)) OR ((pid1 >= 19) AND (pid1 <= 21))) OR ((pid1 >= 31) AND (pid1 <= 33))) OR (pa1 > 0) + │ + └── • scan + missing stats + table: parent1@parent1_pkey + spans: FULL SCAN · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJzElVGPozYUhd_7K6z7lHQdmWsDwyCNxKqbbbOaTbbJVK3U5oENboKUBWqI1NVo_nsFTDYkw9hBNNkXFPD9fOxzruNHyP_Zgg_jPz7dv51MyeDdZPGw-PV-SBbj-_FPD-RH8n4--0iyUMmkQPJhNpmStQqTaLWJtxEnvy0m05_JIIsjHJLffxnPx2QwGFTv5K-dZQl5RxCH5O30HWl8Xd0RFMMhmc3JafFtWzHHYWu1aJ1aiH11Fu5riTUECkkayWn4Rebg_wkIFDhQEEDBBgoOLClkKl3JPE9VWfJYAZPoX_AtCnGS7Yry85LCKlUS_Eco4mIrwYeH8PNWzmUYScUsoBDJIoy3lcyzd0Gm4i-h-goUFlmY5D4ZwfKJQrorDtPmRbiW4OMTPV_6fbwtpJKK4bFu_d2v8giaafi-P5k-eM_OBc1I9kPPXjex21cx_m3GFk68LifECRfwb2HtR04sOuz681eyCfPN8ZYDhOXTwUbexcZmgvx42kbDt6TIOLM544yTMIkIkrTYSNVr3eLVdR_m2SWpiqSS0dFMy5Lcl7QVQIBvaq3T_X-Uai0_pHEiFbs5Zrby72IQ4JvhnYrXm_onUJjtCp8ESANOA5sGDg1cGty82tR2j121rHeajtKMIT_df6u2c6SN53eC06ETGFrMsconWiOGNnPs8on2hRoDr98YaF2-M_j56Ygu6eyP6Yi5zHaZy9wL5cKvn4t3-VjE-bG4nQ7N4ZyMGHrM8conehcKR3yHQ4OXT8c-Px27Szr7ozJq_rlV0fA6mvx_zMa-fja3173pWlYzl3mWJrk86x6zyv3IaC1ri_J0p1byk0pXlUz9Oqu46kMk86IexfplktRD5QKbMJ7C2ITFEYzdYK8PjLwP7faStvQ072AZ7wZ7feATyzrSbi9pS08LbYvaer9tfX8LfYM7fdLSw4a09LApLT1tSMsgbUjL7ZPWTR-_9bDBbz1s8ltPG_w2SBv89vr4fdvHbz1s8FsPm_zW0wa_DdIGv1F_YxoMxy5X5kvtLndmV9rkuQE3mG4SN7n-4grRur58-uG_AAAA__8gZppK +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJzEldGvokYUxt_7V0zOk3THDDMDXCS5CZuu27q5q1u1aZPWB1amSuICHTDpzY3_ewPoVbk4I6XaFyJwfvON33cO8wLZXxvwYPjbl6f3ozHqfRjN5rOfnww0Gz4Nf5ij79HH6eQzSgMp4pyiT5PRGK1kEIfLdbQJGfplNhr_iHppFFID_frTcDpEvV6vvEd_bE2Ti0dEqYHejz-gk6fLR0S5YaDJFNWLB03FjBqN1bxxac4P1WlwqEWmARjiJBTj4JvIwPsdKGBggIEDBgsw2LDAkMpkKbIskUXJSwmMwr_BMzFEcbrNi8cLDMtECvBeII_yjQAP5sHXjZiKIBSSmIAhFHkQbUqZE7v8VEbfAvkMGGZpEGce6hNGLEYYYSiIQ0RRkq-FhMUOQ7LN93pHma_PaB1k63MBn8Jit8CQ5cFKgEd3-N_t2z5fdp95w55r2ztKszbSH6NNLqSQxDnXrZ57ZR_5p13ked5oPHf3ifunrXR4te-RU2xwEWOvKzZw_LIc5zXOZ69NdnjTKUF-0cbjOokMhRRhfZ13hfCxahs31ZVa9Tg-C7kSn5IoFpI8nC-7EX_mPZ--Mx5ltFpXPwHDZJt7yHew_4B9hn2OfQv79sXmsK74Vy32O076SUooq1U2a9tn2vT6meAtZplQk9hmcaVmn1CL2FZxpdaNRpvevzGoefvOYNenQ9ukc_jQ9olDLIc4xLlRLuz-ubi3j4VfH4vVamiOc9In1CW2W1ype6Nw-P8wNPT26VjXp8PapHMYlf7px62MhlXRZP9hNtb9sxnc96Rr2M1UZGkSZ-Kqc8ws_o8IV6KyKEu2cim-yGRZylS3k5Irj4dQZHn1llc3o7h6VWzwetjtAlPWhXY6SZtqmtZp85Q-37dZh1kLv1k72O0C1_xuSTudpE01zZV-W-qwLCVMuTotu8t0qGHNdKhh3XSoac10aKQ10-F0Seuhi99qWOO3Gtb5raY1fmukNX67XfwedPFbDWv8VsM6v9W0xm-NtO7r_-bwaGM4fXN4tHFcQ2ss19A6zzW4xnSduM71N0eI0vXF7rt_AgAA__-KXZo1 query T EXPLAIN (VERBOSE) @@ -689,19 +689,19 @@ vectorized: true │ └── • merge join │ equality: (pid1) = (pid1) - │ left cols are key + │ right cols are key │ ├── • scan │ missing stats - │ table: parent1@parent1_pkey + │ table: child1@child1_pkey │ spans: [/10 - /39] │ └── • scan missing stats - table: child1@child1_pkey + table: parent1@parent1_pkey spans: [/10 - /39] · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJyckkGPmzAQhe_9Fdb0Aqq7YMhlkSIRdWnLKiHbkFUrtRwonrJIBFPbSK2i_PcKs1JCtKTK3uw38_m9GdiD-l1DANG3h-UiToh1F6fb9MvSJmm0jD5siep2VptLbDS7aSvObGqk4qmqObspeoV83KxX5LmJ3K_jhAxl8pjGySdiGY58_RxtouFCfnSu6-OcMNcmi-TuVC3mxL-1gUIjOCb5DhUE34FBRqGVokClhOylvWmI-R8IXApV03a6lzMKhZAIwR50pWuEALb5zxo3mHOUjgsUOOq8qs2zz6HDVla7XP4FCmmbNyogDnPfO_6t8xYorDsdkJBBdqAgOn00UjovEQJ2oK8Lw8ZhhqVNZJm5xyQ09CbDeJNhjhm6RkiOEvnIP-vJ_7W8MNEKZYn3ompQOt54ohp_aStk7-y5rMqn4Xg6hj85hn_NThdlKbHMtZCOPw6QPq6skPWe5uTZk4azaww3qFrRKDzf34svu_3SkJc4fAQlOlnggxSFsRmua8MZgaPSQ9UbLnFjSuYvO4XZFbB3DnsXYX8Eu-ewfxGencHZ4c2_AAAA___MwGdW +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJycktFvmzAQxt_3V1i3F9C8gqEvRYpEtLKNKiFdSLVJGw8evlEkgpltpE1R_vcJUy0lajJlb_Z39_P33cEO9M8GIki-3C_maUac2zTf5J8WLsmTRfJuQ3S_dTqusDXsqqsFc6mVyse6EeyqHBTyfr1akqcmcrdKMzKWyUOeZh-IYzny-WOyTsYL-db7fogzwnyXzLPb52o5I-GNCxRaKTDjW9QQfQUGBYVOyRK1lmqQdrYhFb8g8inUbdebQS4olFIhRDswtWkQItjw7w2ukQtUng8UBBpeN_bZMWfcqXrL1W-gkHe81RHxmP_Wux6aV72JSMxoHECxpyB7czDShlcIEdvT_wvDpmGeNngiTXjjvT7kORkmOBnmkKFvpRKoUEz8i4H8V8sLEy1RVXgn6xaVF0wnavCHcWL2xp2punocj39nCM_tNLxkp_OqUlhxI5UXTgPkD0snZoOnPQXuScPrSwzXqDvZajze34sv-8PSUFQ4fgQte1XivZKltRmvK8tZQaA2YzUYL2lrS_Yvew6zC-DgGA7OwuEE9o_h8Cx8fQQX-1d_AgAA__-qVGdX ############### # Outer joins # diff --git a/pkg/sql/opt/exec/execbuilder/testdata/distsql_single_flow b/pkg/sql/opt/exec/execbuilder/testdata/distsql_single_flow index 9dbe644220cc..e3e6660406b9 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/distsql_single_flow +++ b/pkg/sql/opt/exec/execbuilder/testdata/distsql_single_flow @@ -111,9 +111,9 @@ EXPLAIN (VEC) SELECT * FROM t AS t1, t AS t2 WHERE t1.b = 1 AND t1.a = t2.a │ └ Node 1 └ *colexecjoin.mergeJoinInnerOp - ├ *colexecsel.selEQInt64Int64ConstOp - │ └ *colfetcher.ColBatchScan - └ *colfetcher.ColBatchScan + ├ *colfetcher.ColBatchScan + └ *colexecsel.selEQInt64Int64ConstOp + └ *colfetcher.ColBatchScan query T EXPLAIN (DISTSQL) SELECT * FROM t AS t1, t AS t2 WHERE t1.b = 1 AND t1.a = t2.a @@ -127,21 +127,21 @@ vectorized: true │ left cols are key │ right cols are key │ -├── • filter -│ │ estimated row count: 3,333 -│ │ filter: b = 1 -│ │ -│ └── • scan -│ estimated row count: 10,000 (100% of the table; stats collected ago) -│ table: t@t_pkey -│ spans: FULL SCAN +├── • scan +│ estimated row count: 10,000 (100% of the table; stats collected ago) +│ table: t@t_pkey +│ spans: FULL SCAN │ -└── • scan - estimated row count: 10,000 (100% of the table; stats collected ago) - table: t@t_pkey - spans: FULL SCAN +└── • filter + │ estimated row count: 3,333 + │ filter: b = 1 + │ + └── • scan + estimated row count: 10,000 (100% of the table; stats collected ago) + table: t@t_pkey + spans: FULL SCAN · -Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJykkFFr2zAQx9_3KY57SjatieRsDEFAYXWZR5p0cWCD4Qc1umUG1_IkGTZCvvuwPWhtmmxhb7qTfve7vw7ofxQoMf5yt1wkKxhdJ-k2_bQcQxov4_dbeAk3m_UtBFikEDj7cxDw-UO8iWEU-NU9zIGPYbG6bksNcwjiSo-RYWkNrfQDeZRfkWPGsHJ2R95b17QO7YPE_EQ5ZZiXVR2adsZwZx2hPGDIQ0EocavvC9qQNuQmU2RoKOi8aMcGVbn8QbtfyDCtdOklvMbsyNDW4XGgD3pPKPmR_bv0Ji8COXIT3jd2fQlKNMmllMlq--6kUlyifJpT_EfO6KT00VWX1hlyZHqerCH_9uSZzW_J7emjzUtyk6i_eUHfwkjxV-O5y_ffuyMyXNdBguJMCaYipmZMvWHq7clIs0v-cUO-sqWnYbRnJ0-bPGT21P2Pt7Xb0Z2zu1bTleuWaxuGfOhueVckZXfVLPgU5mfhqAfzISwugMUQjs7Cs8Ha2fHF7wAAAP__f31KBg== +Diagram: https://cockroachdb.github.io/distsqlplan/decode.html#eJykklFr2zAQx9_3KY57Sja1iWRnDEFAYXWZR-p0cWCD4Qc1vmUG1_IkGTZCvvuwPegSmmze3nQn_f4_6dAe3bcSJUaf7peLOIHRTZxu0g_LMaTRMnq7gZdwu17dgYdFCp6zXwsBH99F6whGnl8_wBz4GBbJTVdqmIMX13qMDCuTU6IfyaH8jBwzhrU1W3LO2La17w7E-XeUU4ZFVTe-bWcMt8YSyj36wpeEEjf6oaQ16ZzsZIoMc_K6KLtYr2pbPGr7Axmmta6chCvMDgxN458Cndc7QskP7N-k_D-kYoj0tig9WbITcWzs-xKUaMctpYyTzZuzyuCs8snUVMbmZCk_EmUt-acjz9z7juyO3puiIjsJjq9e0hc_UvzVeG6L3dd-iQxXjZegQqZmTL1mijMlmArOPikcMsXEXJl6Ep6-7Nng2ZDgNbnaVI7-KnnaDoryHfWDd6axW7q3Zttp-nLVcV0jJ-f73aAv4qrb6j7t7zC_CIsjeHoKiwFmcQoHF-Hwsjm8CM9O4Ozw4mcAAAD__1A-e3k= # However, if we add a selective filter, the flow is kept on the remote node. query T diff --git a/pkg/sql/opt/exec/execbuilder/testdata/fk b/pkg/sql/opt/exec/execbuilder/testdata/fk index 9540254506cf..63ea37b78bb6 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/fk +++ b/pkg/sql/opt/exec/execbuilder/testdata/fk @@ -1130,22 +1130,22 @@ vectorized: true • filter │ filter: z IS NULL │ -└── • merge join (left outer) - │ equality: (a_z, a_y, a_x) = (z, y, x) - │ right cols are key +└── • merge join (right outer) + │ equality: (z, y, x) = (a_z, a_y, a_x) + │ left cols are key │ - ├── • filter - │ │ filter: (a_y IS NOT NULL) AND (a_x IS NOT NULL) - │ │ - │ └── • scan - │ missing stats - │ table: b@idx - │ spans: (/NULL - ] + ├── • scan + │ missing stats + │ table: a@primary + │ spans: FULL SCAN │ - └── • scan - missing stats - table: a@primary - spans: FULL SCAN + └── • filter + │ filter: (a_y IS NOT NULL) AND (a_x IS NOT NULL) + │ + └── • scan + missing stats + table: b@idx + spans: (/NULL - ] statement ok ALTER TABLE b VALIDATE CONSTRAINT fk_ref diff --git a/pkg/sql/opt/exec/execbuilder/testdata/join b/pkg/sql/opt/exec/execbuilder/testdata/join index d7ca0b054bfa..a809a55ce87d 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/join +++ b/pkg/sql/opt/exec/execbuilder/testdata/join @@ -1590,24 +1590,24 @@ vectorized: true │ estimated row count: 33 (missing stats) │ └── • merge join (inner) - │ columns: (x, y, u, x, y, v) + │ columns: (x, y, v, x, y, u) │ estimated row count: 33 (missing stats) │ equality: (x, y) = (x, y) │ merge ordering: +"(x=x)",+"(y=y)" │ ├── • scan - │ columns: (x, y, u) + │ columns: (x, y, v) │ ordering: +x,+y - │ estimated row count: 333 (missing stats) - │ table: xyu@xyu_pkey - │ spans: /1/2/4- + │ estimated row count: 1,000 (missing stats) + │ table: xyv@xyv_pkey + │ spans: FULL SCAN │ └── • scan - columns: (x, y, v) + columns: (x, y, u) ordering: +x,+y - estimated row count: 1,000 (missing stats) - table: xyv@xyv_pkey - spans: FULL SCAN + estimated row count: 333 (missing stats) + table: xyu@xyu_pkey + spans: /1/2/4- # Regression test for #20765/27431. @@ -1760,17 +1760,17 @@ vectorized: true · • merge join │ equality: (a, b) = (a, b) -│ right cols are key +│ left cols are key │ ├── • scan │ missing stats -│ table: abcdef@abcdef_pkey -│ spans: [/1/2/6/9 - ] +│ table: abg@abg_pkey +│ spans: FULL SCAN │ └── • scan missing stats - table: abg@abg_pkey - spans: FULL SCAN + table: abcdef@abcdef_pkey + spans: [/1/2/6/9 - ] # Regression tests for mixed-type equality columns (#22514). statement ok diff --git a/pkg/sql/opt/exec/execbuilder/testdata/tpch_vec b/pkg/sql/opt/exec/execbuilder/testdata/tpch_vec index 3ef2ded290a0..2edfa1fdd4bc 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/tpch_vec +++ b/pkg/sql/opt/exec/execbuilder/testdata/tpch_vec @@ -726,9 +726,9 @@ EXPLAIN (VEC) SELECT nation, o_year, sum(amount) AS sum_profit FROM ( SELECT n_n │ └ *rowexec.joinReader │ └ *rowexec.joinReader │ └ *colexecjoin.mergeJoinInnerOp - │ ├ *colexecsel.selContainsBytesBytesConstOp - │ │ └ *colfetcher.ColBatchScan - │ └ *colfetcher.ColBatchScan + │ ├ *colfetcher.ColBatchScan + │ └ *colexecsel.selContainsBytesBytesConstOp + │ └ *colfetcher.ColBatchScan └ *colfetcher.ColBatchScan # Query 10 @@ -889,12 +889,12 @@ EXPLAIN (VEC) SELECT c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice, s └ *colexec.hashAggregator └ *colexecjoin.hashJoiner ├ *colexecjoin.mergeJoinInnerOp - │ ├ *colexecjoin.mergeJoinLeftSemiOp - │ │ ├ *colfetcher.ColBatchScan - │ │ └ *colexecsel.selGTFloat64Float64ConstOp - │ │ └ *colexec.orderedAggregator - │ │ └ *colfetcher.ColBatchScan - │ └ *colfetcher.ColBatchScan + │ ├ *colfetcher.ColBatchScan + │ └ *colexecjoin.mergeJoinLeftSemiOp + │ ├ *colfetcher.ColBatchScan + │ └ *colexecsel.selGTFloat64Float64ConstOp + │ └ *colexec.orderedAggregator + │ └ *colfetcher.ColBatchScan └ *colfetcher.ColBatchScan # Query 19 diff --git a/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local b/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local index 634abeac5a7d..0cc75705a171 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local +++ b/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local @@ -104,7 +104,7 @@ EXPLAIN (OPT, VERBOSE) SELECT c.a FROM c INNER MERGE JOIN d ON c.a = d.b project ├── columns: a:1 ├── stats: [rows=10] - ├── cost: 1120.82 + ├── cost: 1121.819 ├── prune: (1) └── inner-join (merge) ├── columns: c.a:1 d.b:8 @@ -112,7 +112,7 @@ project ├── left ordering: +1 ├── right ordering: +8 ├── stats: [rows=10, distinct(1)=1, null(1)=0, distinct(8)=1, null(8)=0] - ├── cost: 1120.7 + ├── cost: 1121.699 ├── fd: (1)==(8), (8)==(1) ├── sort │ ├── columns: c.a:1 diff --git a/pkg/sql/opt/memo/testdata/stats/lookup-join b/pkg/sql/opt/memo/testdata/stats/lookup-join index 12c2d31a55d2..82bb9a8a2ead 100644 --- a/pkg/sql/opt/memo/testdata/stats/lookup-join +++ b/pkg/sql/opt/memo/testdata/stats/lookup-join @@ -203,23 +203,23 @@ SELECT a, b, c, d, e, f FROM abc JOIN def ON a = e ---- inner-join (merge) ├── columns: a:1(int!null) b:2(int) c:3(int!null) d:6(int) e:7(int!null) f:8(int!null) - ├── left ordering: +1 - ├── right ordering: +7 + ├── left ordering: +7 + ├── right ordering: +1 ├── stats: [rows=10000, distinct(1)=100, null(1)=0, distinct(7)=100, null(7)=0] ├── key: (3,7,8) ├── fd: (1,3)-->(2), (7,8)-->(6), (1)==(7), (7)==(1) - ├── scan abc - │ ├── columns: a:1(int!null) b:2(int) c:3(int!null) - │ ├── stats: [rows=100, distinct(1)=100, null(1)=0, distinct(3)=10, null(3)=0] - │ ├── key: (1,3) - │ ├── fd: (1,3)-->(2) - │ └── ordering: +1 ├── scan def@e_idx │ ├── columns: d:6(int) e:7(int!null) f:8(int!null) │ ├── stats: [rows=10000, distinct(7)=100, null(7)=0, distinct(8)=10000, null(8)=0] │ ├── key: (7,8) │ ├── fd: (7,8)-->(6) │ └── ordering: +7 + ├── scan abc + │ ├── columns: a:1(int!null) b:2(int) c:3(int!null) + │ ├── stats: [rows=100, distinct(1)=100, null(1)=0, distinct(3)=10, null(3)=0] + │ ├── key: (1,3) + │ ├── fd: (1,3)-->(2) + │ └── ordering: +1 └── filters (true) # Check column statistics for lookup join. diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 index c4250c3ba31c..1ba04fecf928 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 @@ -626,11 +626,11 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_ ----Stats for q2_scan_12---- column_names row_count distinct_count null_count {s_acctbal} 10000 9967 0 -{s_address} 10000 10027 0 +{s_address} 10000 10000 0 {s_comment} 10000 9934 0 {s_name} 10000 9990 0 {s_nationkey} 10000 25 0 -{s_phone} 10000 10021 0 +{s_phone} 10000 10000 0 {s_suppkey} 10000 9920 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 index 1692e7e3fba0..8be9610227f1 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 @@ -222,7 +222,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_cou column_names row_count distinct_count null_count {o_custkey} 727305 99492 0 {o_orderdate} 727305 1169 0 -{o_orderkey} 727305 730170 0 +{o_orderkey} 727305 727305 0 {o_shippriority} 727305 1 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err @@ -235,13 +235,13 @@ column_names row_count_est row_count_err distinct_count_est distinct_cou column_names row_count distinct_count null_count {o_custkey} 1500000 99846 0 {o_orderdate} 1500000 2406 0 -{o_orderkey} 1500000 1527270 0 +{o_orderkey} 1500000 1500000 0 {o_shippriority} 1500000 1 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_custkey} 1500000.00 1.00 99853.00 1.00 0.00 1.00 {o_orderdate} 1500000.00 1.00 2406.00 1.00 0.00 1.00 -{o_orderkey} 1500000.00 1.00 1500000.00 1.02 0.00 1.00 +{o_orderkey} 1500000.00 1.00 1500000.00 1.00 0.00 1.00 {o_shippriority} 1500000.00 1.00 1.00 1.00 0.00 1.00 ----Stats for q3_select_8---- diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 index 57db825de24c..7d2e81b56750 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 @@ -116,7 +116,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_co ----Stats for q4_index_join_4---- column_names row_count distinct_count null_count {o_orderdate} 57218 92 0 -{o_orderkey} 57218 57392 0 +{o_orderkey} 57218 57218 0 {o_orderpriority} 57218 5 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err @@ -127,7 +127,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_co ----Stats for q4_scan_5---- column_names row_count distinct_count null_count {o_orderdate} 57218 92 0 -{o_orderkey} 57218 57392 0 +{o_orderkey} 57218 57218 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_orderdate} 62887.00 1.10 92.00 1.00 0.00 1.00 diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 index cc508168c880..ffd277db1cf4 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 @@ -195,7 +195,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q5_project_3---- column_names row_count distinct_count null_count -{column60} 7243 7245 0 +{column60} 7243 7243 0 {n_name} 7243 5 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err @@ -301,21 +301,21 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_ column_names row_count distinct_count null_count {o_custkey} 227597 86427 0 {o_orderdate} 227597 365 0 -{o_orderkey} 227597 229152 0 +{o_orderkey} 227597 227597 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_custkey} 233988.00 1.03 92038.00 1.06 0.00 1.00 {o_orderdate} 233988.00 1.03 365.00 1.00 0.00 1.00 -{o_orderkey} 233988.00 1.03 233988.00 1.02 0.00 1.00 +{o_orderkey} 233988.00 1.03 233988.00 1.03 0.00 1.00 ----Stats for q5_scan_8---- column_names row_count distinct_count null_count {o_orderdate} 227597 365 0 -{o_orderkey} 227597 229152 0 +{o_orderkey} 227597 227597 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_orderdate} 233988.00 1.03 365.00 1.00 0.00 1.00 -{o_orderkey} 233988.00 1.03 233988.00 1.02 0.00 1.00 +{o_orderkey} 233988.00 1.03 233988.00 1.03 0.00 1.00 ----Stats for q5_lookup_join_9---- column_names row_count distinct_count null_count diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 index d3d6619a3060..412193d6d4b8 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 @@ -412,7 +412,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_co ----Stats for q8_lookup_join_10---- column_names row_count distinct_count null_count -{c_custkey} 29952 30253 0 +{c_custkey} 29952 29952 0 {c_nationkey} 29952 5 0 {n_nationkey} 29952 5 0 {n_regionkey} 29952 1 0 @@ -420,7 +420,7 @@ column_names row_count distinct_count null_count {r_regionkey} 29952 1 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{c_custkey} 30000.00 1.00 27672.00 1.09 0.00 1.00 +{c_custkey} 30000.00 1.00 27672.00 1.08 0.00 1.00 {c_nationkey} 30000.00 1.00 5.00 1.00 0.00 1.00 {n_nationkey} 30000.00 1.00 5.00 1.00 0.00 1.00 {n_regionkey} 30000.00 1.00 1.00 1.00 0.00 1.00 @@ -462,12 +462,12 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_ column_names row_count distinct_count null_count {l_discount} 13389 11 0 {l_extendedprice} 13389 12076 0 -{l_orderkey} 13389 13400 0 +{l_orderkey} 13389 13389 0 {l_partkey} 13389 1451 0 {l_suppkey} 13389 4044 0 {o_custkey} 13389 12274 0 {o_orderdate} 13389 731 0 -{o_orderkey} 13389 13400 0 +{o_orderkey} 13389 13389 0 {p_partkey} 13389 1451 0 {p_type} 13389 1 0 ~~~~ diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 index ceb8cdcc37fe..6e43c9d33da8 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 @@ -121,20 +121,33 @@ sort │ │ │ │ │ │ ├── inner-join (merge) │ │ │ │ │ │ │ ├── save-table-name: q9_merge_join_10 │ │ │ │ │ │ │ ├── columns: p_partkey:1(int!null) p_name:2(varchar!null) ps_partkey:39(int!null) ps_suppkey:40(int!null) ps_supplycost:42(float!null) - │ │ │ │ │ │ │ ├── left ordering: +1 - │ │ │ │ │ │ │ ├── right ordering: +39 + │ │ │ │ │ │ │ ├── left ordering: +39 + │ │ │ │ │ │ │ ├── right ordering: +1 │ │ │ │ │ │ │ ├── stats: [rows=267682.522, distinct(1)=66618.6736, null(1)=0, distinct(2)=120, null(2)=0, distinct(39)=66618.6736, null(39)=0, distinct(40)=9920, null(40)=0, distinct(42)=1000, null(42)=0] │ │ │ │ │ │ │ ├── key: (39,40) │ │ │ │ │ │ │ ├── fd: (1)-->(2), (39,40)-->(42), (1)==(39), (39)==(1) + │ │ │ │ │ │ │ ├── scan partsupp + │ │ │ │ │ │ │ │ ├── save-table-name: q9_scan_11 + │ │ │ │ │ │ │ │ ├── columns: ps_partkey:39(int!null) ps_suppkey:40(int!null) ps_supplycost:42(float!null) + │ │ │ │ │ │ │ │ ├── stats: [rows=800000, distinct(39)=199241, null(39)=0, distinct(40)=9920, null(40)=0, distinct(42)=1000, null(42)=0] + │ │ │ │ │ │ │ │ │ histogram(39)= 0 0 0 79.993 3911.6 79.993 3941.6 79.993 3926.6 79.993 3926.6 79.993 3926.6 79.993 3916.6 79.993 3908.6 79.993 3912.6 79.993 3934.6 79.993 3934.6 79.993 3929.6 79.993 3923.6 79.993 3932.6 158.99 3918.6 79.993 3915.6 79.993 3907.6 79.993 3912.6 79.993 3914.6 79.993 3922.6 79.993 3914.6 79.993 3921.6 79.993 3909.6 79.993 3927.6 79.993 3913.6 79.993 3913.6 79.993 3904.6 79.993 3913.6 79.993 3920.6 79.993 3913.6 79.993 3904.6 79.993 3904.6 79.993 3908.6 79.993 3908.6 79.993 3944.6 79.993 3916.6 79.993 3930.6 79.993 3904.6 79.993 3927.6 79.993 3923.6 79.993 3911.6 79.993 3911.6 79.993 3914.6 79.993 3913.6 79.993 3913.6 79.993 3922.6 79.993 3901.6 79.993 3922.6 79.993 3924.6 79.993 3920.6 79.993 3921.6 79.993 3930.6 79.993 3925.6 79.993 3913.6 79.993 3924.6 79.993 3939.6 79.993 3928.6 79.993 3924.6 79.993 3918.6 79.993 3928.6 79.993 3916.6 79.993 3911.6 79.993 3936.6 79.993 3933.6 79.993 3831.6 158.99 3903.6 79.993 3911.6 79.993 3913.6 79.993 3911.6 79.993 3909.6 79.993 3913.6 79.993 3920.6 79.993 3910.6 79.993 3906.6 79.993 3918.6 79.993 3910.6 79.993 3908.6 79.993 3910.6 79.993 3911.6 79.993 3903.6 79.993 3903.6 79.993 3904.6 79.993 3923.6 79.993 3933.6 79.993 3928.6 79.993 3834.6 158.99 3928.6 79.993 3904.6 79.993 3943.6 79.993 3909.6 79.993 3943.6 79.993 3918.6 79.993 3919.6 79.993 3923.6 79.993 3916.6 79.993 3942.6 79.993 3934.6 79.993 3922.6 79.993 3908.6 79.993 3919.6 79.993 3910.6 79.993 3943.6 79.993 3912.6 79.993 3916.6 79.993 3951.6 79.993 3919.6 79.993 3906.6 79.993 3920.6 79.993 3941.6 79.993 3913.6 79.993 3905.6 79.993 3909.6 79.993 3909.6 79.993 3905.6 79.993 3913.6 79.993 3918.6 79.993 3934.6 79.993 3902.6 79.993 3907.6 79.993 3923.6 79.993 3922.6 79.993 3926.6 79.993 3917.6 79.993 3827.6 158.99 3911.6 79.993 3911.6 79.993 3937.6 79.993 3904.6 79.993 3975.6 79.993 3905.6 158.99 3917.6 79.993 3918.6 79.993 3906.6 79.993 3930.6 79.993 3917.6 79.993 3909.6 79.993 3916.6 79.993 3912.6 79.993 3922.6 79.993 3912.6 79.993 3907.6 79.993 3905.6 79.993 3912.6 79.993 3931.6 79.993 3913.6 79.993 3918.6 79.993 3920.6 79.993 3911.6 79.993 3902.6 79.993 3917.6 79.993 3920.6 79.993 3915.6 79.993 3904.6 79.993 4056.6 79.993 4020.6 79.993 4006.6 79.993 4046.6 79.993 4006.6 79.993 3998.6 79.993 4012.6 79.993 3985.6 79.993 3991.6 79.993 4009.6 79.993 3989.6 79.993 4032.6 79.993 3989.6 79.993 3990.6 79.993 3985.6 79.993 4024.6 79.993 3993.6 79.993 4000.6 79.993 4039.6 79.993 3987.6 79.993 4016.6 79.993 4017.6 79.993 3990.6 79.993 3998.6 79.993 4002.6 79.993 4005.6 79.993 4003.6 79.993 4000.6 79.993 4026.6 79.993 3995.6 79.993 3997.6 79.993 4014.6 79.993 4008.6 79.993 4002.6 79.993 3989.6 79.993 4017.6 79.993 4003.6 79.993 3988.6 79.993 4028.6 79.993 3997.6 79.993 4000.6 79.993 3998.6 158.99 3904.6 79.993 3990.6 79.993 3989.6 79.993 3997.6 79.993 3988.6 79.993 0 0 + │ │ │ │ │ │ │ │ │ <--- -9223372036854775808 ---- 5 ---------- 925 --------- 2147 -------- 3243 -------- 4346 -------- 5444 -------- 6433 -------- 7290 -------- 8225 -------- 9393 -------- 10561 -------- 11687 -------- 12755 -------- 13905 -------- 14921 -------- 15897 -------- 16739 -------- 17675 -------- 18640 -------- 19705 -------- 20671 -------- 21719 -------- 22602 -------- 23714 -------- 24672 -------- 25620 -------- 26379 -------- 27327 -------- 28366 -------- 29320 -------- 30070 -------- 30837 -------- 31707 -------- 32567 -------- 33809 -------- 34795 -------- 35930 -------- 36688 -------- 37801 -------- 38874 -------- 39798 -------- 40717 -------- 41686 -------- 42635 -------- 43586 -------- 44647 -------- 45188 -------- 46250 -------- 47331 -------- 48366 -------- 49411 -------- 50547 -------- 51636 -------- 52588 -------- 53673 -------- 54877 -------- 55992 -------- 57071 -------- 58085 -------- 59202 -------- 60194 -------- 61109 -------- 62289 -------- 63452 -------- 64350 -------- 65061 -------- 65983 -------- 66930 -------- 67848 -------- 68740 -------- 69687 -------- 70729 -------- 71625 -------- 72441 -------- 73462 -------- 74357 -------- 75226 -------- 76123 -------- 77037 -------- 77774 -------- 78521 -------- 79269 -------- 80341 -------- 81504 -------- 82626 -------- 83571 -------- 84691 -------- 85448 -------- 86681 -------- 87565 -------- 88799 -------- 89810 -------- 90834 -------- 91901 -------- 92892 -------- 94119 -------- 95284 -------- 96346 -------- 97205 -------- 98231 -------- 99141 -------- 100372 -------- 101314 -------- 102304 -------- 103594 -------- 104620 -------- 105437 -------- 106475 -------- 107696 -------- 108646 -------- 109445 -------- 110330 -------- 111220 -------- 112020 -------- 112978 -------- 113996 -------- 115166 -------- 115838 -------- 116694 -------- 117766 -------- 118830 -------- 119934 -------- 120936 -------- 121757 -------- 122685 -------- 123602 -------- 124791 -------- 125549 -------- 126970 -------- 127770 -------- 128776 -------- 129788 -------- 130602 -------- 131740 -------- 132748 -------- 133632 -------- 134629 -------- 135564 -------- 136625 -------- 137559 -------- 138398 -------- 139204 -------- 140136 -------- 141281 -------- 142230 -------- 143246 -------- 144284 -------- 145212 -------- 145869 -------- 146873 -------- 147910 -------- 148889 -------- 149656 -------- 151106 -------- 152338 -------- 153457 -------- 154854 -------- 155973 -------- 157005 -------- 158178 -------- 159002 -------- 159939 -------- 161091 -------- 162000 -------- 163314 -------- 164230 -------- 165157 -------- 165980 -------- 167242 -------- 168207 -------- 169271 -------- 170628 -------- 171503 -------- 172711 -------- 173922 -------- 174853 -------- 175886 -------- 176965 -------- 178073 -------- 179166 -------- 180231 -------- 181505 -------- 182510 -------- 183541 -------- 184734 -------- 185872 -------- 186957 -------- 187866 -------- 189075 -------- 190166 -------- 191061 -------- 192354 -------- 193380 -------- 194445 -------- 195479 -------- 196243 -------- 197170 -------- 198076 -------- 199103 -------- 199994 --- 9223372036854775807 + │ │ │ │ │ │ │ │ │ histogram(40)= 0 240 3920 160 3840 160 3840 160 3920 80 3760 240 3920 80 3920 160 3920 80 3920 80 3920 160 3760 320 3920 80 3920 80 3840 160 3920 80 3760 240 3840 240 3920 80 3920 80 3840 160 3920 80 3920 80 3920 80 3920 240 3920 320 3920 80 3920 80 3920 80 3920 80 3920 160 3920 160 3920 80 3920 160 3680 320 3920 160 3840 240 3920 80 3920 80 3840 160 3920 80 3920 80 3840 160 3920 80 3920 320 3920 80 3920 160 3920 80 3920 160 3920 160 3920 80 3920 80 3840 320 3920 80 3680 320 3920 160 3680 400 3840 240 3840 160 3920 240 3920 80 3920 320 3920 320 3920 160 3760 320 3760 240 3920 80 3920 80 3840 160 3840 320 3840 160 3920 160 3920 80 3920 80 3920 80 3920 80 3920 160 3920 80 3840 240 3920 80 3840 160 3840 160 3840 240 3840 160 3920 80 3920 160 3920 80 3840 160 3920 80 3920 240 3920 160 3920 160 3840 160 3920 160 3840 80 3840 240 3840 160 3760 160 3920 160 3840 80 3840 160 3920 80 3920 80 3920 80 3920 80 3840 160 3920 240 3760 160 3840 80 3840 320 3760 160 3680 240 3840 160 3760 320 3840 240 3840 160 3840 80 3840 160 3840 80 3920 80 3920 240 3760 160 3840 80 3920 160 3600 320 3920 160 3840 240 3840 80 3840 160 3840 160 3680 320 3760 240 3840 80 3840 160 3760 400 3840 160 3840 160 3680 320 3840 80 3840 80 3920 80 3840 160 3920 160 3840 80 3920 80 3840 160 3920 320 3760 160 3840 160 3840 80 3840 160 3840 80 3680 320 3920 80 3920 160 3840 80 3840 240 3840 80 3920 80 3920 80 3760 240 3920 160 3840 160 3840 240 3840 80 3680 240 3920 80 3920 80 3680 320 3920 80 3920 80 3920 160 3840 240 3840 160 3840 160 3680 240 3760 160 3920 80 3920 80 3760 240 3840 240 3840 80 3840 160 3760 240 3920 160 3840 240 3680 320 3840 160 3840 80 3760 160 3920 240 3840 80 3840 80 3840 160 3920 80 3680 320 3920 160 3760 160 3840 160 3920 80 + │ │ │ │ │ │ │ │ │ <--- 1 ------ 48 ------ 112 ------ 174 ------ 233 ------ 271 ------ 318 ------ 364 ------ 415 ------ 468 ------ 521 ------ 565 ------ 617 ------ 658 ------ 713 ------ 767 ------ 817 ------ 878 ------ 930 ------ 978 ------ 1034 ------ 1095 ------ 1150 ------ 1193 ------ 1243 ------ 1294 ------ 1348 ------ 1402 ------ 1468 ------ 1507 ------ 1552 ------ 1614 ------ 1673 ------ 1731 ------ 1780 ------ 1834 ------ 1881 ------ 1940 ------ 1994 ------ 2041 ------ 2096 ------ 2153 ------ 2199 ------ 2256 ------ 2304 ------ 2349 ------ 2401 ------ 2451 ------ 2498 ------ 2546 ------ 2599 ------ 2643 ------ 2683 ------ 2731 ------ 2772 ------ 2827 ------ 2869 ------ 2915 ------ 2973 ------ 3023 ------ 3078 ------ 3129 ------ 3184 ------ 3236 ------ 3296 ------ 3353 ------ 3413 ------ 3473 ------ 3521 ------ 3566 ------ 3607 ------ 3657 ------ 3718 ------ 3775 ------ 3813 ------ 3868 ------ 3924 ------ 3971 ------ 4008 ------ 4065 ------ 4123 ------ 4174 ------ 4231 ------ 4269 ------ 4332 ------ 4385 ------ 4436 ------ 4484 ------ 4537 ------ 4588 ------ 4633 ------ 4685 ------ 4733 ------ 4783 ------ 4834 ------ 4874 ------ 4918 ------ 4953 ------ 5008 ------ 5052 ------ 5096 ------ 5147 ------ 5197 ------ 5245 ------ 5292 ------ 5341 ------ 5396 ------ 5449 ------ 5498 ------ 5547 ------ 5602 ------ 5651 ------ 5703 ------ 5742 ------ 5786 ------ 5827 ------ 5865 ------ 5935 ------ 5980 ------ 6050 ------ 6096 ------ 6153 ------ 6208 ------ 6270 ------ 6309 ------ 6358 ------ 6412 ------ 6464 ------ 6518 ------ 6577 ------ 6640 ------ 6676 ------ 6735 ------ 6792 ------ 6835 ------ 6877 ------ 6916 ------ 6968 ------ 7025 ------ 7061 ------ 7107 ------ 7159 ------ 7221 ------ 7264 ------ 7300 ------ 7356 ------ 7390 ------ 7448 ------ 7495 ------ 7547 ------ 7605 ------ 7648 ------ 7707 ------ 7755 ------ 7818 ------ 7858 ------ 7904 ------ 7951 ------ 7992 ------ 8049 ------ 8098 ------ 8144 ------ 8194 ------ 8234 ------ 8282 ------ 8323 ------ 8376 ------ 8433 ------ 8474 ------ 8514 ------ 8564 ------ 8623 ------ 8676 ------ 8734 ------ 8784 ------ 8819 ------ 8857 ------ 8907 ------ 8955 ------ 9012 ------ 9060 ------ 9103 ------ 9157 ------ 9200 ------ 9249 ------ 9301 ------ 9339 ------ 9381 ------ 9430 ------ 9474 ------ 9529 ------ 9579 ------ 9631 ------ 9673 ------ 9732 ------ 9780 ------ 9832 ------ 9886 ------ 9941 ------ 9997 + │ │ │ │ │ │ │ │ │ histogram(42)= 0 1040 7.9808e+05 880 + │ │ │ │ │ │ │ │ │ <--- 0.009999999776482582 ------------ 10.0 + │ │ │ │ │ │ │ │ ├── key: (39,40) + │ │ │ │ │ │ │ │ ├── fd: (39,40)-->(42) + │ │ │ │ │ │ │ │ └── ordering: +39 │ │ │ │ │ │ │ ├── select - │ │ │ │ │ │ │ │ ├── save-table-name: q9_select_11 + │ │ │ │ │ │ │ │ ├── save-table-name: q9_select_12 │ │ │ │ │ │ │ │ ├── columns: p_partkey:1(int!null) p_name:2(varchar!null) │ │ │ │ │ │ │ │ ├── stats: [rows=66666.6667, distinct(1)=66618.6736, null(1)=0, distinct(2)=120, null(2)=0] │ │ │ │ │ │ │ │ ├── key: (1) │ │ │ │ │ │ │ │ ├── fd: (1)-->(2) │ │ │ │ │ │ │ │ ├── ordering: +1 │ │ │ │ │ │ │ │ ├── scan part - │ │ │ │ │ │ │ │ │ ├── save-table-name: q9_scan_12 + │ │ │ │ │ │ │ │ │ ├── save-table-name: q9_scan_13 │ │ │ │ │ │ │ │ │ ├── columns: p_partkey:1(int!null) p_name:2(varchar!null) │ │ │ │ │ │ │ │ │ ├── stats: [rows=200000, distinct(1)=199241, null(1)=0, distinct(2)=120, null(2)=0] │ │ │ │ │ │ │ │ │ │ histogram(1)= 0 0 0 3.9981 1014.5 3.9981 1043.5 3.9981 946.55 3.9981 1105.5 3.9981 1017.5 3.9981 1020.5 3.9981 880.58 3.9981 954.55 3.9981 883.58 3.9981 933.56 3.9981 891.58 3.9981 1085.5 3.9981 1045.5 3.9981 1134.5 3.9981 1008.5 3.9981 1099.5 3.9981 941.55 3.9981 988.53 3.9981 1003.5 3.9981 894.58 3.9981 975.54 3.9981 1141.5 3.9981 990.53 3.9981 1008.5 3.9981 1074.5 3.9981 966.54 3.9981 994.53 3.9981 906.57 3.9981 1089.5 3.9981 922.56 3.9981 1010.5 3.9981 882.58 3.9981 971.54 3.9981 862.59 3.9981 972.54 3.9981 925.56 3.9981 1156.5 3.9981 1097.5 3.9981 972.54 3.9981 983.53 3.9981 1005.5 3.9981 1048.5 3.9981 1084.5 3.9981 898.57 3.9981 900.57 3.9981 1289.4 3.9981 864.59 3.9981 940.55 3.9981 968.54 3.9981 949.55 3.9981 1023.5 3.9981 865.59 3.9981 1019.5 3.9981 1051.5 3.9981 945.55 3.9981 930.56 3.9981 1086.5 3.9981 1108.5 3.9981 1102.5 3.9981 981.53 3.9981 967.54 3.9981 968.54 3.9981 1045.5 3.9981 829.61 3.9981 1082.5 3.9981 1100.5 3.9981 1007.5 3.9981 1041.5 3.9981 1044.5 3.9981 874.58 3.9981 1075.5 3.9981 1091.5 3.9981 923.56 3.9981 1049.5 3.9981 1064.5 3.9981 1056.5 3.9981 864.59 3.9981 1094.5 3.9981 921.56 3.9981 941.55 3.9981 1055.5 3.9981 1044.5 3.9981 939.55 3.9981 918.56 3.9981 1042.5 3.9981 901.57 3.9981 1003.5 3.9981 1177.4 3.9981 928.56 3.9981 1067.5 3.9981 987.53 3.9981 874.58 3.9981 912.57 3.9981 832.6 3.9981 953.55 3.9981 1078.5 3.9981 886.58 3.9981 894.58 3.9981 938.55 3.9981 987.53 3.9981 985.53 3.9981 1002.5 3.9981 1042.5 3.9981 1274.4 3.9981 1056.5 3.9981 953.55 3.9981 970.54 3.9981 1032.5 3.9981 967.54 3.9981 968.54 3.9981 937.55 3.9981 1130.5 3.9981 918.56 3.9981 904.57 3.9981 957.55 3.9981 1235.4 3.9981 1105.5 3.9981 1009.5 3.9981 1047.5 3.9981 950.55 3.9981 1022.5 3.9981 1069.5 3.9981 1005.5 3.9981 1118.5 3.9981 828.61 3.9981 1119.5 3.9981 842.6 3.9981 995.53 3.9981 983.53 3.9981 921.56 3.9981 1135.5 3.9981 1136.5 3.9981 972.54 3.9981 1125.5 3.9981 887.58 3.9981 1000.5 3.9981 1009.5 3.9981 987.53 3.9981 1066.5 3.9981 947.55 3.9981 991.53 3.9981 1025.5 3.9981 1119.5 3.9981 1020.5 3.9981 1034.5 3.9981 980.53 3.9981 895.57 3.9981 921.56 3.9981 964.54 3.9981 1014.5 3.9981 946.55 3.9981 1039.5 3.9981 1014.5 3.9981 953.55 3.9981 961.54 3.9981 936.56 3.9981 925.56 3.9981 951.55 3.9981 1036.5 3.9981 1020.5 3.9981 1033.5 3.9981 1004.5 3.9981 1053.5 3.9981 1009.5 3.9981 1094.5 3.9981 976.54 3.9981 1012.5 3.9981 1021.5 3.9981 1015.5 3.9981 919.56 3.9981 1078.5 3.9981 1038.5 3.9981 991.53 3.9981 930.56 3.9981 1064.5 3.9981 960.54 3.9981 1011.5 3.9981 970.54 3.9981 1103.5 3.9981 999.53 3.9981 1038.5 3.9981 1108.5 3.9981 1007.5 3.9981 1263.4 3.9981 861.59 3.9981 1009.5 3.9981 917.56 3.9981 1099.5 3.9981 1027.5 3.9981 1008.5 3.9981 983.53 3.9981 1010.5 3.9981 1067.5 3.9981 931.56 3.9981 984.53 3.9981 874.58 3.9981 1002.5 3.9981 954.55 3.9981 1040.5 3.9981 0 0 @@ -146,19 +159,6 @@ sort │ │ │ │ │ │ │ │ │ └── ordering: +1 │ │ │ │ │ │ │ │ └── filters │ │ │ │ │ │ │ │ └── p_name:2 LIKE '%green%' [type=bool, outer=(2), constraints=(/2: (/NULL - ])] - │ │ │ │ │ │ │ ├── scan partsupp - │ │ │ │ │ │ │ │ ├── save-table-name: q9_scan_13 - │ │ │ │ │ │ │ │ ├── columns: ps_partkey:39(int!null) ps_suppkey:40(int!null) ps_supplycost:42(float!null) - │ │ │ │ │ │ │ │ ├── stats: [rows=800000, distinct(39)=199241, null(39)=0, distinct(40)=9920, null(40)=0, distinct(42)=1000, null(42)=0] - │ │ │ │ │ │ │ │ │ histogram(39)= 0 0 0 79.993 3911.6 79.993 3941.6 79.993 3926.6 79.993 3926.6 79.993 3926.6 79.993 3916.6 79.993 3908.6 79.993 3912.6 79.993 3934.6 79.993 3934.6 79.993 3929.6 79.993 3923.6 79.993 3932.6 158.99 3918.6 79.993 3915.6 79.993 3907.6 79.993 3912.6 79.993 3914.6 79.993 3922.6 79.993 3914.6 79.993 3921.6 79.993 3909.6 79.993 3927.6 79.993 3913.6 79.993 3913.6 79.993 3904.6 79.993 3913.6 79.993 3920.6 79.993 3913.6 79.993 3904.6 79.993 3904.6 79.993 3908.6 79.993 3908.6 79.993 3944.6 79.993 3916.6 79.993 3930.6 79.993 3904.6 79.993 3927.6 79.993 3923.6 79.993 3911.6 79.993 3911.6 79.993 3914.6 79.993 3913.6 79.993 3913.6 79.993 3922.6 79.993 3901.6 79.993 3922.6 79.993 3924.6 79.993 3920.6 79.993 3921.6 79.993 3930.6 79.993 3925.6 79.993 3913.6 79.993 3924.6 79.993 3939.6 79.993 3928.6 79.993 3924.6 79.993 3918.6 79.993 3928.6 79.993 3916.6 79.993 3911.6 79.993 3936.6 79.993 3933.6 79.993 3831.6 158.99 3903.6 79.993 3911.6 79.993 3913.6 79.993 3911.6 79.993 3909.6 79.993 3913.6 79.993 3920.6 79.993 3910.6 79.993 3906.6 79.993 3918.6 79.993 3910.6 79.993 3908.6 79.993 3910.6 79.993 3911.6 79.993 3903.6 79.993 3903.6 79.993 3904.6 79.993 3923.6 79.993 3933.6 79.993 3928.6 79.993 3834.6 158.99 3928.6 79.993 3904.6 79.993 3943.6 79.993 3909.6 79.993 3943.6 79.993 3918.6 79.993 3919.6 79.993 3923.6 79.993 3916.6 79.993 3942.6 79.993 3934.6 79.993 3922.6 79.993 3908.6 79.993 3919.6 79.993 3910.6 79.993 3943.6 79.993 3912.6 79.993 3916.6 79.993 3951.6 79.993 3919.6 79.993 3906.6 79.993 3920.6 79.993 3941.6 79.993 3913.6 79.993 3905.6 79.993 3909.6 79.993 3909.6 79.993 3905.6 79.993 3913.6 79.993 3918.6 79.993 3934.6 79.993 3902.6 79.993 3907.6 79.993 3923.6 79.993 3922.6 79.993 3926.6 79.993 3917.6 79.993 3827.6 158.99 3911.6 79.993 3911.6 79.993 3937.6 79.993 3904.6 79.993 3975.6 79.993 3905.6 158.99 3917.6 79.993 3918.6 79.993 3906.6 79.993 3930.6 79.993 3917.6 79.993 3909.6 79.993 3916.6 79.993 3912.6 79.993 3922.6 79.993 3912.6 79.993 3907.6 79.993 3905.6 79.993 3912.6 79.993 3931.6 79.993 3913.6 79.993 3918.6 79.993 3920.6 79.993 3911.6 79.993 3902.6 79.993 3917.6 79.993 3920.6 79.993 3915.6 79.993 3904.6 79.993 4056.6 79.993 4020.6 79.993 4006.6 79.993 4046.6 79.993 4006.6 79.993 3998.6 79.993 4012.6 79.993 3985.6 79.993 3991.6 79.993 4009.6 79.993 3989.6 79.993 4032.6 79.993 3989.6 79.993 3990.6 79.993 3985.6 79.993 4024.6 79.993 3993.6 79.993 4000.6 79.993 4039.6 79.993 3987.6 79.993 4016.6 79.993 4017.6 79.993 3990.6 79.993 3998.6 79.993 4002.6 79.993 4005.6 79.993 4003.6 79.993 4000.6 79.993 4026.6 79.993 3995.6 79.993 3997.6 79.993 4014.6 79.993 4008.6 79.993 4002.6 79.993 3989.6 79.993 4017.6 79.993 4003.6 79.993 3988.6 79.993 4028.6 79.993 3997.6 79.993 4000.6 79.993 3998.6 158.99 3904.6 79.993 3990.6 79.993 3989.6 79.993 3997.6 79.993 3988.6 79.993 0 0 - │ │ │ │ │ │ │ │ │ <--- -9223372036854775808 ---- 5 ---------- 925 --------- 2147 -------- 3243 -------- 4346 -------- 5444 -------- 6433 -------- 7290 -------- 8225 -------- 9393 -------- 10561 -------- 11687 -------- 12755 -------- 13905 -------- 14921 -------- 15897 -------- 16739 -------- 17675 -------- 18640 -------- 19705 -------- 20671 -------- 21719 -------- 22602 -------- 23714 -------- 24672 -------- 25620 -------- 26379 -------- 27327 -------- 28366 -------- 29320 -------- 30070 -------- 30837 -------- 31707 -------- 32567 -------- 33809 -------- 34795 -------- 35930 -------- 36688 -------- 37801 -------- 38874 -------- 39798 -------- 40717 -------- 41686 -------- 42635 -------- 43586 -------- 44647 -------- 45188 -------- 46250 -------- 47331 -------- 48366 -------- 49411 -------- 50547 -------- 51636 -------- 52588 -------- 53673 -------- 54877 -------- 55992 -------- 57071 -------- 58085 -------- 59202 -------- 60194 -------- 61109 -------- 62289 -------- 63452 -------- 64350 -------- 65061 -------- 65983 -------- 66930 -------- 67848 -------- 68740 -------- 69687 -------- 70729 -------- 71625 -------- 72441 -------- 73462 -------- 74357 -------- 75226 -------- 76123 -------- 77037 -------- 77774 -------- 78521 -------- 79269 -------- 80341 -------- 81504 -------- 82626 -------- 83571 -------- 84691 -------- 85448 -------- 86681 -------- 87565 -------- 88799 -------- 89810 -------- 90834 -------- 91901 -------- 92892 -------- 94119 -------- 95284 -------- 96346 -------- 97205 -------- 98231 -------- 99141 -------- 100372 -------- 101314 -------- 102304 -------- 103594 -------- 104620 -------- 105437 -------- 106475 -------- 107696 -------- 108646 -------- 109445 -------- 110330 -------- 111220 -------- 112020 -------- 112978 -------- 113996 -------- 115166 -------- 115838 -------- 116694 -------- 117766 -------- 118830 -------- 119934 -------- 120936 -------- 121757 -------- 122685 -------- 123602 -------- 124791 -------- 125549 -------- 126970 -------- 127770 -------- 128776 -------- 129788 -------- 130602 -------- 131740 -------- 132748 -------- 133632 -------- 134629 -------- 135564 -------- 136625 -------- 137559 -------- 138398 -------- 139204 -------- 140136 -------- 141281 -------- 142230 -------- 143246 -------- 144284 -------- 145212 -------- 145869 -------- 146873 -------- 147910 -------- 148889 -------- 149656 -------- 151106 -------- 152338 -------- 153457 -------- 154854 -------- 155973 -------- 157005 -------- 158178 -------- 159002 -------- 159939 -------- 161091 -------- 162000 -------- 163314 -------- 164230 -------- 165157 -------- 165980 -------- 167242 -------- 168207 -------- 169271 -------- 170628 -------- 171503 -------- 172711 -------- 173922 -------- 174853 -------- 175886 -------- 176965 -------- 178073 -------- 179166 -------- 180231 -------- 181505 -------- 182510 -------- 183541 -------- 184734 -------- 185872 -------- 186957 -------- 187866 -------- 189075 -------- 190166 -------- 191061 -------- 192354 -------- 193380 -------- 194445 -------- 195479 -------- 196243 -------- 197170 -------- 198076 -------- 199103 -------- 199994 --- 9223372036854775807 - │ │ │ │ │ │ │ │ │ histogram(40)= 0 240 3920 160 3840 160 3840 160 3920 80 3760 240 3920 80 3920 160 3920 80 3920 80 3920 160 3760 320 3920 80 3920 80 3840 160 3920 80 3760 240 3840 240 3920 80 3920 80 3840 160 3920 80 3920 80 3920 80 3920 240 3920 320 3920 80 3920 80 3920 80 3920 80 3920 160 3920 160 3920 80 3920 160 3680 320 3920 160 3840 240 3920 80 3920 80 3840 160 3920 80 3920 80 3840 160 3920 80 3920 320 3920 80 3920 160 3920 80 3920 160 3920 160 3920 80 3920 80 3840 320 3920 80 3680 320 3920 160 3680 400 3840 240 3840 160 3920 240 3920 80 3920 320 3920 320 3920 160 3760 320 3760 240 3920 80 3920 80 3840 160 3840 320 3840 160 3920 160 3920 80 3920 80 3920 80 3920 80 3920 160 3920 80 3840 240 3920 80 3840 160 3840 160 3840 240 3840 160 3920 80 3920 160 3920 80 3840 160 3920 80 3920 240 3920 160 3920 160 3840 160 3920 160 3840 80 3840 240 3840 160 3760 160 3920 160 3840 80 3840 160 3920 80 3920 80 3920 80 3920 80 3840 160 3920 240 3760 160 3840 80 3840 320 3760 160 3680 240 3840 160 3760 320 3840 240 3840 160 3840 80 3840 160 3840 80 3920 80 3920 240 3760 160 3840 80 3920 160 3600 320 3920 160 3840 240 3840 80 3840 160 3840 160 3680 320 3760 240 3840 80 3840 160 3760 400 3840 160 3840 160 3680 320 3840 80 3840 80 3920 80 3840 160 3920 160 3840 80 3920 80 3840 160 3920 320 3760 160 3840 160 3840 80 3840 160 3840 80 3680 320 3920 80 3920 160 3840 80 3840 240 3840 80 3920 80 3920 80 3760 240 3920 160 3840 160 3840 240 3840 80 3680 240 3920 80 3920 80 3680 320 3920 80 3920 80 3920 160 3840 240 3840 160 3840 160 3680 240 3760 160 3920 80 3920 80 3760 240 3840 240 3840 80 3840 160 3760 240 3920 160 3840 240 3680 320 3840 160 3840 80 3760 160 3920 240 3840 80 3840 80 3840 160 3920 80 3680 320 3920 160 3760 160 3840 160 3920 80 - │ │ │ │ │ │ │ │ │ <--- 1 ------ 48 ------ 112 ------ 174 ------ 233 ------ 271 ------ 318 ------ 364 ------ 415 ------ 468 ------ 521 ------ 565 ------ 617 ------ 658 ------ 713 ------ 767 ------ 817 ------ 878 ------ 930 ------ 978 ------ 1034 ------ 1095 ------ 1150 ------ 1193 ------ 1243 ------ 1294 ------ 1348 ------ 1402 ------ 1468 ------ 1507 ------ 1552 ------ 1614 ------ 1673 ------ 1731 ------ 1780 ------ 1834 ------ 1881 ------ 1940 ------ 1994 ------ 2041 ------ 2096 ------ 2153 ------ 2199 ------ 2256 ------ 2304 ------ 2349 ------ 2401 ------ 2451 ------ 2498 ------ 2546 ------ 2599 ------ 2643 ------ 2683 ------ 2731 ------ 2772 ------ 2827 ------ 2869 ------ 2915 ------ 2973 ------ 3023 ------ 3078 ------ 3129 ------ 3184 ------ 3236 ------ 3296 ------ 3353 ------ 3413 ------ 3473 ------ 3521 ------ 3566 ------ 3607 ------ 3657 ------ 3718 ------ 3775 ------ 3813 ------ 3868 ------ 3924 ------ 3971 ------ 4008 ------ 4065 ------ 4123 ------ 4174 ------ 4231 ------ 4269 ------ 4332 ------ 4385 ------ 4436 ------ 4484 ------ 4537 ------ 4588 ------ 4633 ------ 4685 ------ 4733 ------ 4783 ------ 4834 ------ 4874 ------ 4918 ------ 4953 ------ 5008 ------ 5052 ------ 5096 ------ 5147 ------ 5197 ------ 5245 ------ 5292 ------ 5341 ------ 5396 ------ 5449 ------ 5498 ------ 5547 ------ 5602 ------ 5651 ------ 5703 ------ 5742 ------ 5786 ------ 5827 ------ 5865 ------ 5935 ------ 5980 ------ 6050 ------ 6096 ------ 6153 ------ 6208 ------ 6270 ------ 6309 ------ 6358 ------ 6412 ------ 6464 ------ 6518 ------ 6577 ------ 6640 ------ 6676 ------ 6735 ------ 6792 ------ 6835 ------ 6877 ------ 6916 ------ 6968 ------ 7025 ------ 7061 ------ 7107 ------ 7159 ------ 7221 ------ 7264 ------ 7300 ------ 7356 ------ 7390 ------ 7448 ------ 7495 ------ 7547 ------ 7605 ------ 7648 ------ 7707 ------ 7755 ------ 7818 ------ 7858 ------ 7904 ------ 7951 ------ 7992 ------ 8049 ------ 8098 ------ 8144 ------ 8194 ------ 8234 ------ 8282 ------ 8323 ------ 8376 ------ 8433 ------ 8474 ------ 8514 ------ 8564 ------ 8623 ------ 8676 ------ 8734 ------ 8784 ------ 8819 ------ 8857 ------ 8907 ------ 8955 ------ 9012 ------ 9060 ------ 9103 ------ 9157 ------ 9200 ------ 9249 ------ 9301 ------ 9339 ------ 9381 ------ 9430 ------ 9474 ------ 9529 ------ 9579 ------ 9631 ------ 9673 ------ 9732 ------ 9780 ------ 9832 ------ 9886 ------ 9941 ------ 9997 - │ │ │ │ │ │ │ │ │ histogram(42)= 0 1040 7.9808e+05 880 - │ │ │ │ │ │ │ │ │ <--- 0.009999999776482582 ------------ 10.0 - │ │ │ │ │ │ │ │ ├── key: (39,40) - │ │ │ │ │ │ │ │ ├── fd: (39,40)-->(42) - │ │ │ │ │ │ │ │ └── ordering: +39 │ │ │ │ │ │ │ └── filters (true) │ │ │ │ │ │ └── filters (true) │ │ │ │ │ └── filters (true) @@ -396,16 +396,27 @@ column_names row_count_est row_count_err distinct_count_est distinct_coun {ps_suppkey} 267683.00 6.28 <== 9920.00 1.01 0.00 1.00 {ps_supplycost} 267683.00 6.28 <== 1000.00 34.85 <== 0.00 1.00 -----Stats for q9_select_11---- +----Stats for q9_scan_11---- +column_names row_count distinct_count null_count +{ps_partkey} 800000 199241 0 +{ps_suppkey} 800000 9920 0 +{ps_supplycost} 800000 100379 0 +~~~~ +column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err +{ps_partkey} 800000.00 1.00 199241.00 1.00 0.00 1.00 +{ps_suppkey} 800000.00 1.00 9920.00 1.00 0.00 1.00 +{ps_supplycost} 800000.00 1.00 1000.00 100.38 <== 0.00 1.00 + +----Stats for q9_select_12---- column_names row_count distinct_count null_count -{p_name} 10664 10680 0 +{p_name} 10664 10664 0 {p_partkey} 10664 10632 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{p_name} 66667.00 6.25 <== 120.00 89.00 <== 0.00 1.00 +{p_name} 66667.00 6.25 <== 120.00 88.87 <== 0.00 1.00 {p_partkey} 66667.00 6.25 <== 66619.00 6.27 <== 0.00 1.00 -----Stats for q9_scan_12---- +----Stats for q9_scan_13---- column_names row_count distinct_count null_count {p_name} 200000 198131 0 {p_partkey} 200000 199241 0 @@ -414,17 +425,6 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e {p_name} 200000.00 1.00 120.00 1651.09 <== 0.00 1.00 {p_partkey} 200000.00 1.00 199241.00 1.00 0.00 1.00 -----Stats for q9_scan_13---- -column_names row_count distinct_count null_count -{ps_partkey} 800000 199241 0 -{ps_suppkey} 800000 9920 0 -{ps_supplycost} 800000 100379 0 -~~~~ -column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{ps_partkey} 800000.00 1.00 199241.00 1.00 0.00 1.00 -{ps_suppkey} 800000.00 1.00 9920.00 1.00 0.00 1.00 -{ps_supplycost} 800000.00 1.00 1000.00 100.38 <== 0.00 1.00 - ----Stats for q9_scan_14---- column_names row_count distinct_count null_count {n_name} 25 25 0 diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 index 1b02071a5ea3..7cc27e07b047 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 @@ -193,11 +193,11 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q10_group_by_2---- column_names row_count distinct_count null_count {c_acctbal} 37967 37658 0 -{c_address} 37967 38065 0 -{c_comment} 37967 38086 0 +{c_address} 37967 37967 0 +{c_comment} 37967 37967 0 {c_custkey} 37967 37904 0 {c_name} 37967 37859 0 -{c_phone} 37967 38026 0 +{c_phone} 37967 37967 0 {n_name} 37967 25 0 {sum} 37967 37685 0 ~~~~ @@ -308,18 +308,18 @@ column_names row_count distinct_count null_count {c_address} 150000 149937 0 {c_comment} 150000 149323 0 {c_custkey} 150000 148813 0 -{c_name} 150000 151126 0 +{c_name} 150000 150000 0 {c_nationkey} 150000 25 0 -{c_phone} 150000 150872 0 +{c_phone} 150000 150000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_acctbal} 150000.00 1.00 140426.00 1.00 0.00 1.00 {c_address} 150000.00 1.00 150000.00 1.00 0.00 1.00 {c_comment} 150000.00 1.00 150000.00 1.00 0.00 1.00 {c_custkey} 150000.00 1.00 148813.00 1.00 0.00 1.00 -{c_name} 150000.00 1.00 150000.00 1.01 0.00 1.00 +{c_name} 150000.00 1.00 150000.00 1.00 0.00 1.00 {c_nationkey} 150000.00 1.00 25.00 1.00 0.00 1.00 -{c_phone} 150000.00 1.00 150000.00 1.01 0.00 1.00 +{c_phone} 150000.00 1.00 150000.00 1.00 0.00 1.00 ----Stats for q10_lookup_join_7---- column_names row_count distinct_count null_count diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 index 9753698ed384..afa5e0a03234 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 @@ -220,19 +220,19 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q11_group_by_3---- column_names row_count distinct_count null_count {ps_partkey} 29818 29669 0 -{sum} 29818 29969 0 +{sum} 29818 29818 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {ps_partkey} 29783.00 1.00 29783.00 1.00 0.00 1.00 -{sum} 29783.00 1.00 29783.00 1.01 0.00 1.00 +{sum} 29783.00 1.00 29783.00 1.00 0.00 1.00 ----Stats for q11_project_4---- column_names row_count distinct_count null_count -{column23} 31680 31888 0 +{column23} 31680 31680 0 {ps_partkey} 31680 29669 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{column23} 32258.00 1.02 31618.00 1.01 0.00 1.00 +{column23} 32258.00 1.02 31618.00 1.00 0.00 1.00 {ps_partkey} 32258.00 1.02 29783.00 1.00 0.00 1.00 ----Stats for q11_lookup_join_5---- @@ -320,10 +320,10 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q11_project_12---- column_names row_count distinct_count null_count -{column47} 31680 31888 0 +{column47} 31680 31680 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{column47} 32258.00 1.02 31618.00 1.01 0.00 1.00 +{column47} 32258.00 1.02 31618.00 1.00 0.00 1.00 ----Stats for q11_lookup_join_13---- column_names row_count distinct_count null_count diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 index ba07bcf90f07..84cfba4814e2 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 @@ -130,35 +130,35 @@ column_names row_count distinct_count null_count {c_custkey} 1533923 148813 0 {o_comment} 1533923 1454164 50005 {o_custkey} 1533923 99846 50005 -{o_orderkey} 1533923 1511432 50005 +{o_orderkey} 1533923 1483919 50005 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_custkey} 503988.00 3.04 <== 148813.00 1.00 0.00 1.00 {o_comment} 503988.00 3.04 <== 317522.00 4.58 <== 0.00 +Inf <== {o_custkey} 503988.00 3.04 <== 99627.00 1.00 0.00 +Inf <== -{o_orderkey} 503988.00 3.04 <== 317522.00 4.76 <== 0.00 +Inf <== +{o_orderkey} 503988.00 3.04 <== 317522.00 4.67 <== 0.00 +Inf <== ----Stats for q13_select_5---- column_names row_count distinct_count null_count {o_comment} 1483918 1454164 0 {o_custkey} 1483918 99846 0 -{o_orderkey} 1483918 1511432 0 +{o_orderkey} 1483918 1483918 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_comment} 500000.00 2.97 <== 500000.00 2.91 <== 0.00 1.00 {o_custkey} 500000.00 2.97 <== 99627.00 1.00 0.00 1.00 -{o_orderkey} 500000.00 2.97 <== 500000.00 3.02 <== 0.00 1.00 +{o_orderkey} 500000.00 2.97 <== 500000.00 2.97 <== 0.00 1.00 ----Stats for q13_scan_6---- column_names row_count distinct_count null_count {o_comment} 1500000 1469402 0 {o_custkey} 1500000 99846 0 -{o_orderkey} 1500000 1527270 0 +{o_orderkey} 1500000 1500000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_comment} 1500000.00 1.00 1473851.00 1.00 0.00 1.00 {o_custkey} 1500000.00 1.00 99853.00 1.00 0.00 1.00 -{o_orderkey} 1500000.00 1.00 1500000.00 1.02 0.00 1.00 +{o_orderkey} 1500000.00 1.00 1500000.00 1.00 0.00 1.00 ----Stats for q13_scan_7---- column_names row_count distinct_count null_count diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q14 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q14 index 56bcc730ef7c..98ca248973aa 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q14 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q14 @@ -119,7 +119,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q14_project_3---- column_names row_count distinct_count null_count {column30} 75983 12638 0 -{column32} 75983 76207 0 +{column32} 75983 75983 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {column30} 85001.00 1.12 85001.00 6.73 <== 0.00 1.00 diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 index 6dc2fa3ad1fd..601a3d1ccd45 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 @@ -224,16 +224,16 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q15_scan_3---- column_names row_count distinct_count null_count -{s_address} 10000 10027 0 -{s_name} 10000 9990 0 -{s_phone} 10000 10021 0 -{s_suppkey} 10000 9920 0 +{s_address} 9200 9200 0 +{s_name} 9200 9167 0 +{s_phone} 9200 9200 0 +{s_suppkey} 9200 9131 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{s_address} 10000.00 1.00 10000.00 1.00 0.00 1.00 -{s_name} 10000.00 1.00 9990.00 1.00 0.00 1.00 -{s_phone} 10000.00 1.00 9840.00 1.02 0.00 1.00 -{s_suppkey} 10000.00 1.00 9920.00 1.00 0.00 1.00 +{s_address} 10000.00 1.09 10000.00 1.09 0.00 1.00 +{s_name} 10000.00 1.09 9990.00 1.09 0.00 1.00 +{s_phone} 10000.00 1.09 9840.00 1.07 0.00 1.00 +{s_suppkey} 10000.00 1.09 9920.00 1.09 0.00 1.00 ----Stats for q15_sort_4---- column_names row_count distinct_count null_count @@ -256,7 +256,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q15_group_by_6---- column_names row_count distinct_count null_count {l_suppkey} 10000 9920 0 -{sum} 10000 10011 0 +{sum} 10000 10000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {l_suppkey} 9920.00 1.01 9920.00 1.00 0.00 1.00 @@ -305,7 +305,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q15_group_by_11---- column_names row_count distinct_count null_count {l_suppkey} 10000 9920 0 -{sum} 10000 10011 0 +{sum} 10000 10000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {l_suppkey} 9920.00 1.01 9920.00 1.00 0.00 1.00 diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 index f1267d53a38f..0345f978d981 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 @@ -179,7 +179,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_co column_names row_count distinct_count null_count {?column?} 6088 194 0 {l_linenumber} 6088 7 0 -{l_orderkey} 6088 6116 0 +{l_orderkey} 6088 6088 0 {l_partkey} 6088 204 0 {p_partkey} 6088 204 0 ~~~~ @@ -226,7 +226,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_ ----Stats for q17_lookup_join_8---- column_names row_count distinct_count null_count {l_linenumber} 6088 7 0 -{l_orderkey} 6088 6116 0 +{l_orderkey} 6088 6088 0 {l_partkey} 6088 204 0 {p_brand} 6088 1 0 {p_container} 6088 1 0 diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 index d7bc4acbbb14..964876f77e57 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 @@ -77,12 +77,21 @@ top-k │ ├── inner-join (merge) │ │ ├── save-table-name: q18_merge_join_4 │ │ ├── columns: o_orderkey:11(int!null) o_custkey:12(int!null) o_totalprice:14(float!null) o_orderdate:15(date!null) l_orderkey:22(int!null) l_quantity:26(float!null) - │ │ ├── left ordering: +11 - │ │ ├── right ordering: +22 + │ │ ├── left ordering: +22 + │ │ ├── right ordering: +11 │ │ ├── stats: [rows=2000764.33, distinct(11)=509090, null(11)=0, distinct(12)=99655.9712, null(12)=0, distinct(14)=497245.854, null(14)=0, distinct(15)=2406, null(15)=0, distinct(22)=509090, null(22)=0, distinct(26)=50, null(26)=0] │ │ ├── fd: (11)-->(12,14,15), (11)==(22), (22)==(11) + │ │ ├── scan lineitem + │ │ │ ├── save-table-name: q18_scan_5 + │ │ │ ├── columns: l_orderkey:22(int!null) l_quantity:26(float!null) + │ │ │ ├── stats: [rows=6002293, distinct(22)=1527270, null(22)=0, distinct(26)=50, null(26)=0] + │ │ │ │ histogram(22)= 0 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 + │ │ │ │ <--- 576 ------- 38535 ------- 66885 ------- 93380 ------- 127425 ------- 157218 ------- 184483 ------- 215330 ------- 252869 ------- 283878 ------- 313798 ------- 337056 ------- 372549 ------- 399591 ------- 426245 ------- 460578 ------- 498439 ------- 526049 ------- 554468 ------- 577921 ------- 609187 ------- 639524 ------- 665345 ------- 686180 ------- 721539 ------- 755680 ------- 782756 ------- 814496 ------- 845446 ------- 872130 ------- 910912 ------- 933697 ------- 965184 ------- 1000353 ------- 1038658 ------- 1073667 ------- 1097891 ------- 1131330 ------- 1157732 ------- 1179943 ------- 1206401 ------- 1230150 ------- 1261824 ------- 1293217 ------- 1326754 ------- 1357573 ------- 1390145 ------- 1429312 ------- 1460418 ------- 1491104 ------- 1523937 ------- 1559812 ------- 1591653 ------- 1615174 ------- 1646759 ------- 1670465 ------- 1696321 ------- 1724192 ------- 1748033 ------- 1777570 ------- 1807428 ------- 1836962 ------- 1872481 ------- 1902817 ------- 1928324 ------- 1960775 ------- 1985989 ------- 2019107 ------- 2044613 ------- 2071490 ------- 2101959 ------- 2135555 ------- 2164486 ------- 2186337 ------- 2213989 ------- 2246309 ------- 2276992 ------- 2306403 ------- 2329921 ------- 2354977 ------- 2380711 ------- 2410529 ------- 2437920 ------- 2462017 ------- 2483714 ------- 2513920 ------- 2542855 ------- 2574112 ------- 2596035 ------- 2625031 ------- 2658051 ------- 2695046 ------- 2725222 ------- 2754245 ------- 2777702 ------- 2804896 ------- 2844579 ------- 2873860 ------- 2903459 ------- 2933249 ------- 2965479 ------- 2996160 ------- 3022976 ------- 3053152 ------- 3083623 ------- 3111136 ------- 3144033 ------- 3180134 ------- 3209799 ------- 3239394 ------- 3270886 ------- 3297664 ------- 3329444 ------- 3357574 ------- 3380838 ------- 3412196 ------- 3438917 ------- 3462467 ------- 3498629 ------- 3530208 ------- 3562148 ------- 3589889 ------- 3621063 ------- 3655456 ------- 3686724 ------- 3709029 ------- 3738215 ------- 3767687 ------- 3804547 ------- 3831142 ------- 3875111 ------- 3905605 ------- 3933795 ------- 3966593 ------- 3995558 ------- 4020134 ------- 4052513 ------- 4078949 ------- 4114208 ------- 4149762 ------- 4176135 ------- 4207782 ------- 4241376 ------- 4270502 ------- 4304167 ------- 4333669 ------- 4362818 ------- 4393537 ------- 4423076 ------- 4452064 ------- 4491143 ------- 4522723 ------- 4550883 ------- 4581382 ------- 4616002 ------- 4649410 ------- 4680485 ------- 4715584 ------- 4740036 ------- 4771554 ------- 4799461 ------- 4826690 ------- 4855525 ------- 4887974 ------- 4917479 ------- 4950885 ------- 4984195 ------- 5010113 ------- 5033571 ------- 5065472 ------- 5100512 ------- 5129413 ------- 5160069 ------- 5186596 ------- 5221538 ------- 5252964 ------- 5284069 ------- 5314051 ------- 5353026 ------- 5388961 ------- 5424644 ------- 5452676 ------- 5483553 ------- 5516612 ------- 5551041 ------- 5579878 ------- 5612576 ------- 5643427 ------- 5673666 ------- 5709218 ------- 5737221 ------- 5766119 ------- 5795044 ------- 5826560 ------- 5855943 ------- 5889604 ------- 5917607 ------- 5942535 ------- 5969639 ------- 5999557 + │ │ │ │ histogram(26)= 0 1.1524e+05 5.7772e+06 1.0984e+05 + │ │ │ │ <----- 1.0 ----------------- 50.0 -- + │ │ │ └── ordering: +22 │ │ ├── semi-join (merge) - │ │ │ ├── save-table-name: q18_merge_join_5 + │ │ │ ├── save-table-name: q18_merge_join_6 │ │ │ ├── columns: o_orderkey:11(int!null) o_custkey:12(int!null) o_totalprice:14(float!null) o_orderdate:15(date!null) │ │ │ ├── left ordering: +11 │ │ │ ├── right ordering: +40 @@ -91,7 +100,7 @@ top-k │ │ │ ├── fd: (11)-->(12,14,15) │ │ │ ├── ordering: +11 │ │ │ ├── scan orders - │ │ │ │ ├── save-table-name: q18_scan_6 + │ │ │ │ ├── save-table-name: q18_scan_7 │ │ │ │ ├── columns: o_orderkey:11(int!null) o_custkey:12(int!null) o_totalprice:14(float!null) o_orderdate:15(date!null) │ │ │ │ ├── stats: [rows=1500000, distinct(11)=1500000, null(11)=0, distinct(12)=99853, null(12)=0, distinct(14)=1469395, null(14)=0, distinct(15)=2406, null(15)=0] │ │ │ │ │ histogram(11)= 0 0 0 0.99998 7461.9 0.99998 7285.9 0.99998 7544.9 0.99998 7589.9 0.99998 7222.9 0.99998 7324.9 0.99998 7506.9 0.99998 7351.9 0.99998 7777.9 0.99998 7576.9 0.99998 7731.9 0.99998 7694.9 0.99998 7586.9 0.99998 7569.9 0.99998 7757.9 0.99998 7624.9 0.99998 7506.9 0.99998 7245.9 0.99998 7820.9 0.99998 7539.9 0.99998 7455.9 0.99998 7589.9 0.99998 7740.9 0.99998 7604.9 0.99998 7710.9 0.99998 7709.9 0.99998 7708.9 0.99998 7490.9 0.99998 7527.9 0.99998 7331.9 0.99998 7311.9 0.99998 7576.9 0.99998 7545.9 0.99998 7277.9 0.99998 7392.9 0.99998 7508.9 0.99998 7622.9 0.99998 7581.9 0.99998 7775.9 0.99998 7523.9 0.99998 7568.9 0.99998 7483.9 0.99998 7662.9 0.99998 7368.9 0.99998 7470.9 0.99998 7380.9 0.99998 7647.9 0.99998 7381.9 0.99998 7635.9 0.99998 7490.9 0.99998 7446.9 0.99998 7526.9 0.99998 7441.9 0.99998 7265.9 0.99998 7960.9 0.99998 7251.9 0.99998 7514.9 0.99998 7294.9 0.99998 7502.9 0.99998 7285.9 0.99998 7320.9 0.99998 7235.9 0.99998 7451.9 0.99998 7810.9 0.99998 7378.9 0.99998 7418.9 0.99998 7661.9 0.99998 7404.9 0.99998 7432.9 0.99998 7579.9 0.99998 7836.9 0.99998 7445.9 0.99998 7355.9 0.99998 7617.9 0.99998 7110.9 0.99998 7398.9 0.99998 7622.9 0.99998 7655.9 0.99998 7433.9 0.99998 7656.9 0.99998 7404.9 0.99998 7474.9 0.99998 7572.9 0.99998 7688.9 0.99998 7559.9 0.99998 7414.9 0.99998 7523.9 0.99998 7558.9 0.99998 7330.9 0.99998 7587.9 0.99998 7388.9 0.99998 7327.9 0.99998 7671.9 0.99998 7523.9 0.99998 7687.9 0.99998 7524.9 0.99998 7614.9 0.99998 7463.9 0.99998 7594.9 0.99998 7372.9 0.99998 7670.9 0.99998 7310.9 0.99998 7270.9 0.99998 7399.9 0.99998 7688.9 0.99998 7487.9 0.99998 7556.9 0.99998 7365.9 0.99998 7521.9 0.99998 7762.9 0.99998 7386.9 0.99998 7399.9 0.99998 7562.9 0.99998 7502.9 0.99998 7201.9 0.99998 7595.9 0.99998 7525.9 0.99998 7451.9 0.99998 7280.9 0.99998 7307.9 0.99998 7386.9 0.99998 7345.9 0.99998 7383.9 0.99998 7530.9 0.99998 7706.9 0.99998 7581.9 0.99998 7512.9 0.99998 7536.9 0.99998 7210.9 0.99998 7689.9 0.99998 7658.9 0.99998 7358.9 0.99998 7646.9 0.99998 7252.9 0.99998 7327.9 0.99998 7525.9 0.99998 7564.9 0.99998 7524.9 0.99998 7438.9 0.99998 7493.9 0.99998 7419.9 0.99998 7509.9 0.99998 7595.9 0.99998 7396.9 0.99998 7378.9 0.99998 7330.9 0.99998 7387.9 0.99998 7552.9 0.99998 7330.9 0.99998 7431.9 0.99998 7773.9 0.99998 7853.9 0.99998 7562.9 0.99998 7548.9 0.99998 7847.9 0.99998 7933.9 0.99998 7768.9 0.99998 7738.9 0.99998 7480.9 0.99998 7679.9 0.99998 7663.9 0.99998 7587.9 0.99998 7527.9 0.99998 7466.9 0.99998 7444.9 0.99998 7519.9 0.99998 7830.9 0.99998 7568.9 0.99998 7671.9 0.99998 7637.9 0.99998 7462.9 0.99998 7851.9 0.99998 7483.9 0.99998 7765.9 0.99998 7451.9 0.99998 8050.9 0.99998 7644.9 0.99998 7724.9 0.99998 7471.9 0.99998 7517.9 0.99998 7830.9 0.99998 7387.9 0.99998 7749.9 0.99998 7545.9 0.99998 7718.9 0.99998 7384.9 0.99998 7464.9 0.99998 7467.9 0.99998 7809.9 0.99998 7766.9 0.99998 7511.9 0.99998 7641.9 0.99998 7711.9 0.99998 7729.9 0.99998 7631.9 0.99998 7734.9 0.99998 7931.9 0.99998 7586.9 0.99998 7964.9 0.99998 0 0 @@ -106,14 +115,14 @@ top-k │ │ │ │ ├── fd: (11)-->(12,14,15) │ │ │ │ └── ordering: +11 │ │ │ ├── select - │ │ │ │ ├── save-table-name: q18_select_7 + │ │ │ │ ├── save-table-name: q18_select_8 │ │ │ │ ├── columns: l_orderkey:40(int!null) sum:58(float!null) │ │ │ │ ├── stats: [rows=509090, distinct(40)=509090, null(40)=0, distinct(58)=509090, null(58)=0] │ │ │ │ ├── key: (40) │ │ │ │ ├── fd: (40)-->(58) │ │ │ │ ├── ordering: +40 │ │ │ │ ├── group-by - │ │ │ │ │ ├── save-table-name: q18_group_by_8 + │ │ │ │ │ ├── save-table-name: q18_group_by_9 │ │ │ │ │ ├── columns: l_orderkey:40(int!null) sum:58(float!null) │ │ │ │ │ ├── grouping columns: l_orderkey:40(int!null) │ │ │ │ │ ├── stats: [rows=1527270, distinct(40)=1527270, null(40)=0, distinct(58)=1527270, null(58)=0] @@ -121,7 +130,7 @@ top-k │ │ │ │ │ ├── fd: (40)-->(58) │ │ │ │ │ ├── ordering: +40 │ │ │ │ │ ├── scan lineitem - │ │ │ │ │ │ ├── save-table-name: q18_scan_9 + │ │ │ │ │ │ ├── save-table-name: q18_scan_10 │ │ │ │ │ │ ├── columns: l_orderkey:40(int!null) l_quantity:44(float!null) │ │ │ │ │ │ ├── stats: [rows=6002293, distinct(40)=1527270, null(40)=0, distinct(44)=50, null(44)=0] │ │ │ │ │ │ │ histogram(40)= 0 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 @@ -135,15 +144,6 @@ top-k │ │ │ │ └── filters │ │ │ │ └── sum:58 > 300.0 [type=bool, outer=(58), constraints=(/58: [/300.00000000000006 - ]; tight)] │ │ │ └── filters (true) - │ │ ├── scan lineitem - │ │ │ ├── save-table-name: q18_scan_10 - │ │ │ ├── columns: l_orderkey:22(int!null) l_quantity:26(float!null) - │ │ │ ├── stats: [rows=6002293, distinct(22)=1527270, null(22)=0, distinct(26)=50, null(26)=0] - │ │ │ │ histogram(22)= 0 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 29411 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 30011 600 - │ │ │ │ <--- 576 ------- 38535 ------- 66885 ------- 93380 ------- 127425 ------- 157218 ------- 184483 ------- 215330 ------- 252869 ------- 283878 ------- 313798 ------- 337056 ------- 372549 ------- 399591 ------- 426245 ------- 460578 ------- 498439 ------- 526049 ------- 554468 ------- 577921 ------- 609187 ------- 639524 ------- 665345 ------- 686180 ------- 721539 ------- 755680 ------- 782756 ------- 814496 ------- 845446 ------- 872130 ------- 910912 ------- 933697 ------- 965184 ------- 1000353 ------- 1038658 ------- 1073667 ------- 1097891 ------- 1131330 ------- 1157732 ------- 1179943 ------- 1206401 ------- 1230150 ------- 1261824 ------- 1293217 ------- 1326754 ------- 1357573 ------- 1390145 ------- 1429312 ------- 1460418 ------- 1491104 ------- 1523937 ------- 1559812 ------- 1591653 ------- 1615174 ------- 1646759 ------- 1670465 ------- 1696321 ------- 1724192 ------- 1748033 ------- 1777570 ------- 1807428 ------- 1836962 ------- 1872481 ------- 1902817 ------- 1928324 ------- 1960775 ------- 1985989 ------- 2019107 ------- 2044613 ------- 2071490 ------- 2101959 ------- 2135555 ------- 2164486 ------- 2186337 ------- 2213989 ------- 2246309 ------- 2276992 ------- 2306403 ------- 2329921 ------- 2354977 ------- 2380711 ------- 2410529 ------- 2437920 ------- 2462017 ------- 2483714 ------- 2513920 ------- 2542855 ------- 2574112 ------- 2596035 ------- 2625031 ------- 2658051 ------- 2695046 ------- 2725222 ------- 2754245 ------- 2777702 ------- 2804896 ------- 2844579 ------- 2873860 ------- 2903459 ------- 2933249 ------- 2965479 ------- 2996160 ------- 3022976 ------- 3053152 ------- 3083623 ------- 3111136 ------- 3144033 ------- 3180134 ------- 3209799 ------- 3239394 ------- 3270886 ------- 3297664 ------- 3329444 ------- 3357574 ------- 3380838 ------- 3412196 ------- 3438917 ------- 3462467 ------- 3498629 ------- 3530208 ------- 3562148 ------- 3589889 ------- 3621063 ------- 3655456 ------- 3686724 ------- 3709029 ------- 3738215 ------- 3767687 ------- 3804547 ------- 3831142 ------- 3875111 ------- 3905605 ------- 3933795 ------- 3966593 ------- 3995558 ------- 4020134 ------- 4052513 ------- 4078949 ------- 4114208 ------- 4149762 ------- 4176135 ------- 4207782 ------- 4241376 ------- 4270502 ------- 4304167 ------- 4333669 ------- 4362818 ------- 4393537 ------- 4423076 ------- 4452064 ------- 4491143 ------- 4522723 ------- 4550883 ------- 4581382 ------- 4616002 ------- 4649410 ------- 4680485 ------- 4715584 ------- 4740036 ------- 4771554 ------- 4799461 ------- 4826690 ------- 4855525 ------- 4887974 ------- 4917479 ------- 4950885 ------- 4984195 ------- 5010113 ------- 5033571 ------- 5065472 ------- 5100512 ------- 5129413 ------- 5160069 ------- 5186596 ------- 5221538 ------- 5252964 ------- 5284069 ------- 5314051 ------- 5353026 ------- 5388961 ------- 5424644 ------- 5452676 ------- 5483553 ------- 5516612 ------- 5551041 ------- 5579878 ------- 5612576 ------- 5643427 ------- 5673666 ------- 5709218 ------- 5737221 ------- 5766119 ------- 5795044 ------- 5826560 ------- 5855943 ------- 5889604 ------- 5917607 ------- 5942535 ------- 5969639 ------- 5999557 - │ │ │ │ histogram(26)= 0 1.1524e+05 5.7772e+06 1.0984e+05 - │ │ │ │ <----- 1.0 ----------------- 50.0 -- - │ │ │ └── ordering: +22 │ │ └── filters (true) │ ├── scan customer │ │ ├── save-table-name: q18_scan_11 @@ -241,7 +241,16 @@ column_names row_count_est row_count_err distinct_count_est distinct_count {o_orderkey} 2000764.00 5014.45 <== 509090.00 8931.40 <== 0.00 1.00 {o_totalprice} 2000764.00 5014.45 <== 497246.00 8723.61 <== 0.00 1.00 -----Stats for q18_merge_join_5---- +----Stats for q18_scan_5---- +column_names row_count distinct_count null_count +{l_orderkey} 5986300 1523585 0 +{l_quantity} 5986300 50 0 +~~~~ +column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err +{l_orderkey} 6002293.00 1.00 1527270.00 1.00 0.00 1.00 +{l_quantity} 6002293.00 1.00 50.00 1.00 0.00 1.00 + +----Stats for q18_merge_join_6---- column_names row_count distinct_count null_count {o_custkey} 57 57 0 {o_orderdate} 57 57 0 @@ -254,20 +263,20 @@ column_names row_count_est row_count_err distinct_count_est distinct_count {o_orderkey} 509090.00 8931.40 <== 509090.00 8931.40 <== 0.00 1.00 {o_totalprice} 509090.00 8931.40 <== 507049.00 8895.60 <== 0.00 1.00 -----Stats for q18_scan_6---- +----Stats for q18_scan_7---- column_names row_count distinct_count null_count {o_custkey} 1497000 99846 0 {o_orderdate} 1497000 2406 0 -{o_orderkey} 1497000 1524557 0 +{o_orderkey} 1497000 1497000 0 {o_totalprice} 1497000 1456760 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {o_custkey} 1500000.00 1.00 99853.00 1.00 0.00 1.00 {o_orderdate} 1500000.00 1.00 2406.00 1.00 0.00 1.00 -{o_orderkey} 1500000.00 1.00 1500000.00 1.02 0.00 1.00 +{o_orderkey} 1500000.00 1.00 1500000.00 1.00 0.00 1.00 {o_totalprice} 1500000.00 1.00 1469395.00 1.01 0.00 1.00 -----Stats for q18_select_7---- +----Stats for q18_select_8---- column_names row_count distinct_count null_count {l_orderkey} 57 57 0 {sum} 57 18 0 @@ -276,16 +285,16 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e {l_orderkey} 509090.00 8931.40 <== 509090.00 8931.40 <== 0.00 1.00 {sum} 509090.00 8931.40 <== 509090.00 28282.78 <== 0.00 1.00 -----Stats for q18_group_by_8---- +----Stats for q18_group_by_9---- column_names row_count distinct_count null_count -{l_orderkey} 1500000 1527270 0 +{l_orderkey} 1500000 1500000 0 {sum} 1500000 318 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{l_orderkey} 1527270.00 1.02 1527270.00 1.00 0.00 1.00 +{l_orderkey} 1527270.00 1.02 1527270.00 1.02 0.00 1.00 {sum} 1527270.00 1.02 1527270.00 4802.74 <== 0.00 1.00 -----Stats for q18_scan_9---- +----Stats for q18_scan_10---- column_names row_count distinct_count null_count {l_orderkey} 6001215 1527270 0 {l_quantity} 6001215 50 0 @@ -294,22 +303,13 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e {l_orderkey} 6002293.00 1.00 1527270.00 1.00 0.00 1.00 {l_quantity} 6002293.00 1.00 50.00 1.00 0.00 1.00 -----Stats for q18_scan_10---- -column_names row_count distinct_count null_count -{l_orderkey} 5986300 1523585 0 -{l_quantity} 5986300 50 0 -~~~~ -column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err -{l_orderkey} 6002293.00 1.00 1527270.00 1.00 0.00 1.00 -{l_quantity} 6002293.00 1.00 50.00 1.00 0.00 1.00 - ----Stats for q18_scan_11---- column_names row_count distinct_count null_count {c_custkey} 150000 148813 0 -{c_name} 150000 151126 0 +{c_name} 150000 150000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_custkey} 150000.00 1.00 148813.00 1.00 0.00 1.00 -{c_name} 150000.00 1.00 150000.00 1.01 0.00 1.00 +{c_name} 150000.00 1.00 150000.00 1.00 0.00 1.00 ---- ---- diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 index 24b283ec0322..242766cdfbf9 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 @@ -217,7 +217,7 @@ column_names row_count distinct_count null_count {s_address} 4397 4369 0 {s_name} 4397 4373 0 {s_nationkey} 4397 25 0 -{s_suppkey} 4397 4434 0 +{s_suppkey} 4397 4397 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {s_address} 0.00 +Inf <== 0.00 +Inf <== 0.00 1.00 @@ -227,11 +227,11 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_ ----Stats for q20_lookup_join_5---- column_names row_count distinct_count null_count -{ps_suppkey} 4397 4434 0 +{ps_suppkey} 4397 4397 0 {s_address} 4397 4369 0 {s_name} 4397 4373 0 {s_nationkey} 4397 25 0 -{s_suppkey} 4397 4434 0 +{s_suppkey} 4397 4397 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {ps_suppkey} 0.00 +Inf <== 0.00 +Inf <== 0.00 1.00 @@ -242,7 +242,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_ ----Stats for q20_distinct_on_6---- column_names row_count distinct_count null_count -{ps_suppkey} 4397 4434 0 +{ps_suppkey} 4397 4397 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {ps_suppkey} 0.00 +Inf <== 0.00 +Inf <== 0.00 1.00 diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 index aa6d2a8f82a3..67686ec81215 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 @@ -179,7 +179,7 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e column_names row_count distinct_count null_count {c_acctbal} 6384 6304 0 {c_custkey} 6384 6359 0 -{c_phone} 6384 6428 0 +{c_phone} 6384 6384 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_acctbal} 0.00 +Inf <== 0.00 +Inf <== 0.00 1.00 @@ -189,24 +189,24 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q22_select_5---- column_names row_count distinct_count null_count {c_acctbal} 19000 18527 0 -{c_custkey} 19000 19097 0 -{c_phone} 19000 19095 0 +{c_custkey} 19000 19000 0 +{c_phone} 19000 19000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_acctbal} 16667.00 1.14 16667.00 1.11 0.00 1.00 -{c_custkey} 16667.00 1.14 16659.00 1.15 0.00 1.00 -{c_phone} 16667.00 1.14 16667.00 1.15 0.00 1.00 +{c_custkey} 16667.00 1.14 16659.00 1.14 0.00 1.00 +{c_phone} 16667.00 1.14 16667.00 1.14 0.00 1.00 ----Stats for q22_scan_6---- column_names row_count distinct_count null_count {c_acctbal} 150000 140628 0 {c_custkey} 150000 148813 0 -{c_phone} 150000 150872 0 +{c_phone} 150000 150000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_acctbal} 150000.00 1.00 140426.00 1.00 0.00 1.00 {c_custkey} 150000.00 1.00 148813.00 1.00 0.00 1.00 -{c_phone} 150000.00 1.00 150000.00 1.01 0.00 1.00 +{c_phone} 150000.00 1.00 150000.00 1.00 0.00 1.00 ----Stats for q22_scalar_group_by_7---- column_names row_count distinct_count null_count @@ -227,10 +227,10 @@ column_names row_count_est row_count_err distinct_count_est distinct_count_e ----Stats for q22_scan_9---- column_names row_count distinct_count null_count {c_acctbal} 150000 140628 0 -{c_phone} 150000 150872 0 +{c_phone} 150000 150000 0 ~~~~ column_names row_count_est row_count_err distinct_count_est distinct_count_err null_count_est null_count_err {c_acctbal} 150000.00 1.00 140426.00 1.00 0.00 1.00 -{c_phone} 150000.00 1.00 150000.00 1.01 0.00 1.00 +{c_phone} 150000.00 1.00 150000.00 1.00 0.00 1.00 ---- ---- diff --git a/pkg/sql/opt/norm/testdata/rules/combo b/pkg/sql/opt/norm/testdata/rules/combo index 4e6da2cc34a4..a21ed51e426c 100644 --- a/pkg/sql/opt/norm/testdata/rules/combo +++ b/pkg/sql/opt/norm/testdata/rules/combo @@ -339,7 +339,7 @@ ReorderJoins └── k:1 = x:8 [outer=(1,8), constraints=(/1: (/NULL - ]; /8: (/NULL - ]), fd=(1)==(8), (8)==(1)] ================================================================================ GenerateMergeJoins - Cost: 2179.51 + Cost: 2180.50 ================================================================================ project ├── columns: s:4 @@ -2947,7 +2947,7 @@ CommuteLeftJoin (higher cost) └── CASE WHEN bool_or:14 THEN true WHEN bool_or:14 IS NULL THEN false ELSE CAST(NULL AS BOOL) END [as=r:12, outer=(14)] ================================================================================ GenerateMergeJoins - Cost: 2249.31 + Cost: 2248.64 ================================================================================ project ├── columns: r:12 @@ -3317,7 +3317,7 @@ GenerateMergeJoins (higher cost) └── CASE WHEN bool_or:14 THEN true WHEN bool_or:14 IS NULL THEN false ELSE CAST(NULL AS BOOL) END [as=r:12, outer=(14)] ================================================================================ GenerateStreamingGroupBy - Cost: 2239.16 + Cost: 2238.49 ================================================================================ project ├── columns: r:12 @@ -3365,7 +3365,7 @@ GenerateStreamingGroupBy └── CASE WHEN bool_or:14 THEN true WHEN bool_or:14 IS NULL THEN false ELSE CAST(NULL AS BOOL) END [as=r:12, outer=(14)] ================================================================================ Final best expression - Cost: 2239.16 + Cost: 2238.49 ================================================================================ project ├── columns: r:12 diff --git a/pkg/sql/opt/norm/testdata/rules/inline b/pkg/sql/opt/norm/testdata/rules/inline index 1c013f7bb2f8..8fc4d29e2031 100644 --- a/pkg/sql/opt/norm/testdata/rules/inline +++ b/pkg/sql/opt/norm/testdata/rules/inline @@ -874,6 +874,10 @@ project └── inner-join (hash) ├── scan t63794_a [as=a] ├── inner-join (merge) + │ ├── project + │ │ ├── scan t63794_bc@t63794_bc_b_idx + │ │ └── projections + │ │ └── b % 2 │ ├── inner-join (merge) │ │ ├── project │ │ │ ├── scan t63794_bc@t63794_bc_b_idx @@ -884,10 +888,6 @@ project │ │ │ └── projections │ │ │ └── b % 2 │ │ └── filters (true) - │ ├── project - │ │ ├── scan t63794_bc@t63794_bc_b_idx - │ │ └── projections - │ │ └── b % 2 │ └── filters (true) └── filters └── a = b diff --git a/pkg/sql/opt/opbench/testdata/tpch-merge-join.csv b/pkg/sql/opt/opbench/testdata/tpch-merge-join.csv index 508f2ada0165..75014acf4b75 100644 --- a/pkg/sql/opt/opbench/testdata/tpch-merge-join.csv +++ b/pkg/sql/opt/opbench/testdata/tpch-merge-join.csv @@ -1,274 +1,274 @@ lineitem_rows,supplier_rows,estimated,actual -10,10,29.530000,0.003806 -1000000,10,1050023.916785,0.006431 -2000000,10,2100028.903570,0.005838 -3000000,10,3150033.890355,0.005697 -4000000,10,4200038.877140,0.005921 -5000000,10,5250043.863925,0.006086 -6000000,10,6300048.850710,0.005889 -7000000,10,7350053.837495,0.005639 -8000000,10,8400058.824280,0.005796 -9000000,10,9450063.811065,0.005771 -10000000,10,10500068.797850,0.005664 -11000000,10,11550073.784635,0.005645 -12000000,10,12600078.771420,0.005754 -10,1000,1108.630000,0.006430 -1000000,1000,1051596.708502,0.357861 -2000000,1000,2102095.387004,0.368291 -3000000,1000,3152594.065506,0.353418 -4000000,1000,4203092.744008,0.349349 -5000000,1000,5253591.432510,0.344271 -6000000,1000,6304090.111012,0.343312 -7000000,1000,7354588.789514,0.348113 -8000000,1000,8405087.468016,0.348981 -9000000,1000,9455586.146518,0.349747 -10000000,1000,10506084.825020,0.347959 -11000000,1000,11556583.503522,0.349222 -12000000,1000,12607082.182024,0.346095 -10,2000,2198.630000,0.006641 -1000000,2000,1053185.387004,0.592044 -2000000,2000,2104182.744008,0.709176 -3000000,2000,3155180.111012,0.713248 -4000000,2000,4206177.468016,0.717638 -5000000,2000,5257174.825020,0.694552 -6000000,2000,6308172.182024,0.695888 -7000000,2000,7359169.539028,0.693536 -8000000,2000,8410166.896032,0.696920 -9000000,2000,9461164.253035,0.728545 -10000000,2000,10512161.610039,0.695813 -11000000,2000,11563158.967043,0.694232 -12000000,2000,12614156.324047,0.696031 -10,3000,3288.630000,0.006987 -1000000,3000,1054774.065506,0.585714 -2000000,3000,2106270.111012,1.057190 -3000000,3000,3157766.146518,1.068748 -4000000,3000,4209262.182024,1.058989 -5000000,3000,5260758.217530,1.048694 -6000000,3000,6312254.253035,1.048794 -7000000,3000,7363750.288541,1.067393 -8000000,3000,8415246.324047,1.048399 -9000000,3000,9466742.359553,1.049247 -10000000,3000,10518238.395059,1.043001 -11000000,3000,11569734.430565,1.048124 -12000000,3000,12621230.466071,1.037962 -10,4000,4378.630000,0.007370 -1000000,4000,1056362.744008,0.575646 -2000000,4000,2108357.468016,1.168315 -3000000,4000,3160352.182024,1.450428 -4000000,4000,4212346.896032,1.430971 -5000000,4000,5264341.610039,1.482457 -6000000,4000,6316336.324047,1.428430 -7000000,4000,7368331.038055,1.414905 -8000000,4000,8420325.752063,1.442082 -9000000,4000,9472320.466071,1.432695 -10000000,4000,10524315.180079,1.426001 -11000000,4000,11576309.894087,1.428244 -12000000,4000,12628304.608095,1.427556 -10,5000,5468.630000,0.003567 -1000000,5000,1057951.432510,0.594622 -2000000,5000,2110444.825020,1.175322 -3000000,5000,3162938.217530,1.812499 -4000000,5000,4215431.610039,1.801634 -5000000,5000,5267925.002549,1.809796 -6000000,5000,6320418.395059,1.794572 -7000000,5000,7372911.787569,1.840704 -8000000,5000,8425405.180079,1.785859 -9000000,5000,9477898.572589,1.777266 -10000000,5000,10530391.965098,1.779596 -11000000,5000,11582885.357608,1.778569 -12000000,5000,12635378.750118,1.786059 -10,6000,6558.630000,0.006327 -1000000,6000,1059540.111012,0.599244 -2000000,6000,2112532.182024,1.183138 -3000000,6000,3165524.253035,1.807208 -4000000,6000,4218516.324047,2.161016 -5000000,6000,5271508.395059,2.144455 -6000000,6000,6324500.466071,2.152747 -7000000,6000,7377492.537083,2.151892 -8000000,6000,8430484.608095,2.150061 -9000000,6000,9483476.679106,2.165961 -10000000,6000,10536468.750118,2.183265 -11000000,6000,11589460.821130,2.162014 -12000000,6000,12642452.892142,2.142501 -10,7000,7648.630000,0.004163 -1000000,7000,1061128.789514,0.587727 -2000000,7000,2114619.539028,1.208202 -3000000,7000,3168110.288541,1.809781 -4000000,7000,4221601.038055,2.409570 -5000000,7000,5275091.787569,2.556088 -6000000,7000,6328582.537083,2.531446 -7000000,7000,7382073.286597,2.536571 -8000000,7000,8435564.036110,2.531997 -9000000,7000,9489054.785624,2.559670 -10000000,7000,10542545.535138,2.581251 -11000000,7000,11596036.284652,2.534237 -12000000,7000,12649527.034165,2.566188 -10,8000,8738.630000,0.004680 -1000000,8000,1062717.468016,0.587327 -2000000,8000,2116706.896032,1.198249 -3000000,8000,3170696.324047,1.825639 -4000000,8000,4224685.752063,2.455271 -5000000,8000,5278675.180079,2.977414 -6000000,8000,6332664.608095,2.940780 -7000000,8000,7386654.036110,2.910592 -8000000,8000,8440643.464126,2.907493 -9000000,8000,9494632.892142,2.915656 -10000000,8000,10548622.320158,2.882350 -11000000,8000,11602611.748173,2.894606 -12000000,8000,12656601.176189,2.962744 -10,9000,9828.630000,0.004759 -1000000,9000,1064306.146518,0.618753 -2000000,9000,2118794.253035,1.208965 -3000000,9000,3173282.359553,1.786105 -4000000,9000,4227770.466071,2.419236 -5000000,9000,5282258.572589,3.034678 -6000000,9000,6336746.679106,3.284500 -7000000,9000,7391234.785624,3.277245 -8000000,9000,8445722.892142,3.294880 -9000000,9000,9500210.998660,3.232375 -10000000,9000,10554699.105177,3.245753 -11000000,9000,11609187.211695,3.294465 -12000000,9000,12663675.318213,3.291908 -10,10000,10918.630000,0.004897 -1000000,10000,1065894.825020,0.603518 -2000000,10000,2120881.610039,1.210479 -3000000,10000,3175868.395059,1.805605 -4000000,10000,4230855.180079,2.436298 -5000000,10000,5285841.965098,3.025817 -6000000,10000,6340828.750118,3.617593 -7000000,10000,7395815.535138,3.647803 -8000000,10000,8450802.320158,3.595432 -9000000,10000,9505789.105177,3.589260 -10000000,10000,10560775.890197,3.642606 -11000000,10000,11615762.675217,3.656523 -12000000,10000,12670749.460236,3.674766 -10,11000,12008.630000,0.004856 -1000000,11000,1067483.503522,0.602559 -2000000,11000,2122968.967043,1.212686 -3000000,11000,3178454.430565,1.830397 -4000000,11000,4233939.894087,2.424751 -5000000,11000,5289425.357608,3.028793 -6000000,11000,6344910.821130,3.655282 -7000000,11000,7400396.284652,4.003521 -8000000,11000,8455881.748173,4.038612 -9000000,11000,9511367.211695,4.108167 -10000000,11000,10566852.675217,4.075739 -11000000,11000,11622338.138738,4.005097 -12000000,11000,12677823.602260,3.975355 -10,12000,13098.630000,0.004883 -1000000,12000,1069072.182024,0.587806 -2000000,12000,2125056.324047,1.192245 -3000000,12000,3181040.466071,1.775383 -4000000,12000,4237024.608095,2.430726 -5000000,12000,5293008.750118,3.024520 -6000000,12000,6348992.892142,3.649697 -7000000,12000,7404977.034165,4.280330 -8000000,12000,8460961.176189,4.387758 -9000000,12000,9516945.318213,4.435012 -10000000,12000,10572929.460236,4.370401 -11000000,12000,11628913.602260,4.335358 -12000000,12000,12684897.744284,4.354250 -10,13000,14188.630000,0.004801 -1000000,13000,1070660.860526,0.583007 -2000000,13000,2127143.681051,1.183126 -3000000,13000,3183626.501577,1.789584 -4000000,13000,4240109.322102,2.432458 -5000000,13000,5296592.142628,3.059125 -6000000,13000,6353074.963154,3.632407 -7000000,13000,7409557.783679,4.231531 -8000000,13000,8466040.604205,4.733011 -9000000,13000,9522523.424730,4.752653 -10000000,13000,10579006.245256,4.673748 -11000000,13000,11635489.065782,4.748541 -12000000,13000,12691971.886307,4.754900 -10,14000,15278.630000,0.005019 -1000000,14000,1072249.539028,0.592332 -2000000,14000,2129231.038055,1.221768 -3000000,14000,3186212.537083,1.797916 -4000000,14000,4243194.036110,2.408206 -5000000,14000,5300175.535138,3.049332 -6000000,14000,6357157.034165,3.649674 -7000000,14000,7414138.533193,4.241669 -8000000,14000,8471120.032221,4.840566 -9000000,14000,9528101.531248,5.124709 -10000000,14000,10585083.030276,5.159288 -11000000,14000,11642064.529303,5.080080 -12000000,14000,12699046.028331,5.143669 -10,15000,16368.630000,0.005462 -1000000,15000,1073838.217530,0.605246 -2000000,15000,2131318.395059,1.225734 -3000000,15000,3188798.572589,1.786052 -4000000,15000,4246278.750118,2.439597 -5000000,15000,5303758.927648,3.004971 -6000000,15000,6361239.105177,3.634920 -7000000,15000,7418719.282707,4.365920 -8000000,15000,8476199.460236,4.878452 -9000000,15000,9533679.637766,5.496486 -10000000,15000,10591159.815295,5.522539 -11000000,15000,11648639.992825,5.512179 -12000000,15000,12706120.170355,5.468712 -10,16000,17458.630000,0.006121 -1000000,16000,1075426.896032,0.609126 -2000000,16000,2133405.752063,1.216694 -3000000,16000,3191384.608095,1.802017 -4000000,16000,4249363.464126,2.409950 -5000000,16000,5307342.320158,3.029878 -6000000,16000,6365321.176189,3.684986 -7000000,16000,7423300.032221,4.270792 -8000000,16000,8481278.888252,4.984693 -9000000,16000,9539257.744284,5.500891 -10000000,16000,10597236.600315,5.880840 -11000000,16000,11655215.456347,5.863849 -12000000,16000,12713194.312378,5.893369 -10,17000,18548.630000,0.006420 -1000000,17000,1077015.574533,0.608901 -2000000,17000,2135493.109067,1.209490 -3000000,17000,3193970.643600,1.811201 -4000000,17000,4252448.178134,2.425002 -5000000,17000,5310925.712667,3.020078 -6000000,17000,6369403.247201,3.647157 -7000000,17000,7427880.781734,4.302938 -8000000,17000,8486358.316268,4.916599 -9000000,17000,9544835.850801,5.473640 -10000000,17000,10603313.385335,6.050727 -11000000,17000,11661790.919868,6.218971 -12000000,17000,12720268.454402,6.262910 -10,18000,19638.630000,0.006747 -1000000,18000,1078604.253035,0.631874 -2000000,18000,2137580.466071,1.229240 -3000000,18000,3196556.679106,1.832322 -4000000,18000,4255532.892142,2.417640 -5000000,18000,5314509.105177,3.028015 -6000000,18000,6373485.318213,3.652809 -7000000,18000,7432461.531248,4.239974 -8000000,18000,8491437.744284,4.851297 -9000000,18000,9550413.957319,5.480013 -10000000,18000,10609390.170355,6.247352 -11000000,18000,11668366.383390,6.627498 -12000000,18000,12727342.596425,6.553806 -10,19000,20728.630000,0.006574 -1000000,19000,1080192.931537,0.604525 -2000000,19000,2139667.823075,1.206845 -3000000,19000,3199142.714612,1.824162 -4000000,19000,4258617.606150,2.402845 -5000000,19000,5318092.497687,3.061442 -6000000,19000,6377567.389225,3.687806 -7000000,19000,7437042.280762,4.268633 -8000000,19000,8496517.172299,4.893125 -9000000,19000,9555992.063837,5.483788 -10000000,19000,10615466.955374,6.110049 -11000000,19000,11674941.846912,6.751712 -12000000,19000,12734416.738449,7.080255 -10,20000,21818.630000,0.006792 -1000000,20000,1081781.610039,0.612154 -2000000,20000,2141755.180079,1.238999 -3000000,20000,3201728.750118,1.835297 -4000000,20000,4261702.320158,2.399989 -5000000,20000,5321675.890197,3.019549 -6000000,20000,6381649.460236,3.633679 -7000000,20000,7441623.030276,4.286694 -8000000,20000,8501596.600315,4.823475 -9000000,20000,9561570.170355,5.493688 -10000000,20000,10621543.740394,6.131865 -11000000,20000,11681517.310433,6.734410 -12000000,20000,12741490.880473,7.268826 +10,10,29.530000,0.001739 +1000000,10,1049023.926785,0.032266 +2000000,10,2098028.913570,0.032537 +3000000,10,3147033.900355,0.029105 +4000000,10,4196038.887140,0.030731 +5000000,10,5245043.873925,0.029708 +6000000,10,6294048.860710,0.029105 +7000000,10,7343053.847495,0.031201 +8000000,10,8392058.834280,0.030173 +9000000,10,9441063.821065,0.030389 +10000000,10,10490068.807850,0.029919 +11000000,10,11539073.794635,0.030398 +12000000,10,12588078.781420,0.029108 +10,1000,1109.620000,0.002506 +1000000,1000,1050597.708502,0.370950 +2000000,1000,2100096.387004,0.374719 +3000000,1000,3149595.065506,0.344377 +4000000,1000,4199093.744008,0.342928 +5000000,1000,5248592.432510,0.358908 +6000000,1000,6298091.111012,0.340612 +7000000,1000,7347589.789514,0.338701 +8000000,1000,8397088.468016,0.355251 +9000000,1000,9446587.146518,0.344550 +10000000,1000,10496085.825020,0.337101 +11000000,1000,11545584.503522,0.352694 +12000000,1000,12595083.182024,0.337451 +10,2000,2200.620000,0.002475 +1000000,2000,1052187.387004,0.536343 +2000000,2000,2102184.744008,0.688866 +3000000,2000,3152182.111012,0.675287 +4000000,2000,4202179.468016,0.659266 +5000000,2000,5252176.825020,0.676596 +6000000,2000,6302174.182024,0.665399 +7000000,2000,7352171.539028,0.663931 +8000000,2000,8402168.896032,0.669095 +9000000,2000,9452166.253035,0.650182 +10000000,2000,10502163.610039,0.666517 +11000000,2000,11552160.967043,0.695509 +12000000,2000,12602158.324047,0.665316 +10,3000,3291.620000,0.002850 +1000000,3000,1053777.065506,0.541173 +2000000,3000,2104273.111012,1.022867 +3000000,3000,3154769.146518,1.013680 +4000000,3000,4205265.182024,1.012737 +5000000,3000,5255761.217530,0.998726 +6000000,3000,6306257.253035,0.998887 +7000000,3000,7356753.288541,1.005627 +8000000,3000,8407249.324047,1.012896 +9000000,3000,9457745.359553,0.974931 +10000000,3000,10508241.395059,0.980876 +11000000,3000,11558737.430565,0.993527 +12000000,3000,12609233.466071,0.999704 +10,4000,4382.620000,0.003049 +1000000,4000,1055366.744008,0.522786 +2000000,4000,2106361.468016,1.071934 +3000000,4000,3157356.182024,1.333521 +4000000,4000,4208350.896032,1.324719 +5000000,4000,5259345.610039,1.366460 +6000000,4000,6310340.324047,1.334605 +7000000,4000,7361335.038055,1.356636 +8000000,4000,8412329.752063,1.330265 +9000000,4000,9463324.466071,1.356177 +10000000,4000,10514319.180079,1.353999 +11000000,4000,11565313.894087,1.373484 +12000000,4000,12616308.608095,1.366862 +10,5000,5473.620000,0.003699 +1000000,5000,1056956.432510,0.543109 +2000000,5000,2108449.825020,1.096696 +3000000,5000,3159943.217530,1.650989 +4000000,5000,4211436.610039,1.690255 +5000000,5000,5262930.002549,1.675706 +6000000,5000,6314423.395059,1.703036 +7000000,5000,7365916.787569,1.681018 +8000000,5000,8417410.180079,1.689242 +9000000,5000,9468903.572589,1.682302 +10000000,5000,10520396.965098,1.661444 +11000000,5000,11571890.357608,1.694598 +12000000,5000,12623383.750118,1.675268 +10,6000,6564.620000,0.004461 +1000000,6000,1058546.111012,0.558389 +2000000,6000,2110538.182024,1.103470 +3000000,6000,3162530.253035,1.644891 +4000000,6000,4214522.324047,2.025893 +5000000,6000,5266514.395059,2.023481 +6000000,6000,6318506.466071,2.020016 +7000000,6000,7370498.537083,2.022044 +8000000,6000,8422490.608095,2.011308 +9000000,6000,9474482.679106,2.033116 +10000000,6000,10526474.750118,2.004905 +11000000,6000,11578466.821130,1.983759 +12000000,6000,12630458.892142,2.009082 +10,7000,7655.620000,0.004161 +1000000,7000,1060135.789514,0.546054 +2000000,7000,2112626.539028,1.084770 +3000000,7000,3165117.288541,1.665682 +4000000,7000,4217608.038055,2.239605 +5000000,7000,5270098.787569,2.387870 +6000000,7000,6322589.537083,2.364654 +7000000,7000,7375080.286597,2.336478 +8000000,7000,8427571.036110,2.346240 +9000000,7000,9480061.785624,2.338914 +10000000,7000,10532552.535138,2.348488 +11000000,7000,11585043.284652,2.370729 +12000000,7000,12637534.034165,2.358697 +10,8000,8746.620000,0.004581 +1000000,8000,1061725.468016,0.531095 +2000000,8000,2114714.896032,1.116658 +3000000,8000,3167704.324047,1.640372 +4000000,8000,4220693.752063,2.229302 +5000000,8000,5273683.180079,2.697611 +6000000,8000,6326672.608095,2.707383 +7000000,8000,7379662.036110,2.883298 +8000000,8000,8432651.464126,2.739879 +9000000,8000,9485640.892142,2.726876 +10000000,8000,10538630.320158,2.735924 +11000000,8000,11591619.748173,2.694543 +12000000,8000,12644609.176189,2.732100 +10,9000,9837.620000,0.004876 +1000000,9000,1063315.146518,0.548072 +2000000,9000,2116803.253035,1.100648 +3000000,9000,3170291.359553,1.677171 +4000000,9000,4223779.466071,2.265948 +5000000,9000,5277267.572589,2.849275 +6000000,9000,6330755.679106,3.088711 +7000000,9000,7384243.785624,3.042883 +8000000,9000,8437731.892142,3.027926 +9000000,9000,9491219.998660,2.977327 +10000000,9000,10544708.105177,3.035313 +11000000,9000,11598196.211695,2.979878 +12000000,9000,12651684.318213,3.050990 +10,10000,10928.620000,0.006008 +1000000,10000,1064904.825020,0.544516 +2000000,10000,2118891.610039,1.087784 +3000000,10000,3172878.395059,1.651662 +4000000,10000,4226865.180079,2.166559 +5000000,10000,5280851.965098,2.781247 +6000000,10000,6334838.750118,3.341002 +7000000,10000,7388825.535138,3.336598 +8000000,10000,8442812.320158,3.306633 +9000000,10000,9496799.105177,3.391895 +10000000,10000,10550785.890197,3.323664 +11000000,10000,11604772.675217,3.270334 +12000000,10000,12658759.460236,3.275144 +10,11000,12019.620000,0.004998 +1000000,11000,1066494.503522,0.536719 +2000000,11000,2120979.967043,1.082626 +3000000,11000,3175465.430565,1.625921 +4000000,11000,4229950.894087,2.176149 +5000000,11000,5284436.357608,2.690528 +6000000,11000,6338921.821130,3.416504 +7000000,11000,7393407.284652,3.253745 +8000000,11000,8447892.748173,3.320888 +9000000,11000,9502378.211695,3.302593 +10000000,11000,10556863.675217,3.306230 +11000000,11000,11611349.138738,3.341800 +12000000,11000,12665834.602260,3.317096 +10,12000,13110.620000,0.004542 +1000000,12000,1068084.182024,0.528114 +2000000,12000,2123068.324047,1.068059 +3000000,12000,3178052.466071,1.641103 +4000000,12000,4233036.608095,2.186015 +5000000,12000,5288020.750118,2.762845 +6000000,12000,6343004.892142,3.279752 +7000000,12000,7397989.034165,3.321890 +8000000,12000,8452973.176189,3.320010 +9000000,12000,9507957.318213,3.327952 +10000000,12000,10562941.460236,3.303082 +11000000,12000,11617925.602260,3.277677 +12000000,12000,12672909.744284,3.291829 +10,13000,14201.620000,0.004793 +1000000,13000,1069673.860526,0.527200 +2000000,13000,2125156.681051,1.072790 +3000000,13000,3180639.501577,1.647767 +4000000,13000,4236122.322102,2.205192 +5000000,13000,5291605.142628,2.754582 +6000000,13000,6347087.963154,3.313098 +7000000,13000,7402570.783679,3.318240 +8000000,13000,8458053.604205,3.258286 +9000000,13000,9513536.424730,3.277801 +10000000,13000,10569019.245256,3.263061 +11000000,13000,11624502.065782,3.187902 +12000000,13000,12679984.886307,3.284980 +10,14000,15292.620000,0.004404 +1000000,14000,1071263.539028,0.517681 +2000000,14000,2127245.038055,1.064244 +3000000,14000,3183226.537083,1.606337 +4000000,14000,4239208.036110,2.103653 +5000000,14000,5295189.535138,2.685025 +6000000,14000,6351171.034165,3.228933 +7000000,14000,7407152.533193,3.239423 +8000000,14000,8463134.032221,3.230408 +9000000,14000,9519115.531248,3.231826 +10000000,14000,10575097.030276,3.258202 +11000000,14000,11631078.529303,3.271409 +12000000,14000,12687060.028331,3.256534 +10,15000,16383.620000,0.005043 +1000000,15000,1072853.217530,0.512275 +2000000,15000,2129333.395059,1.063722 +3000000,15000,3185813.572589,1.620265 +4000000,15000,4242293.750118,2.155953 +5000000,15000,5298773.927648,2.694425 +6000000,15000,6355254.105177,3.248130 +7000000,15000,7411734.282707,3.248848 +8000000,15000,8468214.460236,3.261128 +9000000,15000,9524694.637766,3.294237 +10000000,15000,10581174.815295,3.333736 +11000000,15000,11637654.992825,3.265902 +12000000,15000,12694135.170355,3.271338 +10,16000,17474.620000,0.004316 +1000000,16000,1074442.896032,0.542045 +2000000,16000,2131421.752063,1.061935 +3000000,16000,3188400.608095,1.614002 +4000000,16000,4245379.464126,2.165436 +5000000,16000,5302358.320158,2.711844 +6000000,16000,6359337.176189,3.245474 +7000000,16000,7416316.032221,3.320659 +8000000,16000,8473294.888252,3.262030 +9000000,16000,9530273.744284,3.276950 +10000000,16000,10587252.600315,3.265737 +11000000,16000,11644231.456347,3.341240 +12000000,16000,12701210.312378,3.329361 +10,17000,18565.620000,0.004845 +1000000,17000,1076032.574533,0.529896 +2000000,17000,2133510.109067,1.086848 +3000000,17000,3190987.643600,1.620933 +4000000,17000,4248465.178134,2.211906 +5000000,17000,5305942.712667,2.738750 +6000000,17000,6363420.247201,3.268886 +7000000,17000,7420897.781734,3.274784 +8000000,17000,8478375.316268,3.217516 +9000000,17000,9535852.850801,3.260153 +10000000,17000,10593330.385335,3.240985 +11000000,17000,11650807.919868,3.239152 +12000000,17000,12708285.454402,3.241075 +10,18000,19656.620000,0.005349 +1000000,18000,1077622.253035,0.533905 +2000000,18000,2135598.466071,1.042534 +3000000,18000,3193574.679106,1.688406 +4000000,18000,4251550.892142,2.141789 +5000000,18000,5309527.105177,2.662798 +6000000,18000,6367503.318213,3.184769 +7000000,18000,7425479.531248,3.205291 +8000000,18000,8483455.744284,3.275736 +9000000,18000,9541431.957319,3.287537 +10000000,18000,10599408.170355,3.241965 +11000000,18000,11657384.383390,3.294432 +12000000,18000,12715360.596425,3.301324 +10,19000,20747.620000,0.004789 +1000000,19000,1079211.931537,0.526987 +2000000,19000,2137686.823075,1.058374 +3000000,19000,3196161.714612,1.605277 +4000000,19000,4254636.606150,2.169600 +5000000,19000,5313111.497687,2.725848 +6000000,19000,6371586.389225,3.280931 +7000000,19000,7430061.280762,3.318047 +8000000,19000,8488536.172299,3.329808 +9000000,19000,9547011.063837,3.314684 +10000000,19000,10605485.955374,3.357281 +11000000,19000,11663960.846912,3.281812 +12000000,19000,12722435.738449,3.289319 +10,20000,21838.620000,0.004472 +1000000,20000,1080801.610039,0.527432 +2000000,20000,2139775.180079,1.054781 +3000000,20000,3198748.750118,1.604164 +4000000,20000,4257722.320158,2.171852 +5000000,20000,5316695.890197,2.679482 +6000000,20000,6375669.460236,3.253336 +7000000,20000,7434643.030276,3.265477 +8000000,20000,8493616.600315,3.277282 +9000000,20000,9552590.170355,3.262370 +10000000,20000,10611563.740394,3.292096 +11000000,20000,11670537.310433,3.251277 +12000000,20000,12729510.880473,3.304060 diff --git a/pkg/sql/opt/xform/coster.go b/pkg/sql/opt/xform/coster.go index fe2e7279e07a..8fe884ad6c11 100644 --- a/pkg/sql/opt/xform/coster.go +++ b/pkg/sql/opt/xform/coster.go @@ -843,7 +843,20 @@ func (c *coster) computeMergeJoinCost(join *memo.MergeJoinExpr) memo.Cost { leftRowCount := join.Left.Relational().Stats.RowCount rightRowCount := join.Right.Relational().Stats.RowCount - cost := memo.Cost(leftRowCount+rightRowCount) * cpuCostFactor + if (join.Op() == opt.SemiJoinOp || join.Op() == opt.AntiJoinOp) && leftRowCount < rightRowCount { + // If we have a semi or an anti join, during the execbuilding we choose + // the relation with smaller cardinality to be on the right side, so we + // need to swap row counts accordingly. + // TODO(raduberinde): we might also need to look at memo.JoinFlags when + // choosing a side. + leftRowCount, rightRowCount = rightRowCount, leftRowCount + } + + // The vectorized merge join in some cases buffers rows from the right side + // whereas the left side is processed in a streaming fashion. To account for + // this difference, we multiply both row counts so that a join with the + // smaller right side is preferred to the symmetric join. + cost := memo.Cost(0.9*leftRowCount+1.1*rightRowCount) * cpuCostFactor filterSetup, filterPerRow := c.computeFiltersCost(join.On, util.FastIntMap{}) cost += filterSetup diff --git a/pkg/sql/opt/xform/testdata/coster/join b/pkg/sql/opt/xform/testdata/coster/join index 0ea30d748df5..0fadb01d4531 100644 --- a/pkg/sql/opt/xform/testdata/coster/join +++ b/pkg/sql/opt/xform/testdata/coster/join @@ -922,13 +922,13 @@ WHERE project ├── columns: attname:3!null atttypid:4!null typbasetype:52 typtype:34 ├── stats: [rows=198] - ├── cost: 2911.11877 + ├── cost: 2910.13877 └── inner-join (merge) ├── columns: attname:3!null atttypid:4!null oid:28!null typtype:34 typbasetype:52 ├── left ordering: +28 ├── right ordering: +4 ├── stats: [rows=198, distinct(4)=17.2927193, null(4)=0, distinct(28)=17.2927193, null(28)=0] - ├── cost: 2909.11877 + ├── cost: 2908.13877 ├── fd: (4)==(28), (28)==(4) ├── scan pg_type@pg_type_oid_idx [as=t] │ ├── columns: oid:28!null typtype:34 typbasetype:52 diff --git a/pkg/sql/opt/xform/testdata/external/hibernate b/pkg/sql/opt/xform/testdata/external/hibernate index 7e6ca57106ef..84abe375a173 100644 --- a/pkg/sql/opt/xform/testdata/external/hibernate +++ b/pkg/sql/opt/xform/testdata/external/hibernate @@ -403,12 +403,23 @@ project │ ├── fd: (1,2)-->(3-6,17) │ ├── inner-join (merge) │ │ ├── columns: defaultaud0_.id:1!null defaultaud0_.rev:2!null defaultaud0_.revtype:3!null defaultaud0_.created_on:4 defaultaud0_.firstname:5 defaultaud0_.lastname:6 defaultaud1_.id:9!null defaultaud1_.rev:10!null - │ │ ├── left ordering: +1 - │ │ ├── right ordering: +9 + │ │ ├── left ordering: +9 + │ │ ├── right ordering: +1 │ │ ├── has-placeholder │ │ ├── key: (2,9,10) │ │ ├── fd: (1,2)-->(3-6), (1)==(9), (9)==(1) │ │ ├── select + │ │ │ ├── columns: defaultaud1_.id:9!null defaultaud1_.rev:10!null + │ │ │ ├── has-placeholder + │ │ │ ├── key: (9,10) + │ │ │ ├── ordering: +9 + │ │ │ ├── scan customer_aud [as=defaultaud1_] + │ │ │ │ ├── columns: defaultaud1_.id:9!null defaultaud1_.rev:10!null + │ │ │ │ ├── key: (9,10) + │ │ │ │ └── ordering: +9 + │ │ │ └── filters + │ │ │ └── defaultaud1_.rev:10 <= $1 [outer=(10), constraints=(/10: (/NULL - ])] + │ │ ├── select │ │ │ ├── columns: defaultaud0_.id:1!null defaultaud0_.rev:2!null defaultaud0_.revtype:3!null defaultaud0_.created_on:4 defaultaud0_.firstname:5 defaultaud0_.lastname:6 │ │ │ ├── has-placeholder │ │ │ ├── key: (1,2) @@ -421,17 +432,6 @@ project │ │ │ │ └── ordering: +1 │ │ │ └── filters │ │ │ └── defaultaud0_.revtype:3 != $2 [outer=(3), constraints=(/3: (/NULL - ])] - │ │ ├── select - │ │ │ ├── columns: defaultaud1_.id:9!null defaultaud1_.rev:10!null - │ │ │ ├── has-placeholder - │ │ │ ├── key: (9,10) - │ │ │ ├── ordering: +9 - │ │ │ ├── scan customer_aud [as=defaultaud1_] - │ │ │ │ ├── columns: defaultaud1_.id:9!null defaultaud1_.rev:10!null - │ │ │ │ ├── key: (9,10) - │ │ │ │ └── ordering: +9 - │ │ │ └── filters - │ │ │ └── defaultaud1_.rev:10 <= $1 [outer=(10), constraints=(/10: (/NULL - ])] │ │ └── filters (true) │ └── aggregations │ ├── max [as=max:17, outer=(10)] @@ -518,11 +518,15 @@ sort │ ├── fd: (1,2)-->(3-8,21) │ ├── inner-join (merge) │ │ ├── columns: queryaudit0_.id:1!null queryaudit0_.rev:2!null queryaudit0_.revtype:3!null queryaudit0_.revend:4 queryaudit0_.created_on:5 queryaudit0_.firstname:6 queryaudit0_.lastname:7 queryaudit0_.address_id:8 queryaudit1_.id:11!null queryaudit1_.rev:12!null - │ │ ├── left ordering: +1 - │ │ ├── right ordering: +11 + │ │ ├── left ordering: +11 + │ │ ├── right ordering: +1 │ │ ├── has-placeholder │ │ ├── key: (2,11,12) │ │ ├── fd: (1,2)-->(3-8), (1)==(11), (11)==(1) + │ │ ├── scan customer_aud [as=queryaudit1_] + │ │ │ ├── columns: queryaudit1_.id:11!null queryaudit1_.rev:12!null + │ │ │ ├── key: (11,12) + │ │ │ └── ordering: +11 │ │ ├── select │ │ │ ├── columns: queryaudit0_.id:1!null queryaudit0_.rev:2!null queryaudit0_.revtype:3!null queryaudit0_.revend:4 queryaudit0_.created_on:5 queryaudit0_.firstname:6 queryaudit0_.lastname:7 queryaudit0_.address_id:8 │ │ │ ├── has-placeholder @@ -536,10 +540,6 @@ sort │ │ │ │ └── ordering: +1 │ │ │ └── filters │ │ │ └── queryaudit0_.revtype:3 != $1 [outer=(3), constraints=(/3: (/NULL - ])] - │ │ ├── scan customer_aud [as=queryaudit1_] - │ │ │ ├── columns: queryaudit1_.id:11!null queryaudit1_.rev:12!null - │ │ │ ├── key: (11,12) - │ │ │ └── ordering: +11 │ │ └── filters (true) │ └── aggregations │ ├── max [as=max:21, outer=(12)] diff --git a/pkg/sql/opt/xform/testdata/external/pgjdbc b/pkg/sql/opt/xform/testdata/external/pgjdbc index 175ca5e492ef..0620a0d26073 100644 --- a/pkg/sql/opt/xform/testdata/external/pgjdbc +++ b/pkg/sql/opt/xform/testdata/external/pgjdbc @@ -202,19 +202,10 @@ sort │ │ │ │ │ │ │ ├── fd: ()-->(3,60), (2)==(9), (9)==(2), (7)==(44), (44)==(7) │ │ │ │ │ │ │ ├── inner-join (merge) │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24!null attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49!null atttypmod:52 a.attnotnull:56 attisdropped:60!null - │ │ │ │ │ │ │ │ ├── left ordering: +7 - │ │ │ │ │ │ │ │ ├── right ordering: +44 + │ │ │ │ │ │ │ │ ├── left ordering: +44 + │ │ │ │ │ │ │ │ ├── right ordering: +7 │ │ │ │ │ │ │ │ ├── fd: ()-->(60), (7)==(44), (44)==(7) │ │ │ │ │ │ │ │ ├── select - │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24!null - │ │ │ │ │ │ │ │ │ ├── ordering: +7 - │ │ │ │ │ │ │ │ │ ├── scan pg_class@pg_class_oid_idx [as=c] - │ │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24 - │ │ │ │ │ │ │ │ │ │ └── ordering: +7 - │ │ │ │ │ │ │ │ │ └── filters - │ │ │ │ │ │ │ │ │ ├── c.relkind:24 IN ('f', 'm', 'p', 'r', 'v') [outer=(24), constraints=(/24: [/'f' - /'f'] [/'m' - /'m'] [/'p' - /'p'] [/'r' - /'r'] [/'v' - /'v']; tight)] - │ │ │ │ │ │ │ │ │ └── c.relname:8 LIKE '%' [outer=(8), constraints=(/8: (/NULL - ])] - │ │ │ │ │ │ │ │ ├── select │ │ │ │ │ │ │ │ │ ├── columns: attrelid:44!null attname:45 atttypid:46 attlen:48 attnum:49!null atttypmod:52 a.attnotnull:56 attisdropped:60!null │ │ │ │ │ │ │ │ │ ├── fd: ()-->(60) │ │ │ │ │ │ │ │ │ ├── ordering: +44 opt(60) [actual: +44] @@ -224,6 +215,15 @@ sort │ │ │ │ │ │ │ │ │ └── filters │ │ │ │ │ │ │ │ │ ├── attnum:49 > 0 [outer=(49), constraints=(/49: [/1 - ]; tight)] │ │ │ │ │ │ │ │ │ └── NOT attisdropped:60 [outer=(60), constraints=(/60: [/false - /false]; tight), fd=()-->(60)] + │ │ │ │ │ │ │ │ ├── select + │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24!null + │ │ │ │ │ │ │ │ │ ├── ordering: +7 + │ │ │ │ │ │ │ │ │ ├── scan pg_class@pg_class_oid_idx [as=c] + │ │ │ │ │ │ │ │ │ │ ├── columns: c.oid:7!null c.relname:8!null c.relnamespace:9 c.relkind:24 + │ │ │ │ │ │ │ │ │ │ └── ordering: +7 + │ │ │ │ │ │ │ │ │ └── filters + │ │ │ │ │ │ │ │ │ ├── c.relkind:24 IN ('f', 'm', 'p', 'r', 'v') [outer=(24), constraints=(/24: [/'f' - /'f'] [/'m' - /'m'] [/'p' - /'p'] [/'r' - /'r'] [/'v' - /'v']; tight)] + │ │ │ │ │ │ │ │ │ └── c.relname:8 LIKE '%' [outer=(8), constraints=(/8: (/NULL - ])] │ │ │ │ │ │ │ │ └── filters (true) │ │ │ │ │ │ │ ├── select │ │ │ │ │ │ │ │ ├── columns: n.oid:2 n.nspname:3!null diff --git a/pkg/sql/opt/xform/testdata/external/tpcc b/pkg/sql/opt/xform/testdata/external/tpcc index 3b0c8dd9f2de..fb962b8636af 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc +++ b/pkg/sql/opt/xform/testdata/external/tpcc @@ -1404,8 +1404,20 @@ scalar-group-by │ ├── columns: o_id:1 o_d_id:2 o_w_id:3 ol_o_id:11 ol_d_id:12 ol_w_id:13 │ ├── full-join (merge) │ │ ├── columns: o_id:1 o_d_id:2 o_w_id:3 ol_o_id:11 ol_d_id:12 ol_w_id:13 - │ │ ├── left ordering: +3,+2,-1 - │ │ ├── right ordering: +13,+12,-11 + │ │ ├── left ordering: +13,+12,-11 + │ │ ├── right ordering: +3,+2,-1 + │ │ ├── project + │ │ │ ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null + │ │ │ ├── ordering: +13,+12,-11 + │ │ │ └── select + │ │ │ ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ol_delivery_d:17 + │ │ │ ├── fd: ()-->(17) + │ │ │ ├── ordering: +13,+12,-11 opt(17) [actual: +13,+12,-11] + │ │ │ ├── scan order_line + │ │ │ │ ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ol_delivery_d:17 + │ │ │ │ └── ordering: +13,+12,-11 + │ │ │ └── filters + │ │ │ └── ol_delivery_d:17 IS NULL [outer=(17), constraints=(/17: [/NULL - /NULL]; tight), fd=()-->(17)] │ │ ├── project │ │ │ ├── columns: o_id:1!null o_d_id:2!null o_w_id:3!null │ │ │ ├── key: (1-3) @@ -1422,18 +1434,6 @@ scalar-group-by │ │ │ │ └── ordering: +3,+2,-1 │ │ │ └── filters │ │ │ └── o_carrier_id:6 IS NULL [outer=(6), constraints=(/6: [/NULL - /NULL]; tight), fd=()-->(6)] - │ │ ├── project - │ │ │ ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null - │ │ │ ├── ordering: +13,+12,-11 - │ │ │ └── select - │ │ │ ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ol_delivery_d:17 - │ │ │ ├── fd: ()-->(17) - │ │ │ ├── ordering: +13,+12,-11 opt(17) [actual: +13,+12,-11] - │ │ │ ├── scan order_line - │ │ │ │ ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ol_delivery_d:17 - │ │ │ │ └── ordering: +13,+12,-11 - │ │ │ └── filters - │ │ │ └── ol_delivery_d:17 IS NULL [outer=(17), constraints=(/17: [/NULL - /NULL]; tight), fd=()-->(17)] │ │ └── filters (true) │ └── filters │ └── (ol_o_id:11 IS NULL) OR (o_id:1 IS NULL) [outer=(1,11)] diff --git a/pkg/sql/opt/xform/testdata/external/tpch b/pkg/sql/opt/xform/testdata/external/tpch index 3a320b1ccac4..381bec604e62 100644 --- a/pkg/sql/opt/xform/testdata/external/tpch +++ b/pkg/sql/opt/xform/testdata/external/tpch @@ -994,10 +994,15 @@ sort │ │ │ │ │ │ ├── fd: (1)-->(2), (39,40)-->(42), (1)==(22,39), (39)==(1,22), (21,24)-->(22,23), (22)==(1,39), (23)==(40), (40)==(23) │ │ │ │ │ │ ├── inner-join (merge) │ │ │ │ │ │ │ ├── columns: p_partkey:1!null p_name:2!null ps_partkey:39!null ps_suppkey:40!null ps_supplycost:42!null - │ │ │ │ │ │ │ ├── left ordering: +1 - │ │ │ │ │ │ │ ├── right ordering: +39 + │ │ │ │ │ │ │ ├── left ordering: +39 + │ │ │ │ │ │ │ ├── right ordering: +1 │ │ │ │ │ │ │ ├── key: (39,40) │ │ │ │ │ │ │ ├── fd: (1)-->(2), (39,40)-->(42), (1)==(39), (39)==(1) + │ │ │ │ │ │ │ ├── scan partsupp + │ │ │ │ │ │ │ │ ├── columns: ps_partkey:39!null ps_suppkey:40!null ps_supplycost:42!null + │ │ │ │ │ │ │ │ ├── key: (39,40) + │ │ │ │ │ │ │ │ ├── fd: (39,40)-->(42) + │ │ │ │ │ │ │ │ └── ordering: +39 │ │ │ │ │ │ │ ├── select │ │ │ │ │ │ │ │ ├── columns: p_partkey:1!null p_name:2!null │ │ │ │ │ │ │ │ ├── key: (1) @@ -1010,11 +1015,6 @@ sort │ │ │ │ │ │ │ │ │ └── ordering: +1 │ │ │ │ │ │ │ │ └── filters │ │ │ │ │ │ │ │ └── p_name:2 LIKE '%green%' [outer=(2), constraints=(/2: (/NULL - ])] - │ │ │ │ │ │ │ ├── scan partsupp - │ │ │ │ │ │ │ │ ├── columns: ps_partkey:39!null ps_suppkey:40!null ps_supplycost:42!null - │ │ │ │ │ │ │ │ ├── key: (39,40) - │ │ │ │ │ │ │ │ ├── fd: (39,40)-->(42) - │ │ │ │ │ │ │ │ └── ordering: +39 │ │ │ │ │ │ │ └── filters (true) │ │ │ │ │ │ └── filters (true) │ │ │ │ │ └── filters (true) @@ -1930,9 +1930,12 @@ top-k │ ├── fd: (1)-->(2), (11)-->(12,14,15), (11)==(22), (22)==(11), (1)==(12), (12)==(1) │ ├── inner-join (merge) │ │ ├── columns: o_orderkey:11!null o_custkey:12!null o_totalprice:14!null o_orderdate:15!null l_orderkey:22!null l_quantity:26!null - │ │ ├── left ordering: +11 - │ │ ├── right ordering: +22 + │ │ ├── left ordering: +22 + │ │ ├── right ordering: +11 │ │ ├── fd: (11)-->(12,14,15), (11)==(22), (22)==(11) + │ │ ├── scan lineitem + │ │ │ ├── columns: l_orderkey:22!null l_quantity:26!null + │ │ │ └── ordering: +22 │ │ ├── semi-join (merge) │ │ │ ├── columns: o_orderkey:11!null o_custkey:12!null o_totalprice:14!null o_orderdate:15!null │ │ │ ├── left ordering: +11 @@ -1965,9 +1968,6 @@ top-k │ │ │ │ └── filters │ │ │ │ └── sum:58 > 300.0 [outer=(58), constraints=(/58: [/300.00000000000006 - ]; tight)] │ │ │ └── filters (true) - │ │ ├── scan lineitem - │ │ │ ├── columns: l_orderkey:22!null l_quantity:26!null - │ │ │ └── ordering: +22 │ │ └── filters (true) │ ├── scan customer │ │ ├── columns: c_custkey:1!null c_name:2!null diff --git a/pkg/sql/opt/xform/testdata/external/tpch-no-stats b/pkg/sql/opt/xform/testdata/external/tpch-no-stats index 569b3c898a94..1b77c00602da 100644 --- a/pkg/sql/opt/xform/testdata/external/tpch-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpch-no-stats @@ -1865,10 +1865,13 @@ top-k ├── fd: (1)-->(2), (11)-->(1,2,14,15,59) ├── inner-join (merge) │ ├── columns: c_custkey:1!null c_name:2!null o_orderkey:11!null o_custkey:12!null o_totalprice:14!null o_orderdate:15!null l_orderkey:22!null l_quantity:26!null - │ ├── left ordering: +11 - │ ├── right ordering: +22 + │ ├── left ordering: +22 + │ ├── right ordering: +11 │ ├── fd: (1)-->(2), (11)-->(12,14,15), (11)==(22), (22)==(11), (1)==(12), (12)==(1) - │ ├── ordering: +(11|22) [actual: +11] + │ ├── ordering: +(11|22) [actual: +22] + │ ├── scan lineitem + │ │ ├── columns: l_orderkey:22!null l_quantity:26!null + │ │ └── ordering: +22 │ ├── inner-join (lookup customer) │ │ ├── columns: c_custkey:1!null c_name:2!null o_orderkey:11!null o_custkey:12!null o_totalprice:14!null o_orderdate:15!null │ │ ├── key columns: [12] = [1] @@ -1914,9 +1917,6 @@ top-k │ │ │ │ └── sum:58 > 300.0 [outer=(58), constraints=(/58: [/300.00000000000006 - ]; tight)] │ │ │ └── filters (true) │ │ └── filters (true) - │ ├── scan lineitem - │ │ ├── columns: l_orderkey:22!null l_quantity:26!null - │ │ └── ordering: +22 │ └── filters (true) └── aggregations ├── sum [as=sum:59, outer=(26)] diff --git a/pkg/sql/opt/xform/testdata/external/trading b/pkg/sql/opt/xform/testdata/external/trading index 9697a12680e1..eedc02a1967c 100644 --- a/pkg/sql/opt/xform/testdata/external/trading +++ b/pkg/sql/opt/xform/testdata/external/trading @@ -822,20 +822,13 @@ project │ ├── fd: (1)-->(2-6), (2,4,5)~~>(1,3,6), (10)-->(1-6,11-17,30), (17)-->(10-16), (1)==(10), (10)==(1) │ ├── inner-join (merge) │ │ ├── columns: id:1!null name:2!null rarity:3 setname:4 number:5!null isfoil:6!null cardsinfo.dealerid:9!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null transactiondetails.dealerid:20 isbuy:21 transactiondate:22 transactiondetails.cardid:23 quantity:24 - │ │ ├── left ordering: +10 - │ │ ├── right ordering: +1 + │ │ ├── left ordering: +1 + │ │ ├── right ordering: +10 │ │ ├── immutable │ │ ├── stats: [rows=5523583.18, distinct(10)=19000, null(10)=0, distinct(23)=19000, null(23)=0] │ │ ├── key: (10,22-24) │ │ ├── fd: ()-->(9), (1)-->(2-6), (2,4,5)~~>(1,3,6), (10)-->(11-17), (17)-->(10-16), (1)==(10), (10)==(1), (10,22-24)-->(20,21) - │ │ ├── ordering: +(1|10) opt(9) [actual: +10] - │ │ ├── scan cardsinfo - │ │ │ ├── columns: cardsinfo.dealerid:9!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null - │ │ │ ├── constraint: /9/10: [/1 - /1] - │ │ │ ├── stats: [rows=58333.3333, distinct(9)=1, null(9)=0, distinct(10)=37420.3552, null(10)=0, distinct(11)=40676.7278, null(11)=0, distinct(12)=40676.7278, null(12)=0, distinct(13)=40676.7278, null(13)=0, distinct(14)=40676.7278, null(14)=0, distinct(15)=40676.7278, null(15)=0, distinct(16)=40676.7278, null(16)=0, distinct(17)=58333.3333, null(17)=0] - │ │ │ ├── key: (10) - │ │ │ ├── fd: ()-->(9), (10)-->(11-17), (17)-->(10-16) - │ │ │ └── ordering: +10 opt(9) [actual: +10] + │ │ ├── ordering: +(1|10) opt(9) [actual: +1] │ │ ├── left-join (lookup transactiondetails@detailscardidindex) │ │ │ ├── columns: id:1!null name:2!null rarity:3 setname:4 number:5!null isfoil:6!null transactiondetails.dealerid:20 isbuy:21 transactiondate:22 transactiondetails.cardid:23 quantity:24 │ │ │ ├── lookup expression @@ -875,6 +868,13 @@ project │ │ │ │ ├── false [as="lookup_join_const_col_@21":43] │ │ │ │ └── 1 [as="lookup_join_const_col_@20":42] │ │ │ └── filters (true) + │ │ ├── scan cardsinfo + │ │ │ ├── columns: cardsinfo.dealerid:9!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null + │ │ │ ├── constraint: /9/10: [/1 - /1] + │ │ │ ├── stats: [rows=58333.3333, distinct(9)=1, null(9)=0, distinct(10)=37420.3552, null(10)=0, distinct(11)=40676.7278, null(11)=0, distinct(12)=40676.7278, null(12)=0, distinct(13)=40676.7278, null(13)=0, distinct(14)=40676.7278, null(14)=0, distinct(15)=40676.7278, null(15)=0, distinct(16)=40676.7278, null(16)=0, distinct(17)=58333.3333, null(17)=0] + │ │ │ ├── key: (10) + │ │ │ ├── fd: ()-->(9), (10)-->(11-17), (17)-->(10-16) + │ │ │ └── ordering: +10 opt(9) [actual: +10] │ │ └── filters (true) │ └── aggregations │ ├── sum [as=sum:30, outer=(24)] diff --git a/pkg/sql/opt/xform/testdata/external/trading-mutation b/pkg/sql/opt/xform/testdata/external/trading-mutation index d2e875fb987a..8b796596bc4b 100644 --- a/pkg/sql/opt/xform/testdata/external/trading-mutation +++ b/pkg/sql/opt/xform/testdata/external/trading-mutation @@ -826,20 +826,13 @@ project │ ├── fd: (1)-->(2-6), (2,4,5)~~>(1,3,6), (10)-->(1-6,11-17,36), (17)-->(10-16), (1)==(10), (10)==(1) │ ├── inner-join (merge) │ │ ├── columns: id:1!null name:2!null rarity:3 setname:4 number:5!null isfoil:6!null cardsinfo.dealerid:9!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null cardsinfo.discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null transactiondetails.dealerid:24 isbuy:25 transactiondate:26 transactiondetails.cardid:27 quantity:28 - │ │ ├── left ordering: +10 - │ │ ├── right ordering: +1 + │ │ ├── left ordering: +1 + │ │ ├── right ordering: +10 │ │ ├── immutable │ │ ├── stats: [rows=5523583.18, distinct(10)=19000, null(10)=0, distinct(27)=19000, null(27)=0] │ │ ├── key: (10,26-28) │ │ ├── fd: ()-->(9), (1)-->(2-6), (2,4,5)~~>(1,3,6), (10)-->(11-17), (17)-->(10-16), (1)==(10), (10)==(1), (10,26-28)-->(24,25) - │ │ ├── ordering: +(1|10) opt(9) [actual: +10] - │ │ ├── scan cardsinfo - │ │ │ ├── columns: cardsinfo.dealerid:9!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null cardsinfo.discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null - │ │ │ ├── constraint: /9/10: [/1 - /1] - │ │ │ ├── stats: [rows=58333.3333, distinct(9)=1, null(9)=0, distinct(10)=37420.3552, null(10)=0, distinct(11)=40676.7278, null(11)=0, distinct(12)=40676.7278, null(12)=0, distinct(13)=40676.7278, null(13)=0, distinct(14)=40676.7278, null(14)=0, distinct(15)=40676.7278, null(15)=0, distinct(16)=40676.7278, null(16)=0, distinct(17)=58333.3333, null(17)=0] - │ │ │ ├── key: (10) - │ │ │ ├── fd: ()-->(9), (10)-->(11-17), (17)-->(10-16) - │ │ │ └── ordering: +10 opt(9) [actual: +10] + │ │ ├── ordering: +(1|10) opt(9) [actual: +1] │ │ ├── left-join (lookup transactiondetails@detailscardidindex) │ │ │ ├── columns: id:1!null name:2!null rarity:3 setname:4 number:5!null isfoil:6!null transactiondetails.dealerid:24 isbuy:25 transactiondate:26 transactiondetails.cardid:27 quantity:28 │ │ │ ├── lookup expression @@ -879,6 +872,13 @@ project │ │ │ │ ├── false [as="lookup_join_const_col_@25":49] │ │ │ │ └── 1 [as="lookup_join_const_col_@24":48] │ │ │ └── filters (true) + │ │ ├── scan cardsinfo + │ │ │ ├── columns: cardsinfo.dealerid:9!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null cardsinfo.discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null + │ │ │ ├── constraint: /9/10: [/1 - /1] + │ │ │ ├── stats: [rows=58333.3333, distinct(9)=1, null(9)=0, distinct(10)=37420.3552, null(10)=0, distinct(11)=40676.7278, null(11)=0, distinct(12)=40676.7278, null(12)=0, distinct(13)=40676.7278, null(13)=0, distinct(14)=40676.7278, null(14)=0, distinct(15)=40676.7278, null(15)=0, distinct(16)=40676.7278, null(16)=0, distinct(17)=58333.3333, null(17)=0] + │ │ │ ├── key: (10) + │ │ │ ├── fd: ()-->(9), (10)-->(11-17), (17)-->(10-16) + │ │ │ └── ordering: +10 opt(9) [actual: +10] │ │ └── filters (true) │ └── aggregations │ ├── sum [as=sum:36, outer=(28)] diff --git a/pkg/sql/opt/xform/testdata/rules/groupby b/pkg/sql/opt/xform/testdata/rules/groupby index 9be26996beb4..414f5b1b3768 100644 --- a/pkg/sql/opt/xform/testdata/rules/groupby +++ b/pkg/sql/opt/xform/testdata/rules/groupby @@ -2176,22 +2176,22 @@ memo (optimized, ~27KB, required=[presentation: w:12] [ordering: +13,+1]) ├── G1: (project G2 G3 x) │ ├── [presentation: w:12] [ordering: +13,+1] │ │ ├── best: (sort G1) - │ │ └── cost: 1420.84 + │ │ └── cost: 1419.85 │ └── [] │ ├── best: (project G2 G3 x) - │ └── cost: 1160.39 + │ └── cost: 1159.40 ├── G2: (ensure-distinct-on G4 G5 cols=(1)) (ensure-distinct-on G4 G5 cols=(1),ordering=+1) │ └── [] │ ├── best: (ensure-distinct-on G4="[ordering: +1]" G5 cols=(1),ordering=+1) - │ └── cost: 1130.37 + │ └── cost: 1129.38 ├── G3: (projections G6 G7) ├── G4: (left-join G8 G9 G10) (right-join G9 G8 G10) (merge-join G8 G9 G11 left-join,+1,+7) (lookup-join G12 G11 kuvw@uvw,keyCols=[1 14],outCols=(1,7-9)) (lookup-join G13 G10 kuvw@vw,keyCols=[15],outCols=(1,7-9)) (merge-join G9 G8 G11 right-join,+7,+1) │ ├── [ordering: +1] │ │ ├── best: (merge-join G8="[ordering: +1]" G9="[ordering: +7 opt(8)]" G11 left-join,+1,+7) - │ │ └── cost: 1100.34 + │ │ └── cost: 1099.35 │ └── [] │ ├── best: (merge-join G8="[ordering: +1]" G9="[ordering: +7 opt(8)]" G11 left-join,+1,+7) - │ └── cost: 1100.34 + │ └── cost: 1099.35 ├── G5: (aggregations G14) ├── G6: (variable kuvw.w) ├── G7: (plus G15 G16) diff --git a/pkg/sql/opt/xform/testdata/rules/join b/pkg/sql/opt/xform/testdata/rules/join index a48974a5758b..3aa2d0265b70 100644 --- a/pkg/sql/opt/xform/testdata/rules/join +++ b/pkg/sql/opt/xform/testdata/rules/join @@ -186,7 +186,7 @@ memo (optimized, ~37KB, required=[presentation: a:1,b:2,c:3,s:7,t:8,u:9,x:12,y:1 ├── G1: (inner-join G2 G3 G4) (inner-join G3 G2 G4) (inner-join G5 G6 G7) (inner-join G6 G5 G7) (inner-join G8 G9 G7) (inner-join G9 G8 G7) (merge-join G2 G3 G10 inner-join,+1,+7) (merge-join G3 G2 G10 inner-join,+7,+1) (lookup-join G3 G10 abc@ab,keyCols=[7],outCols=(1-3,7-9,12-14)) (merge-join G5 G6 G10 inner-join,+7,+12) (merge-join G6 G5 G10 inner-join,+12,+7) (lookup-join G6 G10 stu,keyCols=[12],outCols=(1-3,7-9,12-14)) (merge-join G8 G9 G10 inner-join,+7,+12) (lookup-join G8 G10 xyz@xy,keyCols=[7],outCols=(1-3,7-9,12-14)) (merge-join G9 G8 G10 inner-join,+12,+7) │ └── [presentation: a:1,b:2,c:3,s:7,t:8,u:9,x:12,y:13,z:14] │ ├── best: (merge-join G5="[ordering: +7]" G6="[ordering: +(1|12)]" G10 inner-join,+7,+12) - │ └── cost: 13054.10 + │ └── cost: 13045.10 ├── G2: (scan abc,cols=(1-3)) (scan abc@ab,cols=(1-3)) (scan abc@bc,cols=(1-3)) │ ├── [ordering: +1] │ │ ├── best: (scan abc@ab,cols=(1-3)) @@ -197,10 +197,10 @@ memo (optimized, ~37KB, required=[presentation: a:1,b:2,c:3,s:7,t:8,u:9,x:12,y:1 ├── G3: (inner-join G5 G9 G7) (inner-join G9 G5 G7) (merge-join G5 G9 G10 inner-join,+7,+12) (lookup-join G5 G10 xyz@xy,keyCols=[7],outCols=(7-9,12-14)) (merge-join G9 G5 G10 inner-join,+12,+7) (lookup-join G9 G10 stu,keyCols=[12],outCols=(7-9,12-14)) │ ├── [ordering: +(7|12)] │ │ ├── best: (merge-join G5="[ordering: +7]" G9="[ordering: +12]" G10 inner-join,+7,+12) - │ │ └── cost: 11929.36 + │ │ └── cost: 11920.36 │ └── [] │ ├── best: (merge-join G5="[ordering: +7]" G9="[ordering: +12]" G10 inner-join,+7,+12) - │ └── cost: 11929.36 + │ └── cost: 11920.36 ├── G4: (filters G11) ├── G5: (scan stu,cols=(7-9)) (scan stu@uts,cols=(7-9)) │ ├── [ordering: +7] @@ -219,11 +219,11 @@ memo (optimized, ~37KB, required=[presentation: a:1,b:2,c:3,s:7,t:8,u:9,x:12,y:1 ├── G7: (filters G13) ├── G8: (inner-join G2 G5 G4) (inner-join G5 G2 G4) (merge-join G2 G5 G10 inner-join,+1,+7) (lookup-join G2 G10 stu,keyCols=[1],outCols=(1-3,7-9)) (merge-join G5 G2 G10 inner-join,+7,+1) (lookup-join G5 G10 abc@ab,keyCols=[7],outCols=(1-3,7-9)) │ ├── [ordering: +(1|7)] - │ │ ├── best: (merge-join G2="[ordering: +1]" G5="[ordering: +7]" G10 inner-join,+1,+7) - │ │ └── cost: 11929.36 + │ │ ├── best: (merge-join G5="[ordering: +7]" G2="[ordering: +1]" G10 inner-join,+7,+1) + │ │ └── cost: 11920.36 │ └── [] - │ ├── best: (merge-join G2="[ordering: +1]" G5="[ordering: +7]" G10 inner-join,+1,+7) - │ └── cost: 11929.36 + │ ├── best: (merge-join G5="[ordering: +7]" G2="[ordering: +1]" G10 inner-join,+7,+1) + │ └── cost: 11920.36 ├── G9: (scan xyz,cols=(12-14)) (scan xyz@xy,cols=(12-14)) (scan xyz@yz,cols=(12-14)) │ ├── [ordering: +12] │ │ ├── best: (scan xyz@xy,cols=(12-14)) @@ -448,7 +448,7 @@ memo (optimized, ~32KB, required=[presentation: s:1,t:2,u:3,a:6,b:7,c:8,x:12,y:1 ├── G1: (inner-join G2 G3 G4) (inner-join G3 G2 G4) (merge-join G2 G3 G5 inner-join,+3,+6) (merge-join G3 G2 G5 inner-join,+6,+3) (lookup-join G3 G5 stu@uts,keyCols=[6],outCols=(1-3,6-8,12-14,18-22)) │ └── [presentation: s:1,t:2,u:3,a:6,b:7,c:8,x:12,y:13,z:14,p:18,q:19,r:20,s:21,t:22] │ ├── best: (merge-join G2="[ordering: +3]" G3="[ordering: +(6|12|18)]" G5 inner-join,+3,+6) - │ └── cost: 14209.14 + │ └── cost: 14200.14 ├── G2: (scan stu,cols=(1-3)) (scan stu@uts,cols=(1-3)) │ ├── [ordering: +3] │ │ ├── best: (scan stu@uts,cols=(1-3)) @@ -649,13 +649,13 @@ SELECT * FROM abc LEFT JOIN stu ON a = s LEFT JOIN small ON a = m ---- -left-join (merge) +right-join (merge) + ├── scan stu ├── left-join (merge) │ ├── scan abc@ab │ ├── sort │ │ └── scan small │ └── filters (true) - ├── scan stu └── filters (true) # Don't add a join between xyz and stu to the memo (because doing so would @@ -1101,8 +1101,8 @@ ReorderJoins Source expression: full-join (hash) ├── full-join (merge) - │ ├── scan abc@ab │ ├── scan stu + │ ├── scan abc@ab │ └── filters (true) ├── scan xyz └── filters @@ -1134,8 +1134,8 @@ New expression 3 of 3: full-join (hash) ├── scan xyz ├── full-join (merge) - │ ├── scan abc@ab │ ├── scan stu + │ ├── scan abc@ab │ └── filters (true) └── filters └── b = y @@ -1255,9 +1255,9 @@ ReorderJoins ================================================================================ Source expression: left-join (hash) - ├── left-join (merge) - │ ├── scan abc@ab + ├── right-join (merge) │ ├── scan stu + │ ├── scan abc@ab │ └── filters (true) ├── scan xyz └── filters @@ -1314,8 +1314,8 @@ ReorderJoins Source expression: left-join (hash) ├── full-join (merge) - │ ├── scan abc@ab │ ├── scan stu + │ ├── scan abc@ab │ └── filters (true) ├── scan xyz └── filters @@ -1521,8 +1521,8 @@ ReorderJoins Source expression: inner-join (hash) ├── inner-join (merge) - │ ├── scan abc@ab │ ├── scan stu + │ ├── scan abc@ab │ └── filters (true) ├── scan xyz └── filters @@ -1532,8 +1532,8 @@ New expression 1 of 1: inner-join (hash) ├── scan xyz ├── inner-join (merge) - │ ├── scan abc@ab │ ├── scan stu + │ ├── scan abc@ab │ └── filters (true) └── filters └── s = x @@ -1864,15 +1864,15 @@ SELECT * FROM abc JOIN xyz ON a=x ---- inner-join (merge) ├── columns: a:1!null b:2 c:3 x:7!null y:8 z:9 - ├── left ordering: +1 - ├── right ordering: +7 + ├── left ordering: +7 + ├── right ordering: +1 ├── fd: (1)==(7), (7)==(1) - ├── scan abc@ab - │ ├── columns: a:1 b:2 c:3 - │ └── ordering: +1 ├── scan xyz@xy │ ├── columns: x:7 y:8 z:9 │ └── ordering: +7 + ├── scan abc@ab + │ ├── columns: a:1 b:2 c:3 + │ └── ordering: +1 └── filters (true) memo @@ -1881,8 +1881,8 @@ SELECT * FROM abc JOIN xyz ON a=x memo (optimized, ~12KB, required=[presentation: a:1,b:2,c:3,x:7,y:8,z:9]) ├── G1: (inner-join G2 G3 G4) (inner-join G3 G2 G4) (merge-join G2 G3 G5 inner-join,+1,+7) (lookup-join G2 G5 xyz@xy,keyCols=[1],outCols=(1-3,7-9)) (merge-join G3 G2 G5 inner-join,+7,+1) (lookup-join G3 G5 abc@ab,keyCols=[7],outCols=(1-3,7-9)) │ └── [presentation: a:1,b:2,c:3,x:7,y:8,z:9] - │ ├── best: (merge-join G2="[ordering: +1]" G3="[ordering: +7]" G5 inner-join,+1,+7) - │ └── cost: 1238.46 + │ ├── best: (merge-join G3="[ordering: +7]" G2="[ordering: +1]" G5 inner-join,+7,+1) + │ └── cost: 1237.56 ├── G2: (scan abc,cols=(1-3)) (scan abc@ab,cols=(1-3)) (scan abc@bc,cols=(1-3)) │ ├── [ordering: +1] │ │ ├── best: (scan abc@ab,cols=(1-3)) @@ -1930,15 +1930,15 @@ SELECT * FROM abc JOIN xyz ON x=a ---- inner-join (merge) ├── columns: a:1!null b:2 c:3 x:7!null y:8 z:9 - ├── left ordering: +1 - ├── right ordering: +7 + ├── left ordering: +7 + ├── right ordering: +1 ├── fd: (1)==(7), (7)==(1) - ├── scan abc@ab - │ ├── columns: a:1 b:2 c:3 - │ └── ordering: +1 ├── scan xyz@xy │ ├── columns: x:7 y:8 z:9 │ └── ordering: +7 + ├── scan abc@ab + │ ├── columns: a:1 b:2 c:3 + │ └── ordering: +1 └── filters (true) opt @@ -1946,15 +1946,15 @@ SELECT * FROM abc JOIN xyz ON a=x AND a=x AND x=a ---- inner-join (merge) ├── columns: a:1!null b:2 c:3 x:7!null y:8 z:9 - ├── left ordering: +1 - ├── right ordering: +7 + ├── left ordering: +7 + ├── right ordering: +1 ├── fd: (1)==(7), (7)==(1) - ├── scan abc@ab - │ ├── columns: a:1 b:2 c:3 - │ └── ordering: +1 ├── scan xyz@xy │ ├── columns: x:7 y:8 z:9 │ └── ordering: +7 + ├── scan abc@ab + │ ├── columns: a:1 b:2 c:3 + │ └── ordering: +1 └── filters (true) # Use constraints to force the choice of an index which doesn't help, and @@ -2273,16 +2273,16 @@ left-join (merge) │ └── ordering: +13 ├── inner-join (merge) │ ├── columns: a:1!null b:2!null c:3 x:7!null y:8!null z:9 - │ ├── left ordering: +1,+2 - │ ├── right ordering: +7,+8 + │ ├── left ordering: +7,+8 + │ ├── right ordering: +1,+2 │ ├── fd: (1)==(7), (7)==(1), (2)==(8), (8)==(2) - │ ├── ordering: +(1|7) [actual: +1] - │ ├── scan abc@ab - │ │ ├── columns: a:1 b:2 c:3 - │ │ └── ordering: +1,+2 + │ ├── ordering: +(1|7) [actual: +7] │ ├── scan xyz@xy │ │ ├── columns: x:7 y:8 z:9 │ │ └── ordering: +7,+8 + │ ├── scan abc@ab + │ │ ├── columns: a:1 b:2 c:3 + │ │ └── ordering: +1,+2 │ └── filters (true) └── filters (true) @@ -2300,16 +2300,16 @@ left-join (merge) │ └── ordering: +15,+14 ├── inner-join (merge) │ ├── columns: a:1!null b:2!null c:3 x:7!null y:8!null z:9 - │ ├── left ordering: +1,+2 - │ ├── right ordering: +7,+8 + │ ├── left ordering: +7,+8 + │ ├── right ordering: +1,+2 │ ├── fd: (1)==(7), (7)==(1), (2)==(8), (8)==(2) - │ ├── ordering: +(1|7),+(2|8) [actual: +1,+2] - │ ├── scan abc@ab - │ │ ├── columns: a:1 b:2 c:3 - │ │ └── ordering: +1,+2 + │ ├── ordering: +(1|7),+(2|8) [actual: +7,+8] │ ├── scan xyz@xy │ │ ├── columns: x:7 y:8 z:9 │ │ └── ordering: +7,+8 + │ ├── scan abc@ab + │ │ ├── columns: a:1 b:2 c:3 + │ │ └── ordering: +1,+2 │ └── filters (true) └── filters (true) @@ -8268,11 +8268,22 @@ inner-join-apply │ └── key: (1-3) ├── inner-join (merge) │ ├── columns: p:6!null q:7!null r:8 pqr.s:9 pqr.t:10 a:13!null b:14 c:15 "?column?":19 - │ ├── left ordering: +7 - │ ├── right ordering: +13 + │ ├── left ordering: +13 + │ ├── right ordering: +7 │ ├── outer: (1) │ ├── immutable │ ├── fd: (6)-->(7-10), (13)-->(19), (7)==(13), (13)==(7) + │ ├── project + │ │ ├── columns: "?column?":19 a:13 b:14 c:15 + │ │ ├── outer: (1) + │ │ ├── immutable + │ │ ├── fd: (13)-->(19) + │ │ ├── ordering: +13 + │ │ ├── scan abc@ab + │ │ │ ├── columns: a:13 b:14 c:15 + │ │ │ └── ordering: +13 + │ │ └── projections + │ │ └── a:13 * stu.s:1 [as="?column?":19, outer=(1,13), immutable] │ ├── index-join pqr │ │ ├── columns: p:6!null q:7 r:8 pqr.s:9 pqr.t:10 │ │ ├── cardinality: [0 - 5] @@ -8285,17 +8296,6 @@ inner-join-apply │ │ ├── key: (6) │ │ ├── fd: (6)-->(7) │ │ └── ordering: +7 - │ ├── project - │ │ ├── columns: "?column?":19 a:13 b:14 c:15 - │ │ ├── outer: (1) - │ │ ├── immutable - │ │ ├── fd: (13)-->(19) - │ │ ├── ordering: +13 - │ │ ├── scan abc@ab - │ │ │ ├── columns: a:13 b:14 c:15 - │ │ │ └── ordering: +13 - │ │ └── projections - │ │ └── a:13 * stu.s:1 [as="?column?":19, outer=(1,13), immutable] │ └── filters (true) └── filters (true) diff --git a/pkg/sql/opt/xform/testdata/rules/join_order b/pkg/sql/opt/xform/testdata/rules/join_order index 8a9d8d3ce8c2..a4d9f258eefc 100644 --- a/pkg/sql/opt/xform/testdata/rules/join_order +++ b/pkg/sql/opt/xform/testdata/rules/join_order @@ -316,7 +316,7 @@ memo (optimized, ~24KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d: ├── G1: (inner-join G2 G3 G4) (merge-join G2 G3 G5 inner-join,+1,+10) │ └── [presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d:12] │ ├── best: (merge-join G2="[ordering: +1]" G3 G5 inner-join,+1,+10) - │ └── cost: 2153.99 + │ └── cost: 2151.99 ├── G2: (scan bx,cols=(1,2)) │ ├── [ordering: +1] │ │ ├── best: (scan bx,cols=(1,2)) @@ -327,7 +327,7 @@ memo (optimized, ~24KB, required=[presentation: b:1,x:2,c:5,y:6,a:9,b:10,c:11,d: ├── G3: (inner-join G6 G7 G8) (merge-join G6 G7 G5 inner-join,+5,+11) (lookup-join G9 G8 abc,keyCols=[15],outCols=(5,6,9-12)) │ └── [] │ ├── best: (merge-join G6="[ordering: +5]" G7 G5 inner-join,+5,+11) - │ └── cost: 1079.54 + │ └── cost: 1078.54 ├── G4: (filters G10) ├── G5: (filters) ├── G6: (scan cy,cols=(5,6)) @@ -550,7 +550,7 @@ memo (optimized, ~25KB, required=[presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b: ├── G3: (inner-join G6 G7 G8) (merge-join G6 G7 G5 inner-join,+6,+10) │ ├── [ordering: +(6|10|13)] │ │ ├── best: (merge-join G6="[ordering: +6]" G7="[ordering: +(10|13)]" G5 inner-join,+6,+10) - │ │ └── cost: 3860.47 + │ │ └── cost: 3860.46 │ └── [] │ ├── best: (inner-join G6 G7 G8) │ └── cost: 3401.72 @@ -616,7 +616,7 @@ memo (optimized, ~55KB, required=[presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b: ├── G3: (inner-join G5 G9 G7) (inner-join G9 G5 G7) (inner-join G10 G14 G12) (inner-join G14 G10 G12) (inner-join G15 G18 G12) (inner-join G18 G15 G12) (merge-join G9 G5 G19 inner-join,+10,+6) (merge-join G14 G10 G19 inner-join,+13,+10) (lookup-join G15 G19 abc,keyCols=[10],outCols=(5,6,9,10,13-16)) (merge-join G18 G15 G19 inner-join,+13,+10) │ ├── [ordering: +(6|10|13)] │ │ ├── best: (merge-join G9="[ordering: +(10|13)]" G5="[ordering: +6]" G19 inner-join,+10,+6) - │ │ └── cost: 3860.47 + │ │ └── cost: 3860.48 │ └── [] │ ├── best: (inner-join G5 G9 G7) │ └── cost: 3401.72 @@ -631,7 +631,7 @@ memo (optimized, ~55KB, required=[presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b: ├── G6: (inner-join G2 G9 G21) (inner-join G9 G2 G21) (inner-join G10 G16 G12) (inner-join G16 G10 G12) (inner-join G13 G18 G12) (inner-join G18 G13 G12) (merge-join G9 G2 G19 inner-join,+10,+2) (merge-join G16 G10 G19 inner-join,+13,+10) (lookup-join G13 G19 abc,keyCols=[10],outCols=(1,2,9,10,13-16)) (merge-join G18 G13 G19 inner-join,+13,+10) │ ├── [ordering: +(2|10|13)] │ │ ├── best: (merge-join G9="[ordering: +(10|13)]" G2="[ordering: +2]" G19 inner-join,+10,+2) - │ │ └── cost: 3860.47 + │ │ └── cost: 3860.48 │ └── [] │ ├── best: (inner-join G2 G9 G21) │ └── cost: 3401.72 @@ -660,7 +660,7 @@ memo (optimized, ~55KB, required=[presentation: b:1,x:2,c:5,y:6,d:9,z:10,a:13,b: ├── G11: (inner-join G2 G14 G4) (inner-join G14 G2 G4) (inner-join G5 G16 G4) (inner-join G16 G5 G4) (inner-join G8 G18 G23) (inner-join G18 G8 G23) (merge-join G14 G2 G19 inner-join,+6,+2) (merge-join G16 G5 G19 inner-join,+2,+6) (lookup-join G8 G19 abc,keyCols=[6],outCols=(1,2,5,6,13-16)) (merge-join G18 G8 G19 inner-join,+13,+6) │ ├── [ordering: +(2|6|13)] │ │ ├── best: (merge-join G14="[ordering: +(6|13)]" G2="[ordering: +2]" G19 inner-join,+6,+2) - │ │ └── cost: 3860.47 + │ │ └── cost: 3860.48 │ └── [] │ ├── best: (inner-join G2 G14 G4) │ └── cost: 3401.72 diff --git a/pkg/sql/pgwire/testdata/pgtest/row_description b/pkg/sql/pgwire/testdata/pgtest/row_description index f18071d10c8b..defee35a1477 100644 --- a/pkg/sql/pgwire/testdata/pgtest/row_description +++ b/pkg/sql/pgwire/testdata/pgtest/row_description @@ -123,10 +123,12 @@ RowDescription ---- {"Type":"RowDescription","Fields":[{"Name":"v1","TableOID":0,"TableAttributeNumber":1,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0},{"Name":"v2","TableOID":0,"TableAttributeNumber":2,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0}]} -until crdb_only +# TODO(yuzefovich): we should not be ignoring table OIDs here, but currently we +# have a bug there (#71891). +until crdb_only ignore_table_oids RowDescription ---- -{"Type":"RowDescription","Fields":[{"Name":"v1","TableOID":52,"TableAttributeNumber":1,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0},{"Name":"v2","TableOID":53,"TableAttributeNumber":2,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0}]} +{"Type":"RowDescription","Fields":[{"Name":"v1","TableOID":0,"TableAttributeNumber":0,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0},{"Name":"v2","TableOID":0,"TableAttributeNumber":0,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0}]} until ignore_table_oids ReadyForQuery From c98a488cb2667410572e4f3c36bc37e7b97e14cf Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Fri, 22 Oct 2021 15:31:57 -0500 Subject: [PATCH 027/205] dev: fix support for `--rewrite` tests The `--run_under 'cd $DIR && '` trick didn't work, so instead we inject the path to the workspace via an environment variable and set the sandbox writable paths accordingly. Also update `pkg/spanconfig/spanconfigstore` test to use the Bazel-friendly utils for finding `testdata`. Closes #69570. Release note: None --- pkg/cmd/dev/test.go | 31 ++++++++------------ pkg/cmd/dev/testdata/recording/test.txt | 17 ++++++++++- pkg/cmd/dev/testdata/test.txt | 9 +++++- pkg/spanconfig/spanconfigstore/BUILD.bazel | 1 + pkg/spanconfig/spanconfigstore/store_test.go | 3 +- pkg/testutils/BUILD.bazel | 1 + pkg/testutils/data_path.go | 9 ++++-- 7 files changed, 48 insertions(+), 23 deletions(-) diff --git a/pkg/cmd/dev/test.go b/pkg/cmd/dev/test.go index 75295d0a550d..b535930f3b98 100644 --- a/pkg/cmd/dev/test.go +++ b/pkg/cmd/dev/test.go @@ -53,8 +53,9 @@ func makeTestCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Comm testCmd.Flags().String(stressArgsFlag, "", "Additional arguments to pass to stress") testCmd.Flags().Bool(raceFlag, false, "run tests using race builds") testCmd.Flags().Bool(ignoreCacheFlag, false, "ignore cached test runs") - testCmd.Flags().Bool(rewriteFlag, false, "rewrite test files (only applicable for certain tests, e.g. logic and datadriven tests)") + testCmd.Flags().String(rewriteFlag, "", "argument to pass to underlying (only applicable for certain tests, e.g. logic and datadriven tests). If unspecified, -rewrite will be passed to the test binary.") testCmd.Flags().String(rewriteArgFlag, "", "additional argument to pass to -rewrite (implies --rewrite)") + testCmd.Flags().Lookup(rewriteFlag).NoOptDefVal = "-rewrite" return testCmd } @@ -73,7 +74,10 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { ignoreCache := mustGetFlagBool(cmd, ignoreCacheFlag) verbose := mustGetFlagBool(cmd, vFlag) rewriteArg := mustGetFlagString(cmd, rewriteArgFlag) - rewrite := mustGetFlagBool(cmd, rewriteFlag) || (rewriteArg != "") + rewrite := mustGetFlagString(cmd, rewriteFlag) + if rewriteArg != "" && rewrite == "" { + rewrite = "-rewrite" + } d.log.Printf("unit test args: stress=%t race=%t filter=%s timeout=%s ignore-cache=%t pkgs=%s", stress, race, filter, timeout, ignoreCache, pkgs) @@ -150,32 +154,23 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { if ignoreCache { args = append(args, "--nocache_test_results") } - if rewrite { + if rewrite != "" { if stress { - // Both of these flags require --run_under, and their usages would conflict. return fmt.Errorf("cannot combine --%s and --%s", stressFlag, rewriteFlag) } workspace, err := d.getWorkspace(ctx) if err != nil { return err } - var cdDir string - for _, testTarget := range testTargets { - dir := getDirectoryFromTarget(testTarget) - if cdDir != "" && cdDir != dir { - // We can't pass different run_under arguments for different tests - // in different packages. - return fmt.Errorf("cannot --%s for selected targets: %s. Hint: try only specifying one test target", - rewriteFlag, strings.Join(testTargets, ",")) - } - cdDir = dir - } - args = append(args, "--run_under", fmt.Sprintf("cd %s && ", filepath.Join(workspace, cdDir))) - args = append(args, "--test_env=YOU_ARE_IN_THE_WORKSPACE=1") - args = append(args, "--test_arg", "-rewrite") + args = append(args, fmt.Sprintf("--test_env=COCKROACH_WORKSPACE=%s", workspace)) + args = append(args, "--test_arg", rewrite) if rewriteArg != "" { args = append(args, "--test_arg", rewriteArg) } + for _, testTarget := range testTargets { + dir := getDirectoryFromTarget(testTarget) + args = append(args, fmt.Sprintf("--sandbox_writable_path=%s", filepath.Join(workspace, dir))) + } } if stress && timeout > 0 { args = append(args, "--run_under", fmt.Sprintf("%s -maxtime=%s %s", stressTarget, timeout, stressArgs)) diff --git a/pkg/cmd/dev/testdata/recording/test.txt b/pkg/cmd/dev/testdata/recording/test.txt index b07863ff6884..e25223471f80 100644 --- a/pkg/cmd/dev/testdata/recording/test.txt +++ b/pkg/cmd/dev/testdata/recording/test.txt @@ -176,5 +176,20 @@ bazel info workspace --color=no ---- go/src/github.com/cockroachdb/cockroach -bazel test //pkg/testutils:testutils_test --run_under 'cd go/src/github.com/cockroachdb/cockroach/pkg/testutils && ' --test_env=YOU_ARE_IN_THE_WORKSPACE=1 --test_arg -rewrite --test_output errors +bazel test //pkg/testutils:testutils_test --test_env=COCKROACH_WORKSPACE=go/src/github.com/cockroachdb/cockroach --test_arg -rewrite --sandbox_writable_path=go/src/github.com/cockroachdb/cockroach/pkg/testutils --test_output errors +---- + +bazel query 'kind(go_test, //pkg/testutils:all)' +---- +//pkg/testutils:testutils_test + +bazel query 'kind(go_test, //pkg/other/test:all)' +---- +//pkg/other/test:test_test + +bazel info workspace --color=no +---- +go/src/github.com/cockroachdb/cockroach + +bazel test //pkg/testutils:testutils_test //pkg/other/test:test_test --test_env=COCKROACH_WORKSPACE=go/src/github.com/cockroachdb/cockroach --test_arg -rewrite --sandbox_writable_path=go/src/github.com/cockroachdb/cockroach/pkg/testutils --sandbox_writable_path=go/src/github.com/cockroachdb/cockroach/pkg/other/test --test_output errors ---- diff --git a/pkg/cmd/dev/testdata/test.txt b/pkg/cmd/dev/testdata/test.txt index 40e25bd7dd52..219b42681d86 100644 --- a/pkg/cmd/dev/testdata/test.txt +++ b/pkg/cmd/dev/testdata/test.txt @@ -61,4 +61,11 @@ dev test //pkg/testutils --rewrite ---- bazel query 'kind(go_test, //pkg/testutils:all)' bazel info workspace --color=no -bazel test //pkg/testutils:testutils_test --run_under 'cd go/src/github.com/cockroachdb/cockroach/pkg/testutils && ' --test_env=YOU_ARE_IN_THE_WORKSPACE=1 --test_arg -rewrite --test_output errors +bazel test //pkg/testutils:testutils_test --test_env=COCKROACH_WORKSPACE=go/src/github.com/cockroachdb/cockroach --test_arg -rewrite --sandbox_writable_path=go/src/github.com/cockroachdb/cockroach/pkg/testutils --test_output errors + +dev test //pkg/testutils pkg/other/test --rewrite +---- +bazel query 'kind(go_test, //pkg/testutils:all)' +bazel query 'kind(go_test, //pkg/other/test:all)' +bazel info workspace --color=no +bazel test //pkg/testutils:testutils_test //pkg/other/test:test_test --test_env=COCKROACH_WORKSPACE=go/src/github.com/cockroachdb/cockroach --test_arg -rewrite --sandbox_writable_path=go/src/github.com/cockroachdb/cockroach/pkg/testutils --sandbox_writable_path=go/src/github.com/cockroachdb/cockroach/pkg/other/test --test_output errors diff --git a/pkg/spanconfig/spanconfigstore/BUILD.bazel b/pkg/spanconfig/spanconfigstore/BUILD.bazel index 9384e0a2dee8..5d6524640d87 100644 --- a/pkg/spanconfig/spanconfigstore/BUILD.bazel +++ b/pkg/spanconfig/spanconfigstore/BUILD.bazel @@ -28,6 +28,7 @@ go_test( "//pkg/roachpb:with-mocks", "//pkg/spanconfig", "//pkg/spanconfig/spanconfigtestutils", + "//pkg/testutils", "//pkg/util/leaktest", "//pkg/util/randutil", "@com_github_cockroachdb_datadriven//:datadriven", diff --git a/pkg/spanconfig/spanconfigstore/store_test.go b/pkg/spanconfig/spanconfigstore/store_test.go index 1158aef75ba8..7e4ee3d45222 100644 --- a/pkg/spanconfig/spanconfigstore/store_test.go +++ b/pkg/spanconfig/spanconfigstore/store_test.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/spanconfig" "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigtestutils" + "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/randutil" "github.com/cockroachdb/datadriven" @@ -47,7 +48,7 @@ func TestDataDriven(t *testing.T) { defer leaktest.AfterTest(t)() ctx := context.Background() - datadriven.Walk(t, "testdata", func(t *testing.T, path string) { + datadriven.Walk(t, testutils.TestDataPath(t), func(t *testing.T, path string) { store := New(spanconfigtestutils.ParseConfig(t, "FALLBACK")) datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { var ( diff --git a/pkg/testutils/BUILD.bazel b/pkg/testutils/BUILD.bazel index f8b1dbd1693d..c6e58439d32c 100644 --- a/pkg/testutils/BUILD.bazel +++ b/pkg/testutils/BUILD.bazel @@ -27,6 +27,7 @@ go_library( "//pkg/security", "//pkg/sql/pgwire/pgerror", "//pkg/util", + "//pkg/util/envutil", "//pkg/util/fileutil", "//pkg/util/log", "//pkg/util/retry", diff --git a/pkg/testutils/data_path.go b/pkg/testutils/data_path.go index 51f41c51256e..7ebf66a875ed 100644 --- a/pkg/testutils/data_path.go +++ b/pkg/testutils/data_path.go @@ -11,12 +11,12 @@ package testutils import ( - "os" "path" "path/filepath" "testing" "github.com/cockroachdb/cockroach/pkg/build/bazel" + "github.com/cockroachdb/cockroach/pkg/util/envutil" "github.com/stretchr/testify/require" ) @@ -29,7 +29,12 @@ func TestDataPath(t testing.TB, relative ...string) string { relative = append([]string{"testdata"}, relative...) // dev notifies the library that the test is running in a subdirectory of the // workspace with the environment variable below. - if bazel.BuiltWithBazel() && os.Getenv("YOU_ARE_IN_THE_WORKSPACE") != "" { + if bazel.BuiltWithBazel() { + //lint:ignore SA4006 apparently a linter bug. + cockroachWorkspace, set := envutil.EnvString("COCKROACH_WORKSPACE", 0) + if set { + return path.Join(cockroachWorkspace, bazel.RelativeTestTargetPath(), path.Join(relative...)) + } runfiles, err := bazel.RunfilesPath() require.NoError(t, err) return path.Join(runfiles, bazel.RelativeTestTargetPath(), path.Join(relative...)) From 49a1d2e92f6ee38b358d095c156cf69a3b1617d1 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Tue, 26 Oct 2021 07:10:09 +1100 Subject: [PATCH 028/205] prometheus: fix RunE no actually terminating an instance Copied repeatRunE and noticed there's not description field - woops. I'm at a loss to explain why this does not error as the `terminate` command does not exist. Release note: None --- pkg/cmd/roachtest/prometheus/prometheus.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cmd/roachtest/prometheus/prometheus.go b/pkg/cmd/roachtest/prometheus/prometheus.go index fea7cbff918f..7453bbfcdcc0 100644 --- a/pkg/cmd/roachtest/prometheus/prometheus.go +++ b/pkg/cmd/roachtest/prometheus/prometheus.go @@ -69,7 +69,6 @@ func Init( if err := c.RunE( ctx, cfg.PrometheusNode, - "terminate existing prometheus instance, if exists", "sudo systemctl stop prometheus || echo 'no prometheus is running'", ); err != nil { return nil, err From b957430279b5d00b2857a3663c6e75aaf48911e8 Mon Sep 17 00:00:00 2001 From: rimadeodhar Date: Fri, 22 Oct 2021 10:53:19 -0700 Subject: [PATCH 029/205] metric: Add rules endpoint API. In this PR, the API to consume the defined rules and convert them to YAML has been implemented. The endpoint API uses the rule registry to build a list of alert and recording rules and exports them in YAML format. Release note: None --- pkg/util/metric/BUILD.bazel | 3 + pkg/util/metric/prometheus_rule_exporter.go | 86 +++++++++++++++++++ .../metric/prometheus_rule_exporter_test.go | 62 +++++++++++++ pkg/util/metric/rule.go | 39 +++++++++ pkg/util/metric/rule_registry.go | 18 +++- 5 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 pkg/util/metric/prometheus_rule_exporter.go create mode 100644 pkg/util/metric/prometheus_rule_exporter_test.go diff --git a/pkg/util/metric/BUILD.bazel b/pkg/util/metric/BUILD.bazel index bfcfa5038c95..5740b94689f2 100644 --- a/pkg/util/metric/BUILD.bazel +++ b/pkg/util/metric/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "graphite_exporter.go", "metric.go", "prometheus_exporter.go", + "prometheus_rule_exporter.go", "registry.go", "rule.go", "rule_registry.go", @@ -31,6 +32,7 @@ go_library( "@com_github_prometheus_prometheus//promql/parser", "@com_github_rcrowley_go_metrics//:go-metrics", "@com_github_vividcortex_ewma//:ewma", + "@in_gopkg_yaml_v3//:yaml_v3", ], ) @@ -40,6 +42,7 @@ go_test( srcs = [ "metric_test.go", "prometheus_exporter_test.go", + "prometheus_rule_exporter_test.go", "registry_test.go", "rule_test.go", ], diff --git a/pkg/util/metric/prometheus_rule_exporter.go b/pkg/util/metric/prometheus_rule_exporter.go new file mode 100644 index 000000000000..f70f38e17ac1 --- /dev/null +++ b/pkg/util/metric/prometheus_rule_exporter.go @@ -0,0 +1,86 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package metric + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" + "gopkg.in/yaml.v3" +) + +const ( + alertRuleGroupName = "rules/alerts" + recordingRuleGroupName = "rules/recording" +) + +// PrometheusRuleNode represents an individual rule node within the YAML output. +type PrometheusRuleNode struct { + Record string `yaml:"record,omitempty"` + Alert string `yaml:"alert,omitempty"` + Expr string `yaml:"expr"` + For string `yaml:"for,omitempty"` + Labels map[string]string `yaml:"labels,omitempty"` + Annotations map[string]string `yaml:"annotations,omitempty"` +} + +// PrometheusRuleGroup is a list of recording and alerting rules. +type PrometheusRuleGroup struct { + Rules []PrometheusRuleNode `yaml:"rules"` +} + +// PrometheusRuleExporter initializes recording and alert rules once from the registry. +// These initialized values will be reused for every alert/rule scrapes. +type PrometheusRuleExporter struct { + mu struct { + syncutil.Mutex + RuleGroups map[string]PrometheusRuleGroup + } +} + +// NewPrometheusRuleExporter creates a new PrometheusRuleExporter. +func NewPrometheusRuleExporter() *PrometheusRuleExporter { + var pe PrometheusRuleExporter + pe.mu.RuleGroups = make(map[string]PrometheusRuleGroup) + return &pe +} + +// ScrapeRegistry scrapes the RuleRegistry to convert the list of registered rules +// to Prometheus compatible rules. +func (re *PrometheusRuleExporter) ScrapeRegistry(ctx context.Context, rr *RuleRegistry) { + re.mu.Lock() + defer re.mu.Unlock() + rr.Each(func(rule Rule) { + ruleGroupName, ruleNode := rule.ToPrometheusRuleNode() + if ruleGroupName != alertRuleGroupName && ruleGroupName != recordingRuleGroupName { + log.Warning(ctx, "invalid prometheus group name, skipping rule") + return + } + promRuleGroup := re.mu.RuleGroups[ruleGroupName] + promRuleGroup.Rules = append(promRuleGroup.Rules, ruleNode) + re.mu.RuleGroups[ruleGroupName] = promRuleGroup + }) +} + +// PrintAsYAMLText returns the rules within PrometheusRuleExporter +// as YAML text. +// TODO(rimadeodhar): Pipe through the help field as comments +// in the exported YAML. +func (re *PrometheusRuleExporter) PrintAsYAMLText() (string, error) { + re.mu.Lock() + defer re.mu.Unlock() + output, err := yaml.Marshal(re.mu.RuleGroups) + if err != nil { + return "", err + } + return string(output), nil +} diff --git a/pkg/util/metric/prometheus_rule_exporter_test.go b/pkg/util/metric/prometheus_rule_exporter_test.go new file mode 100644 index 000000000000..bacc65df14c2 --- /dev/null +++ b/pkg/util/metric/prometheus_rule_exporter_test.go @@ -0,0 +1,62 @@ +// Copyright 2015 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package metric + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// TestPrometheusRuleExporter tests that the PrometheusRuleExporter +// generates valid YAML output for a given set of alerting and +// aggregation rules. +func TestPrometheusRuleExporter(t *testing.T) { + ctx := context.Background() + rules, expectedYAMLText := getRulesAndExpectedYAML(t) + registry := NewRuleRegistry() + registry.AddRules(rules) + ruleExporter := NewPrometheusRuleExporter() + ruleExporter.ScrapeRegistry(ctx, registry) + yamlText, err := ruleExporter.PrintAsYAMLText() + require.NoError(t, err) + require.Equal(t, expectedYAMLText, yamlText) +} + +func getRulesAndExpectedYAML(t *testing.T) (rules []Rule, expectedYAML string) { + aggRule, err := NewAggregationRule("test_aggregation_rule_1", "sum without(store) (capacity_available)", nil, "", false) + if err != nil { + t.Fatal(err) + } + rules = append(rules, aggRule) + var annotations []LabelPair + description := "description" + value := "{{ $labels.instance }} for cluster {{ $labels.cluster }} has\n been down for more than 15 minutes." + annotation := LabelPair{ + Name: &description, + Value: &value, + } + annotations = append(annotations, annotation) + alertRule, err := NewAlertingRule("test_alert_rule", "resets(sys_uptime[10m]) > 0 and resets(sys_uptime[10m]) < 5", annotations, nil, time.Minute, "", false) + if err != nil { + t.Fatal(err) + } + rules = append(rules, alertRule) + expectedYAML = "rules/alerts:\n rules:\n - alert: test_alert_rule\n " + + "expr: resets(sys_uptime[10m]) > 0 and resets(sys_uptime[10m]) < 5\n for: 1m0s\n " + + "annotations:\n description: |-\n {{ $labels.instance }} for " + + "cluster {{ $labels.cluster }} has\n been down for more than 15 minutes.\nrules/recording:" + + "\n rules:\n - record: test_aggregation_rule_1\n expr: sum without(store) (capacity_available)\n" + + return rules, expectedYAML +} diff --git a/pkg/util/metric/rule.go b/pkg/util/metric/rule.go index 308bb3c4b4f4..a8fe0ab0ce9c 100644 --- a/pkg/util/metric/rule.go +++ b/pkg/util/metric/rule.go @@ -31,6 +31,10 @@ type Rule interface { // IsKV returns true if the metrics involved in the // rule are for the KV layer. IsKV() bool + // ToPrometheusRuleNode converts the rule to a prometheus equivalent + // rule.This is used by the PrometheusRuleExporter to export all + // rules in a prometheus compatible format. + ToPrometheusRuleNode() (ruleGroupName string, ruleNode PrometheusRuleNode) } // AlertingRule encapsulates an alert specification @@ -135,6 +139,19 @@ func (a *AlertingRule) IsKV() bool { return a.isKV } +// ToPrometheusRuleNode implements the Rule interface. +func (a *AlertingRule) ToPrometheusRuleNode() (ruleGroupName string, ruleNode PrometheusRuleNode) { + var node PrometheusRuleNode + node.Alert = a.name + node.Expr = a.expr + if a.recommendedHoldDuration > 0*time.Second { + node.For = a.recommendedHoldDuration.String() + } + node.Labels = getLabelMap(a.labels) + node.Annotations = getLabelMap(a.annotations) + return alertRuleGroupName, node +} + // Name implements the Rule interface. func (ag *AggregationRule) Name() string { return ag.name @@ -159,3 +176,25 @@ func (ag *AggregationRule) Help() string { func (ag *AggregationRule) IsKV() bool { return ag.isKV } + +// ToPrometheusRuleNode implements the Rule interface. +func (ag *AggregationRule) ToPrometheusRuleNode() ( + ruleGroupName string, + ruleNode PrometheusRuleNode, +) { + var node PrometheusRuleNode + node.Record = ag.name + node.Expr = ag.expr + node.Labels = getLabelMap(ag.labels) + return recordingRuleGroupName, node +} + +func getLabelMap(labels []LabelPair) map[string]string { + labelMap := make(map[string]string) + for _, label := range labels { + if label.Name != nil && label.Value != nil { + labelMap[*label.Name] = *label.Value + } + } + return labelMap +} diff --git a/pkg/util/metric/rule_registry.go b/pkg/util/metric/rule_registry.go index 21a18394efb3..eabd0b1c9e89 100644 --- a/pkg/util/metric/rule_registry.go +++ b/pkg/util/metric/rule_registry.go @@ -28,9 +28,25 @@ func NewRuleRegistry() *RuleRegistry { } } -// AddRule adds a rule to the registry +// AddRule adds a rule to the registry. func (r *RuleRegistry) AddRule(rule Rule) { r.Lock() defer r.Unlock() r.rules = append(r.rules, rule) } + +// AddRules adds multiple rules to the registry. +func (r *RuleRegistry) AddRules(rules []Rule) { + r.Lock() + defer r.Unlock() + r.rules = append(r.rules, rules...) +} + +// Each calls the given closure for all rules. +func (r *RuleRegistry) Each(f func(rule Rule)) { + r.Lock() + defer r.Unlock() + for _, currentRule := range r.rules { + f(currentRule) + } +} From 948b384c7edb4d2c15f38cd0ec1055aeb281521d Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Tue, 14 Sep 2021 22:37:54 -0400 Subject: [PATCH 030/205] row: simplify fetcher to only handle a single table Previously, `row.Fetcher` could handle multiple tables at the same time. This was needed to support the interleaved tables efficiently, but since the interleaved tables are no more, we can simplify the fetcher now. There are more things that can be removed from the fetcher about handling of the interleaved tables, but I chose to defer them to a later commit. This commit's main focus is simplifying the fetcher to work under the assumption that it will be fetching only from a single table. Release note: None --- pkg/ccl/changefeedccl/rowfetcher_cache.go | 3 +- pkg/ccl/cliccl/debug_backup.go | 1 - pkg/sql/delete_range.go | 24 +- pkg/sql/distsql_spec_exec_factory.go | 1 - pkg/sql/indexbackfiller_test.go | 1 - pkg/sql/opt/exec/execbuilder/mutation.go | 153 +----- pkg/sql/opt/exec/factory.opt | 10 +- pkg/sql/opt_exec_factory.go | 15 +- pkg/sql/row/fetcher.go | 541 +++++++--------------- pkg/sql/row/fetcher_mvcc_test.go | 34 +- pkg/sql/row/fetcher_test.go | 98 ++-- 11 files changed, 247 insertions(+), 634 deletions(-) diff --git a/pkg/ccl/changefeedccl/rowfetcher_cache.go b/pkg/ccl/changefeedccl/rowfetcher_cache.go index 68e9f1a54458..1c61ff381dbd 100644 --- a/pkg/ccl/changefeedccl/rowfetcher_cache.go +++ b/pkg/ccl/changefeedccl/rowfetcher_cache.go @@ -146,7 +146,7 @@ func (c *rowFetcherCache) RowFetcherForTableDesc( // guaranteed that the tables have the same version. Additionally, these // fetchers are always initialized with a single tabledesc.Immutable. if rf, ok := c.fetchers[idVer]; ok && - catalog.UserDefinedTypeColsHaveSameVersion(tableDesc, rf.GetTables()[0].(catalog.TableDescriptor)) { + catalog.UserDefinedTypeColsHaveSameVersion(tableDesc, rf.GetTable().(catalog.TableDescriptor)) { return rf, nil } // TODO(dan): Allow for decoding a subset of the columns. @@ -159,7 +159,6 @@ func (c *rowFetcherCache) RowFetcherForTableDesc( var rf row.Fetcher rfArgs := row.FetcherTableArgs{ - Spans: tableDesc.AllIndexSpans(c.codec), Desc: tableDesc, Index: tableDesc.GetPrimaryIndex(), ColIdxMap: colIdxMap, diff --git a/pkg/ccl/cliccl/debug_backup.go b/pkg/ccl/cliccl/debug_backup.go index 6a044dc0be2d..d708bf57f86a 100644 --- a/pkg/ccl/cliccl/debug_backup.go +++ b/pkg/ccl/cliccl/debug_backup.go @@ -586,7 +586,6 @@ func makeRowFetcher( } table := row.FetcherTableArgs{ - Spans: []roachpb.Span{entry.Span}, Desc: entry.Desc, Index: entry.Desc.GetPrimaryIndex(), ColIdxMap: colIdxMap, diff --git a/pkg/sql/delete_range.go b/pkg/sql/delete_range.go index 420a8c00003f..82a3b0945a2e 100644 --- a/pkg/sql/delete_range.go +++ b/pkg/sql/delete_range.go @@ -34,16 +34,10 @@ import ( // However, if the optimizer can prove that only a small number of rows will // be deleted, it'll enable autoCommit for delete range. type deleteRangeNode struct { - // interleavedFastPath is true if we can take the fast path despite operating - // on an interleaved table. - interleavedFastPath bool // spans are the spans to delete. spans roachpb.Spans // desc is the table descriptor the delete is operating on. desc catalog.TableDescriptor - // interleavedDesc are the table descriptors of any child interleaved tables - // the delete is operating on. - interleavedDesc []catalog.TableDescriptor // fetcher is around to decode the returned keys from the DeleteRange, so that // we can count the number of rows deleted. fetcher row.Fetcher @@ -92,26 +86,12 @@ func (d *deleteRangeNode) startExec(params runParams) error { if err := params.p.cancelChecker.Check(); err != nil { return err } - if d.interleavedFastPath { - for i := range d.spans { - d.spans[i].EndKey = d.spans[i].EndKey.PrefixEnd() - } - } // Configure the fetcher, which is only used to decode the returned keys from // the DeleteRange, and is never used to actually fetch kvs. - allTables := make([]row.FetcherTableArgs, len(d.interleavedDesc)+1) - allTables[0] = row.FetcherTableArgs{ + table := row.FetcherTableArgs{ Desc: d.desc, Index: d.desc.GetPrimaryIndex(), - Spans: d.spans, - } - for i, interleaved := range d.interleavedDesc { - allTables[i+1] = row.FetcherTableArgs{ - Desc: interleaved, - Index: interleaved.GetPrimaryIndex(), - Spans: d.spans, - } } if err := d.fetcher.Init( params.ctx, @@ -123,7 +103,7 @@ func (d *deleteRangeNode) startExec(params runParams) error { false, /* isCheck */ params.p.alloc, nil, /* memMonitor */ - allTables..., + table, ); err != nil { return err } diff --git a/pkg/sql/distsql_spec_exec_factory.go b/pkg/sql/distsql_spec_exec_factory.go index f3d5d1882384..12bc449d4182 100644 --- a/pkg/sql/distsql_spec_exec_factory.go +++ b/pkg/sql/distsql_spec_exec_factory.go @@ -921,7 +921,6 @@ func (e *distSQLSpecExecFactory) ConstructDeleteRange( table cat.Table, needed exec.TableColumnOrdinalSet, indexConstraint *constraint.Constraint, - interleavedTables []cat.Table, autoCommit bool, ) (exec.Node, error) { return nil, unimplemented.NewWithIssue(47473, "experimental opt-driven distsql planning: delete range") diff --git a/pkg/sql/indexbackfiller_test.go b/pkg/sql/indexbackfiller_test.go index 75bbe45ba4b3..35d73d63e876 100644 --- a/pkg/sql/indexbackfiller_test.go +++ b/pkg/sql/indexbackfiller_test.go @@ -390,7 +390,6 @@ INSERT INTO foo VALUES (1), (10), (100); &alloc, mm.Monitor(), row.FetcherTableArgs{ - Spans: spans, Desc: table, Index: idx, ColIdxMap: colIdxMap, diff --git a/pkg/sql/opt/exec/execbuilder/mutation.go b/pkg/sql/opt/exec/execbuilder/mutation.go index 6a7b14300993..10339d91ca08 100644 --- a/pkg/sql/opt/exec/execbuilder/mutation.go +++ b/pkg/sql/opt/exec/execbuilder/mutation.go @@ -12,7 +12,6 @@ package execbuilder import ( "bytes" - "context" "fmt" "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" @@ -556,25 +555,13 @@ func (b *Builder) tryBuildDeleteRange(del *memo.DeleteExpr) (_ execPlan, ok bool return execPlan{}, false, nil } - primaryIdx := tab.Index(cat.PrimaryIndex) - - // If the table is interleaved in another table, we cannot use the fast path. - if primaryIdx.InterleaveAncestorCount() > 0 { - return execPlan{}, false, nil - } - - if primaryIdx.InterleavedByCount() > 0 { - return b.tryBuildDeleteRangeOnInterleaving(del, tab) - } - - // No other tables interleaved inside this table. We can use the fast path - // if we don't need to buffer the input to the delete operator (for foreign - // key checks/cascades). + // We can use the fast path if we don't need to buffer the input to the + // delete operator (for foreign key checks/cascades). if del.WithID != 0 { return execPlan{}, false, nil } - ep, err := b.buildDeleteRange(del, nil /* interleavedTables */) + ep, err := b.buildDeleteRange(del) if err != nil { return execPlan{}, false, err } @@ -587,116 +574,10 @@ func (b *Builder) tryBuildDeleteRange(del *memo.DeleteExpr) (_ execPlan, ok bool return ep, true, nil } -// tryBuildDeleteRangeOnInterleaving attempts to construct a fast DeleteRange -// execution for a logical Delete operator when the table is at the root of an -// interleaving hierarchy. -// -// We can use DeleteRange only when foreign keys are set up such that a deletion -// of a row cascades into deleting all interleaved rows with the same prefix. -// More specifically, the following conditions must apply: -// - none of the tables in the hierarchy have secondary indexes; -// - none of the tables in the hierarchy are referenced by any tables outside -// the hierarchy; -// - all foreign key references between tables in the hierarchy have columns -// that match the interleaving; -// - all tables in the interleaving hierarchy have at least an ON DELETE -// CASCADE foreign key reference to an ancestor. -// -func (b *Builder) tryBuildDeleteRangeOnInterleaving( - del *memo.DeleteExpr, root cat.Table, -) (_ execPlan, ok bool, _ error) { - // To check the conditions above, we explore the entire hierarchy using - // breadth-first search. - queue := make([]cat.Table, 0, root.Index(cat.PrimaryIndex).InterleavedByCount()) - tables := make(map[cat.StableID]cat.Table) - tables[root.ID()] = root - queue = append(queue, root) - for queuePos := 0; queuePos < len(queue); queuePos++ { - currTab := queue[queuePos] - - if currTab.DeletableIndexCount() > 1 { - return execPlan{}, false, nil - } - - currIdx := currTab.Index(cat.PrimaryIndex) - for i, n := 0, currIdx.InterleavedByCount(); i < n; i++ { - // We don't care about the index ID because we bail if any of the tables - // have any secondary indexes anyway. - tableID, _ := currIdx.InterleavedBy(i) - if tab, ok := tables[tableID]; ok { - err := errors.AssertionFailedf("multiple interleave paths to table %s", tab.Name()) - return execPlan{}, false, err - } - ds, _, err := b.catalog.ResolveDataSourceByID(context.TODO(), cat.Flags{}, tableID) - if err != nil { - return execPlan{}, false, err - } - child := ds.(cat.Table) - tables[tableID] = child - queue = append(queue, child) - } - } - - // Verify that there are no "inbound" foreign key references from outside the - // hierarchy and that all foreign key references between tables in the hierarchy - // match the interleaving (i.e. a prefix of the PK of the child references the - // PK of the ancestor). - for _, parent := range queue { - for i, n := 0, parent.InboundForeignKeyCount(); i < n; i++ { - fk := parent.InboundForeignKey(i) - child, ok := tables[fk.OriginTableID()] - if !ok { - // Foreign key from a table outside of the hierarchy. - return execPlan{}, false, nil - } - childIdx := child.Index(cat.PrimaryIndex) - parentIdx := parent.Index(cat.PrimaryIndex) - numCols := fk.ColumnCount() - if parentIdx.KeyColumnCount() != numCols || childIdx.KeyColumnCount() < numCols { - return execPlan{}, false, nil - } - for i := 0; i < numCols; i++ { - if fk.OriginColumnOrdinal(child, i) != childIdx.Column(i).Ordinal() { - return execPlan{}, false, nil - } - if fk.ReferencedColumnOrdinal(parent, i) != parentIdx.Column(i).Ordinal() { - return execPlan{}, false, nil - } - } - } - } - - // Finally, verify that each table (except for the root) has an ON DELETE - // CASCADE foreign key reference to another table in the hierarchy. - for _, tab := range queue[1:] { - found := false - for i, n := 0, tab.OutboundForeignKeyCount(); i < n; i++ { - fk := tab.OutboundForeignKey(i) - if fk.DeleteReferenceAction() == tree.Cascade && tables[fk.ReferencedTableID()] != nil { - // Note that we must have already checked above that this foreign key matches - // the interleaving. - found = true - break - } - } - if !found { - return execPlan{}, false, nil - } - } - - ep, err := b.buildDeleteRange(del, queue[1:]) - if err != nil { - return execPlan{}, false, err - } - return ep, true, nil -} - // buildDeleteRange constructs a DeleteRange operator that deletes contiguous // rows in the primary index; the caller must have already checked the // conditions which allow use of DeleteRange. -func (b *Builder) buildDeleteRange( - del *memo.DeleteExpr, interleavedTables []cat.Table, -) (execPlan, error) { +func (b *Builder) buildDeleteRange(del *memo.DeleteExpr) (execPlan, error) { // tryBuildDeleteRange has already validated that input is a Scan operator. scan := del.Input.(*memo.ScanExpr) tab := b.mem.Metadata().Table(scan.Table) @@ -714,33 +595,23 @@ func (b *Builder) buildDeleteRange( // request doesn't work properly if the limit is hit. So, we permit autocommit // here if we can guarantee that the number of returned keys is finite and // relatively small. + // + // Mutations only allow auto-commit if there are no FK checks or cascades. - // We can't calculate the maximum number of keys if there are interleaved - // children, as we don't know how many children rows may be in range. - if len(interleavedTables) == 0 { - if maxRows, ok := b.indexConstraintMaxResults(&scan.ScanPrivate, scan.Relational()); ok { - if maxKeys := maxRows * uint64(tab.FamilyCount()); maxKeys <= row.TableTruncateChunkSize { - // Other mutations only allow auto-commit if there are no FK checks or - // cascades. In this case, we won't actually execute anything for the - // checks or cascades - if we got this far, we determined that the FKs - // match the interleaving hierarchy and a delete range is sufficient. - autoCommit = true - } - } - if len(del.FKChecks) > 0 || len(del.FKCascades) > 0 { - // Do not allow autocommit if we have checks or cascades. This does not - // apply for the interleaved case, where we decided that the delete - // range takes care of all the FKs as well. - autoCommit = false + if maxRows, ok := b.indexConstraintMaxResults(&scan.ScanPrivate, scan.Relational()); ok { + if maxKeys := maxRows * uint64(tab.FamilyCount()); maxKeys <= row.TableTruncateChunkSize { + autoCommit = true } } + if len(del.FKChecks) > 0 || len(del.FKCascades) > 0 { + autoCommit = false + } } root, err := b.factory.ConstructDeleteRange( tab, needed, scan.Constraint, - interleavedTables, autoCommit, ) if err != nil { diff --git a/pkg/sql/opt/exec/factory.opt b/pkg/sql/opt/exec/factory.opt index e2fd76ffa632..1e433b30d3cb 100644 --- a/pkg/sql/opt/exec/factory.opt +++ b/pkg/sql/opt/exec/factory.opt @@ -559,23 +559,15 @@ define Delete { # true: # - there are no secondary indexes; # - the input to the delete is a scan (without limits); -# - the table is not involved in interleaving, or it is at the root of an -# interleaving hierarchy with cascading FKs such that a delete of a row -# cascades and deletes all interleaved rows corresponding to that row; -# - there are no inbound FKs to the table (other than within the -# interleaving as described above). +# - there are no inbound FKs to the table. # # See the comment for ConstructScan for descriptions of the needed and # indexConstraint parameters, since DeleteRange combines Delete + Scan into a # single operator. -# -# If any interleavedTables are passed, they are all the descendant tables in -# an interleaving hierarchy we are deleting from. define DeleteRange { Table cat.Table Needed exec.TableColumnOrdinalSet IndexConstraint *constraint.Constraint - InterleavedTables []cat.Table # If set, the operator will commit the transaction as part of its execution. # This is false when executing inside an explicit transaction, or there are diff --git a/pkg/sql/opt_exec_factory.go b/pkg/sql/opt_exec_factory.go index a8c349e600ca..30f38daf609f 100644 --- a/pkg/sql/opt_exec_factory.go +++ b/pkg/sql/opt_exec_factory.go @@ -1728,7 +1728,6 @@ func (ef *execFactory) ConstructDeleteRange( table cat.Table, needed exec.TableColumnOrdinalSet, indexConstraint *constraint.Constraint, - interleavedTables []cat.Table, autoCommit bool, ) (exec.Node, error) { tabDesc := table.(*optTable).desc @@ -1746,19 +1745,11 @@ func (ef *execFactory) ConstructDeleteRange( } dr := &deleteRangeNode{ - interleavedFastPath: false, - spans: spans, - desc: tabDesc, - autoCommitEnabled: autoCommit, + spans: spans, + desc: tabDesc, + autoCommitEnabled: autoCommit, } - if len(interleavedTables) > 0 { - dr.interleavedFastPath = true - dr.interleavedDesc = make([]catalog.TableDescriptor, len(interleavedTables)) - for i := range dr.interleavedDesc { - dr.interleavedDesc[i] = interleavedTables[i].(*optTable).desc - } - } return dr, nil } diff --git a/pkg/sql/row/fetcher.go b/pkg/sql/row/fetcher.go index 91f623d730bf..a716d91bbfb9 100644 --- a/pkg/sql/row/fetcher.go +++ b/pkg/sql/row/fetcher.go @@ -61,15 +61,10 @@ type tableInfo struct { // Used to determine whether a key retrieved belongs to the span we // want to scan. - spans roachpb.Spans desc catalog.TableDescriptor index catalog.Index isSecondaryIndex bool indexColumnDirs []descpb.IndexDescriptor_Direction - // equivSignature is an equivalence class for each unique table-index - // pair. It allows us to check if an index key belongs to a given - // table-index. - equivSignature []byte // The table columns to use for fetching, possibly including ones currently in // schema changes. @@ -144,11 +139,6 @@ type tableInfo struct { // FetcherTableArgs are the arguments passed to Fetcher.Init // for a given table that includes descriptors and row information. type FetcherTableArgs struct { - // The spans of keys to return for the given table. Fetcher - // ignores keys outside these spans. - // This is irrelevant if Fetcher is initialize with only one - // table. - Spans roachpb.Spans Desc catalog.TableDescriptor Index catalog.Index ColIdxMap catalog.TableColMap @@ -188,8 +178,7 @@ func (fta *FetcherTableArgs) InitCols( } } -// Fetcher handles fetching kvs and forming table rows for an -// arbitrary number of tables. +// Fetcher handles fetching kvs and forming table rows for a single table. // Usage: // var rf Fetcher // err := rf.Init(..) @@ -209,28 +198,15 @@ type Fetcher struct { // codec is used to encode and decode sql keys. codec keys.SQLCodec - // tables is a slice of all the tables and their descriptors for which - // rows are returned. - tables []tableInfo - - // allEquivSignatures is a map used for checking if an equivalence - // signature belongs to any table or table's ancestor. It also maps the - // string representation of every table's and every table's ancestors' - // signature to the table's index in 'tables' for lookup during decoding. - // If 2+ tables share the same ancestor signature, allEquivSignatures - // will map the signature to the largest 'tables' index. - // The full signature for a given table in 'tables' will always map to - // its own index in 'tables'. - allEquivSignatures map[string]int + table tableInfo // reverse denotes whether or not the spans should be read in reverse // or not when StartScan is invoked. reverse bool - // maxKeysPerRow memoizes the maximum number of keys per row - // out of all the tables. This is used to calculate the kvBatchFetcher's - // firstBatchLimit. - maxKeysPerRow int + // numKeysPerRow is the number of keys per row of the table used to + // calculate the kvBatchFetcher's firstBatchLimit. + numKeysPerRow int // True if the index key must be decoded. // If there is more than one table, the index key must always be decoded. @@ -256,8 +232,7 @@ type Fetcher struct { traceKV bool // mvccDecodeStrategy controls whether or not MVCC timestamps should - // be decoded from KV's fetched. It is set if any of the requested tables - // are required to produce an MVCC timestamp system column. + // be decoded from KV's fetched. mvccDecodeStrategy MVCCDecodingStrategy // -- Fields updated during a scan -- @@ -268,11 +243,6 @@ type Fetcher struct { valueColsFound int // how many needed cols we've found so far in the value - rowReadyTable *tableInfo // the table for which a row was fully decoded and ready for output - currentTable *tableInfo // the most recent table for which a key was decoded - keySigBuf []byte // buffer for the index key's signature - keyRestBuf []byte // buffer for the rest of the index key that is not part of the signature - // The current key/value, unless kvEnd is true. kv roachpb.KeyValue keyRemainingBytes []byte @@ -300,7 +270,7 @@ type Fetcher struct { // reallocation of all of those slice fields. func (rf *Fetcher) Reset() { *rf = Fetcher{ - tables: rf.tables[:0], + table: rf.table, } } @@ -328,12 +298,8 @@ func (rf *Fetcher) Init( isCheck bool, alloc *rowenc.DatumAlloc, memMonitor *mon.BytesMonitor, - tables ...FetcherTableArgs, + tableArgs FetcherTableArgs, ) error { - if len(tables) == 0 { - return errors.AssertionFailedf("no tables to fetch from") - } - rf.codec = codec rf.reverse = reverse rf.lockStrength = lockStrength @@ -349,223 +315,153 @@ func (rf *Fetcher) Init( rf.kvFetcherMemAcc = &memAcc } - // We must always decode the index key if we need to distinguish between - // rows from more than one table. - nTables := len(tables) - multipleTables := nTables >= 2 - rf.mustDecodeIndexKey = multipleTables - if multipleTables { - rf.allEquivSignatures = make(map[string]int, len(tables)) - } - - if cap(rf.tables) >= nTables { - rf.tables = rf.tables[:nTables] - } else { - rf.tables = make([]tableInfo, nTables) - } - for tableIdx, tableArgs := range tables { - oldTable := rf.tables[tableIdx] - - table := tableInfo{ - spans: tableArgs.Spans, - desc: tableArgs.Desc, - colIdxMap: tableArgs.ColIdxMap, - index: tableArgs.Index, - isSecondaryIndex: tableArgs.IsSecondaryIndex, - cols: tableArgs.Cols, - row: make(rowenc.EncDatumRow, len(tableArgs.Cols)), - decodedRow: make(tree.Datums, len(tableArgs.Cols)), - - // These slice fields might get re-allocated below, so reslice them from - // the old table here in case they've got enough capacity already. - indexColIdx: oldTable.indexColIdx[:0], - keyVals: oldTable.keyVals[:0], - extraVals: oldTable.extraVals[:0], - timestampOutputIdx: noOutputColumn, - oidOutputIdx: noOutputColumn, - } - - var err error - if multipleTables { - // We produce references to every signature's reference. - equivSignatures, err := rowenc.TableEquivSignatures(table.desc, table.index) - if err != nil { - return err - } - for i, sig := range equivSignatures { - // We always map the table's equivalence signature (last - // 'sig' in 'equivSignatures') to its tableIdx. - // This allows us to overwrite previous "ancestor - // signatures" (see below). - if i == len(equivSignatures)-1 { - rf.allEquivSignatures[string(sig)] = tableIdx - break - } - // Map each table's ancestors' signatures to -1 so - // we know during ReadIndexKey if the parsed index - // key belongs to ancestor or one of our tables. - // We must check if the signature has already been set - // since it's possible for a later 'table' to have an - // ancestor that is a previous 'table', and we do not - // want to overwrite the previous table's tableIdx. - if _, exists := rf.allEquivSignatures[string(sig)]; !exists { - rf.allEquivSignatures[string(sig)] = -1 - } - } - // The last signature is the given table's equivalence signature. - table.equivSignature = equivSignatures[len(equivSignatures)-1] - } - - // Scan through the entire columns map to see which columns are - // required. - for _, col := range table.cols { - idx := table.colIdxMap.GetDefault(col.GetID()) - if tableArgs.ValNeededForCol.Contains(idx) { - // The idx-th column is required. - table.neededCols.Add(int(col.GetID())) - - // Set up any system column metadata, if this column is a system column. - switch colinfo.GetSystemColumnKindFromColumnID(col.GetID()) { - case descpb.SystemColumnKind_MVCCTIMESTAMP: - table.timestampOutputIdx = idx - rf.mvccDecodeStrategy = MVCCDecodingRequired - case descpb.SystemColumnKind_TABLEOID: - table.oidOutputIdx = idx - table.tableOid = tree.NewDOid(tree.DInt(tableArgs.Desc.GetID())) - } + table := &rf.table + *table = tableInfo{ + desc: tableArgs.Desc, + colIdxMap: tableArgs.ColIdxMap, + index: tableArgs.Index, + isSecondaryIndex: tableArgs.IsSecondaryIndex, + cols: tableArgs.Cols, + row: make(rowenc.EncDatumRow, len(tableArgs.Cols)), + decodedRow: make(tree.Datums, len(tableArgs.Cols)), + + // These slice fields might get re-allocated below, so reslice them from + // the old table here in case they've got enough capacity already. + indexColIdx: rf.table.indexColIdx[:0], + keyVals: rf.table.keyVals[:0], + extraVals: rf.table.extraVals[:0], + timestampOutputIdx: noOutputColumn, + oidOutputIdx: noOutputColumn, + } + + var err error + + // Scan through the entire columns map to see which columns are + // required. + for _, col := range table.cols { + idx := table.colIdxMap.GetDefault(col.GetID()) + if tableArgs.ValNeededForCol.Contains(idx) { + // The idx-th column is required. + table.neededCols.Add(int(col.GetID())) + + // Set up any system column metadata, if this column is a system column. + switch colinfo.GetSystemColumnKindFromColumnID(col.GetID()) { + case descpb.SystemColumnKind_MVCCTIMESTAMP: + table.timestampOutputIdx = idx + rf.mvccDecodeStrategy = MVCCDecodingRequired + case descpb.SystemColumnKind_TABLEOID: + table.oidOutputIdx = idx + table.tableOid = tree.NewDOid(tree.DInt(tableArgs.Desc.GetID())) } } + } - table.knownPrefixLength = len( - rowenc.MakeIndexKeyPrefix(codec, table.desc, table.index.GetID()), - ) + table.knownPrefixLength = len( + rowenc.MakeIndexKeyPrefix(codec, table.desc, table.index.GetID()), + ) - var indexColumnIDs []descpb.ColumnID - indexColumnIDs, table.indexColumnDirs = catalog.FullIndexColumnIDs(table.index) + var indexColumnIDs []descpb.ColumnID + indexColumnIDs, table.indexColumnDirs = catalog.FullIndexColumnIDs(table.index) - table.neededValueColsByIdx = tableArgs.ValNeededForCol.Copy() - neededIndexCols := 0 - nIndexCols := len(indexColumnIDs) - if cap(table.indexColIdx) >= nIndexCols { - table.indexColIdx = table.indexColIdx[:nIndexCols] - } else { - table.indexColIdx = make([]int, nIndexCols) - } - for i, id := range indexColumnIDs { - colIdx, ok := table.colIdxMap.Get(id) - if ok { - table.indexColIdx[i] = colIdx - if table.neededCols.Contains(int(id)) { - neededIndexCols++ - table.neededValueColsByIdx.Remove(colIdx) - } - } else { - table.indexColIdx[i] = -1 - if table.neededCols.Contains(int(id)) { - return errors.AssertionFailedf("needed column %d not in colIdxMap", id) - } + table.neededValueColsByIdx = tableArgs.ValNeededForCol.Copy() + neededIndexCols := 0 + nIndexCols := len(indexColumnIDs) + if cap(table.indexColIdx) >= nIndexCols { + table.indexColIdx = table.indexColIdx[:nIndexCols] + } else { + table.indexColIdx = make([]int, nIndexCols) + } + for i, id := range indexColumnIDs { + colIdx, ok := table.colIdxMap.Get(id) + if ok { + table.indexColIdx[i] = colIdx + if table.neededCols.Contains(int(id)) { + neededIndexCols++ + table.neededValueColsByIdx.Remove(colIdx) } - } - - // In order to track #40410 more effectively, check that the contents of - // table.neededValueColsByIdx are valid. - for idx, ok := table.neededValueColsByIdx.Next(0); ok; idx, ok = table.neededValueColsByIdx.Next(idx + 1) { - if idx >= len(table.row) || idx < 0 { - return errors.AssertionFailedf( - "neededValueColsByIdx contains an invalid index. column %d requested, but table has %d columns", - idx, - len(table.row), - ) + } else { + table.indexColIdx[i] = -1 + if table.neededCols.Contains(int(id)) { + return errors.AssertionFailedf("needed column %d not in colIdxMap", id) } } + } - // - If there is more than one table, we have to decode the index key to - // figure out which table the row belongs to. - // - If there are interleaves, we need to read the index key in order to - // determine whether this row is actually part of the index we're scanning. - // - If there are needed columns from the index key, we need to read it. - // - // Otherwise, we can completely avoid decoding the index key. - if !rf.mustDecodeIndexKey && (neededIndexCols > 0 || table.index.NumInterleavedBy() > 0 || table.index.NumInterleaveAncestors() > 0) { - rf.mustDecodeIndexKey = true + // In order to track #40410 more effectively, check that the contents of + // table.neededValueColsByIdx are valid. + for idx, ok := table.neededValueColsByIdx.Next(0); ok; idx, ok = table.neededValueColsByIdx.Next(idx + 1) { + if idx >= len(table.row) || idx < 0 { + return errors.AssertionFailedf( + "neededValueColsByIdx contains an invalid index. column %d requested, but table has %d columns", + idx, + len(table.row), + ) } + } - // The number of columns we need to read from the value part of the key. - // It's the total number of needed columns minus the ones we read from the - // index key, except for composite columns. - table.neededValueCols = table.neededCols.Len() - neededIndexCols + table.index.NumCompositeColumns() - - if table.isSecondaryIndex { - colIDs := table.index.CollectKeyColumnIDs() - colIDs.UnionWith(table.index.CollectSecondaryStoredColumnIDs()) - colIDs.UnionWith(table.index.CollectKeySuffixColumnIDs()) - for i := range table.cols { - if table.neededCols.Contains(int(table.cols[i].GetID())) && !colIDs.Contains(table.cols[i].GetID()) { - return errors.Errorf("requested column %s not in index", table.cols[i].GetName()) - } + // - If there is more than one table, we have to decode the index key to + // figure out which table the row belongs to. + // - If there are interleaves, we need to read the index key in order to + // determine whether this row is actually part of the index we're scanning. + // - If there are needed columns from the index key, we need to read it. + // + // Otherwise, we can completely avoid decoding the index key. + if !rf.mustDecodeIndexKey && (neededIndexCols > 0 || table.index.NumInterleavedBy() > 0 || table.index.NumInterleaveAncestors() > 0) { + rf.mustDecodeIndexKey = true + } + + // The number of columns we need to read from the value part of the key. + // It's the total number of needed columns minus the ones we read from the + // index key, except for composite columns. + table.neededValueCols = table.neededCols.Len() - neededIndexCols + table.index.NumCompositeColumns() + + if table.isSecondaryIndex { + colIDs := table.index.CollectKeyColumnIDs() + colIDs.UnionWith(table.index.CollectSecondaryStoredColumnIDs()) + colIDs.UnionWith(table.index.CollectKeySuffixColumnIDs()) + for i := range table.cols { + if table.neededCols.Contains(int(table.cols[i].GetID())) && !colIDs.Contains(table.cols[i].GetID()) { + return errors.Errorf("requested column %s not in index", table.cols[i].GetName()) } } + } - // Prepare our index key vals slice. - table.keyValTypes, err = colinfo.GetColumnTypes(table.desc, indexColumnIDs, table.keyValTypes) - if err != nil { - return err - } - if cap(table.keyVals) >= nIndexCols { - table.keyVals = table.keyVals[:nIndexCols] + // Prepare our index key vals slice. + table.keyValTypes, err = colinfo.GetColumnTypes(table.desc, indexColumnIDs, table.keyValTypes) + if err != nil { + return err + } + if cap(table.keyVals) >= nIndexCols { + table.keyVals = table.keyVals[:nIndexCols] + } else { + table.keyVals = make([]rowenc.EncDatum, nIndexCols) + } + + if hasExtraCols(table) { + // Unique secondary indexes have a value that is the + // primary index key. + // Primary indexes only contain ascendingly-encoded + // values. If this ever changes, we'll probably have to + // figure out the directions here too. + table.extraTypes, err = colinfo.GetColumnTypes(table.desc, table.index.IndexDesc().KeySuffixColumnIDs, table.extraTypes) + nExtraColumns := table.index.NumKeySuffixColumns() + if cap(table.extraVals) >= nExtraColumns { + table.extraVals = table.extraVals[:nExtraColumns] } else { - table.keyVals = make([]rowenc.EncDatum, nIndexCols) + table.extraVals = make([]rowenc.EncDatum, nExtraColumns) } - - if hasExtraCols(&table) { - // Unique secondary indexes have a value that is the - // primary index key. - // Primary indexes only contain ascendingly-encoded - // values. If this ever changes, we'll probably have to - // figure out the directions here too. - table.extraTypes, err = colinfo.GetColumnTypes(table.desc, table.index.IndexDesc().KeySuffixColumnIDs, table.extraTypes) - nExtraColumns := table.index.NumKeySuffixColumns() - if cap(table.extraVals) >= nExtraColumns { - table.extraVals = table.extraVals[:nExtraColumns] - } else { - table.extraVals = make([]rowenc.EncDatum, nExtraColumns) - } - if err != nil { - return err - } - } - - // Keep track of the maximum keys per row to accommodate a - // limitHint when StartScan is invoked. - keysPerRow, err := table.desc.KeysPerRow(table.index.GetID()) if err != nil { return err } - if keysPerRow > rf.maxKeysPerRow { - rf.maxKeysPerRow = keysPerRow - } - - rf.tables[tableIdx] = table - } - - if len(tables) == 1 { - // If there is more than one table, currentTable will be - // updated every time NextKey is invoked and rowReadyTable - // will be updated when a row is fully decoded. - rf.currentTable = &(rf.tables[0]) - rf.rowReadyTable = &(rf.tables[0]) } - return nil + rf.numKeysPerRow, err = table.desc.KeysPerRow(table.index.GetID()) + return err } -// GetTables returns all tables that this Fetcher was initialized with. -func (rf *Fetcher) GetTables() []catalog.Descriptor { - ret := make([]catalog.Descriptor, len(rf.tables)) - for i := range rf.tables { - ret[i] = rf.tables[i].desc - } - return ret +// GetTable returns the table that this Fetcher was initialized with. +func (rf *Fetcher) GetTable() catalog.Descriptor { + return rf.table.desc } // StartScan initializes and starts the key-value scan. Can be used multiple @@ -733,7 +629,7 @@ func (rf *Fetcher) rowLimitToKeyLimit(rowLimitHint rowinfra.RowLimit) rowinfra.K // rows we could potentially scan over. // // We add an extra key to make sure we form the last row. - return rowinfra.KeyLimit(int64(rowLimitHint)*int64(rf.maxKeysPerRow) + 1) + return rowinfra.KeyLimit(int64(rowLimitHint)*int64(rf.numKeysPerRow) + 1) } // StartScanFrom initializes and starts a scan from the given kvBatchFetcher. Can be @@ -788,15 +684,12 @@ func (rf *Fetcher) NextKey(ctx context.Context) (rowDone bool, err error) { rf.kvEnd = !moreKVs if rf.kvEnd { - // No more keys in the scan. We need to transition - // rf.rowReadyTable to rf.currentTable for the last - // row. + // No more keys in the scan. // // NB: this assumes that the KV layer will never split a range // between column families, which is a brittle assumption. // See: // https://github.com/cockroachdb/cockroach/pull/42056 - rf.rowReadyTable = rf.currentTable return true, nil } @@ -824,18 +717,6 @@ func (rf *Fetcher) NextKey(ctx context.Context) (rowDone bool, err error) { // reading the index key. if unchangedPrefix { // Skip decoding! - // We must set the rowReadyTable to the currentTable like ReadIndexKey - // would do. This will happen when we see 2 rows in a row with the same - // prefix. If the previous prefix was from a different table, then we must - // update the ready table to the current table, updating the fetcher state - // machine to recognize that the next row that it outputs will be from - // rf.currentTable, which will be set to the table of the key that was - // last sent to ReadIndexKey. - // - // TODO(jordan): this is a major (but correct) mess. The fetcher is past - // due for a refactor, now that it's (more) clear what the state machine - // it's trying to model is. - rf.rowReadyTable = rf.currentTable } else if rf.mustDecodeIndexKey || rf.traceKV { rf.keyRemainingBytes, moreKVs, foundNull, err = rf.ReadIndexKey(rf.kv.Key) if err != nil { @@ -876,8 +757,8 @@ func (rf *Fetcher) NextKey(ctx context.Context) (rowDone bool, err error) { // them when processing the index. The difference with unique secondary indexes // is that the extra columns are not always there, and are used to unique-ify // the index key, rather than provide the primary key column values. - if foundNull && rf.currentTable.isSecondaryIndex && rf.currentTable.index.IsUnique() && len(rf.currentTable.desc.GetFamilies()) != 1 { - for i := 0; i < rf.currentTable.index.NumKeySuffixColumns(); i++ { + if foundNull && rf.table.isSecondaryIndex && rf.table.index.IsUnique() && len(rf.table.desc.GetFamilies()) != 1 { + for i := 0; i < rf.table.index.NumKeySuffixColumns(); i++ { var err error // Slice off an extra encoded column from rf.keyRemainingBytes. rf.keyRemainingBytes, err = rowenc.SkipTableKey(rf.keyRemainingBytes) @@ -888,17 +769,13 @@ func (rf *Fetcher) NextKey(ctx context.Context) (rowDone bool, err error) { } switch { - case len(rf.currentTable.desc.GetFamilies()) == 1: + case len(rf.table.desc.GetFamilies()) == 1: // If we only have one family, we know that there is only 1 k/v pair per row. rowDone = true case !unchangedPrefix: // If the prefix of the key has changed, current key is from a different // row than the previous one. rowDone = true - case rf.rowReadyTable != rf.currentTable: - // For rowFetchers with more than one table, if the table changes the row - // is done. - rowDone = true default: rowDone = false } @@ -936,94 +813,26 @@ func (rf *Fetcher) prettyEncDatums(types []*types.T, vals []rowenc.EncDatum) str func (rf *Fetcher) ReadIndexKey( key roachpb.Key, ) (remaining []byte, ok bool, foundNull bool, err error) { - // If there is only one table to check keys for, there is no need - // to go through the equivalence signature checks. - if len(rf.tables) == 1 { - return rowenc.DecodeIndexKeyWithoutTableIDIndexIDPrefix( - rf.currentTable.desc, - rf.currentTable.index, - rf.currentTable.keyValTypes, - rf.currentTable.keyVals, - rf.currentTable.indexColumnDirs, - key[rf.currentTable.knownPrefixLength:], - ) - } - - // Make a copy of the initial key for validating whether it's within - // the table's specified spans. - initialKey := key - - key, err = rf.codec.StripTenantPrefix(key) - if err != nil { - return nil, false, false, err - } - - // key now contains the bytes in the key (if match) that are not part - // of the signature in order. - tableIdx, key, match, err := rowenc.IndexKeyEquivSignature(key, rf.allEquivSignatures, rf.keySigBuf, rf.keyRestBuf) - if err != nil { - return nil, false, false, err - } - // The index key does not belong to our table because either: - // !match: part of the index key's signature did not match any of - // rf.allEquivSignatures. - // tableIdx == -1: index key belongs to an ancestor. - if !match || tableIdx == -1 { - return nil, false, false, nil - } - - // The index key is not within our specified span of keys for the - // particular table. - // TODO(richardwu): ContainsKey checks every span within spans. We - // can check that spans is ordered (or sort it) and memoize - // the last span we've checked for each table. We can pass in this - // information to ContainsKey as a hint for which span to start - // checking first. - if !rf.tables[tableIdx].spans.ContainsKey(initialKey) { - return nil, false, false, nil - } - - // Either a new table is encountered or the rowReadyTable differs from - // the currentTable (the rowReadyTable was outputted in the previous - // read). We transition the references. - if &rf.tables[tableIdx] != rf.currentTable || rf.rowReadyTable != rf.currentTable { - rf.rowReadyTable = rf.currentTable - rf.currentTable = &rf.tables[tableIdx] - - // rf.rowReadyTable is nil if this is the very first key. - // We want to ensure this does not differ from rf.currentTable - // to prevent another transition. - if rf.rowReadyTable == nil { - rf.rowReadyTable = rf.currentTable - } - } - - // We can simply decode all the column values we retrieved - // when processing the ind - // ex key. The column values are at the - // front of the key. - if key, foundNull, err = rowenc.DecodeKeyVals( - rf.currentTable.keyValTypes, - rf.currentTable.keyVals, - rf.currentTable.indexColumnDirs, - key, - ); err != nil { - return nil, false, false, err - } - - return key, true, foundNull, nil + return rowenc.DecodeIndexKeyWithoutTableIDIndexIDPrefix( + rf.table.desc, + rf.table.index, + rf.table.keyValTypes, + rf.table.keyVals, + rf.table.indexColumnDirs, + key[rf.table.knownPrefixLength:], + ) } // KeyToDesc implements the KeyToDescTranslator interface. The implementation is // used by ConvertFetchError. func (rf *Fetcher) KeyToDesc(key roachpb.Key) (catalog.TableDescriptor, bool) { - if rf.currentTable != nil && len(key) < rf.currentTable.knownPrefixLength { + if len(key) < rf.table.knownPrefixLength { return nil, false } if _, ok, _, err := rf.ReadIndexKey(key); !ok || err != nil { return nil, false } - return rf.currentTable.desc, true + return rf.table.desc, true } // processKV processes the given key/value, setting values in the row @@ -1032,7 +841,7 @@ func (rf *Fetcher) KeyToDesc(key roachpb.Key) (catalog.TableDescriptor, bool) { func (rf *Fetcher) processKV( ctx context.Context, kv roachpb.KeyValue, ) (prettyKey string, prettyValue string, err error) { - table := rf.currentTable + table := &rf.table if rf.traceKV { prettyKey = fmt.Sprintf( @@ -1361,7 +1170,7 @@ func (rf *Fetcher) NextRow( } if rf.isCheck { - rf.rowReadyTable.lastKV = rf.kv + rf.table.lastKV = rf.kv } rowDone, err := rf.NextKey(ctx) if err != nil { @@ -1369,7 +1178,7 @@ func (rf *Fetcher) NextRow( } if rowDone { err := rf.finalizeRow() - return rf.rowReadyTable.row, rf.rowReadyTable.desc, rf.rowReadyTable.index, err + return rf.table.row, rf.table.desc, rf.table.index, err } } } @@ -1393,22 +1202,22 @@ func (rf *Fetcher) NextRowDecoded( for i, encDatum := range row { if encDatum.IsUnset() { - rf.rowReadyTable.decodedRow[i] = tree.DNull + rf.table.decodedRow[i] = tree.DNull continue } - if err := encDatum.EnsureDecoded(rf.rowReadyTable.cols[i].GetType(), rf.alloc); err != nil { + if err := encDatum.EnsureDecoded(rf.table.cols[i].GetType(), rf.alloc); err != nil { return nil, nil, nil, err } - rf.rowReadyTable.decodedRow[i] = encDatum.Datum + rf.table.decodedRow[i] = encDatum.Datum } - return rf.rowReadyTable.decodedRow, table, index, nil + return rf.table.decodedRow, table, index, nil } // RowLastModified may only be called after NextRow has returned a non-nil row // and returns the timestamp of the last modification to that row. func (rf *Fetcher) RowLastModified() hlc.Timestamp { - return rf.rowReadyTable.rowLastModified + return rf.table.rowLastModified } // RowIsDeleted may only be called after NextRow has returned a non-nil row and @@ -1416,7 +1225,7 @@ func (rf *Fetcher) RowLastModified() hlc.Timestamp { // meaningful when the configured kvBatchFetcher returns deletion tombstones, which // the normal one (via `StartScan`) does not. func (rf *Fetcher) RowIsDeleted() bool { - return rf.rowReadyTable.rowIsDeleted + return rf.table.rowIsDeleted } // NextRowWithErrors calls NextRow to fetch the next row and also run @@ -1447,13 +1256,13 @@ func (rf *Fetcher) NextRowWithErrors(ctx context.Context) (rowenc.EncDatumRow, e // functions require that the table.row datums are decoded. for i := range row { if row[i].IsUnset() { - rf.rowReadyTable.decodedRow[i] = tree.DNull + rf.table.decodedRow[i] = tree.DNull continue } - if err := row[i].EnsureDecoded(rf.rowReadyTable.cols[i].GetType(), rf.alloc); err != nil { + if err := row[i].EnsureDecoded(rf.table.cols[i].GetType(), rf.alloc); err != nil { return nil, err } - rf.rowReadyTable.decodedRow[i] = row[i].Datum + rf.table.decodedRow[i] = row[i].Datum } if index.GetID() == table.GetPrimaryIndexID() { @@ -1474,7 +1283,7 @@ func (rf *Fetcher) NextRowWithErrors(ctx context.Context) (rowenc.EncDatumRow, e // on all values in the buffered row. This check is specific to primary // index datums. func (rf *Fetcher) checkPrimaryIndexDatumEncodings(ctx context.Context) error { - table := rf.rowReadyTable + table := &rf.table scratch := make([]byte, 1024) colIDToColumn := make(map[descpb.ColumnID]catalog.Column) for _, col := range table.desc.PublicColumns() { @@ -1533,7 +1342,7 @@ func (rf *Fetcher) checkPrimaryIndexDatumEncodings(ctx context.Context) error { // check on all values in the buffered row. This check is specific to // secondary index datums. func (rf *Fetcher) checkSecondaryIndexDatumEncodings(ctx context.Context) error { - table := rf.rowReadyTable + table := &rf.table colToEncDatum := make(map[descpb.ColumnID]rowenc.EncDatum, len(table.row)) values := make(tree.Datums, len(table.row)) for i, col := range table.cols { @@ -1551,14 +1360,14 @@ func (rf *Fetcher) checkSecondaryIndexDatumEncodings(ctx context.Context) error for _, indexEntry := range indexEntries { // We ignore the first 4 bytes of the values. These bytes are a // checksum which are not set by EncodeSecondaryIndex. - if !indexEntry.Key.Equal(rf.rowReadyTable.lastKV.Key) { + if !indexEntry.Key.Equal(rf.table.lastKV.Key) { return scrub.WrapError(scrub.IndexKeyDecodingError, errors.Errorf( "secondary index key failed to round-trip encode. expected %#v, got: %#v", - rf.rowReadyTable.lastKV.Key, indexEntry.Key)) + rf.table.lastKV.Key, indexEntry.Key)) } else if !indexEntry.Value.EqualTagAndData(table.lastKV.Value) { return scrub.WrapError(scrub.IndexValueDecodingError, errors.Errorf( "secondary index value failed to round-trip encode. expected %#v, got: %#v", - rf.rowReadyTable.lastKV.Value, indexEntry.Value)) + rf.table.lastKV.Value, indexEntry.Value)) } } return nil @@ -1568,11 +1377,11 @@ func (rf *Fetcher) checkSecondaryIndexDatumEncodings(ctx context.Context) error // have the same ordering as the encoded key. func (rf *Fetcher) checkKeyOrdering(ctx context.Context) error { defer func() { - rf.rowReadyTable.lastDatums = append(tree.Datums(nil), rf.rowReadyTable.decodedRow...) + rf.table.lastDatums = append(tree.Datums(nil), rf.table.decodedRow...) }() - if !rf.rowReadyTable.hasLast { - rf.rowReadyTable.hasLast = true + if !rf.table.hasLast { + rf.table.hasLast = true return nil } @@ -1581,11 +1390,11 @@ func (rf *Fetcher) checkKeyOrdering(ctx context.Context) error { // previous row in that column. When the first column with a differing value // is found, compare the values to ensure the ordering matches the column // ordering. - for i := 0; i < rf.rowReadyTable.index.NumKeyColumns(); i++ { - id := rf.rowReadyTable.index.GetKeyColumnID(i) - idx := rf.rowReadyTable.colIdxMap.GetDefault(id) - result := rf.rowReadyTable.decodedRow[idx].Compare(&evalCtx, rf.rowReadyTable.lastDatums[idx]) - expectedDirection := rf.rowReadyTable.index.GetKeyColumnDirection(i) + for i := 0; i < rf.table.index.NumKeyColumns(); i++ { + id := rf.table.index.GetKeyColumnID(i) + idx := rf.table.colIdxMap.GetDefault(id) + result := rf.table.decodedRow[idx].Compare(&evalCtx, rf.table.lastDatums[idx]) + expectedDirection := rf.table.index.GetKeyColumnDirection(i) if rf.reverse && expectedDirection == descpb.IndexDescriptor_ASC { expectedDirection = descpb.IndexDescriptor_DESC } else if rf.reverse && expectedDirection == descpb.IndexDescriptor_DESC { @@ -1608,7 +1417,7 @@ func (rf *Fetcher) checkKeyOrdering(ctx context.Context) error { } func (rf *Fetcher) finalizeRow() error { - table := rf.rowReadyTable + table := &rf.table // Fill in any system columns if requested. if table.timestampOutputIdx != noOutputColumn { @@ -1677,11 +1486,11 @@ func (rf *Fetcher) PartialKey(nCols int) (roachpb.Key, error) { return nil, nil } n, err := consumeIndexKeyWithoutTableIDIndexIDPrefix( - rf.currentTable.index, nCols, rf.kv.Key[rf.currentTable.knownPrefixLength:]) + rf.table.index, nCols, rf.kv.Key[rf.table.knownPrefixLength:]) if err != nil { return nil, err } - return rf.kv.Key[:n+rf.currentTable.knownPrefixLength], nil + return rf.kv.Key[:n+rf.table.knownPrefixLength], nil } // GetBytesRead returns total number of bytes read by the underlying KVFetcher. diff --git a/pkg/sql/row/fetcher_mvcc_test.go b/pkg/sql/row/fetcher_mvcc_test.go index c8e094f069f6..2fa422d42555 100644 --- a/pkg/sql/row/fetcher_mvcc_test.go +++ b/pkg/sql/row/fetcher_mvcc_test.go @@ -82,24 +82,20 @@ func TestRowFetcherMVCCMetadata(t *testing.T) { a STRING PRIMARY KEY, b STRING, c STRING, d STRING, FAMILY (a, b, c), FAMILY (d) )`) - parentDesc := catalogkv.TestingGetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, `d`, `parent`) - var args []row.FetcherTableArgs - for _, desc := range []catalog.TableDescriptor{parentDesc} { - var colIdxMap catalog.TableColMap - var valNeededForCol util.FastIntSet - for i, col := range desc.PublicColumns() { - colIdxMap.Set(col.GetID(), i) - valNeededForCol.Add(i) - } - args = append(args, row.FetcherTableArgs{ - Spans: desc.AllIndexSpans(keys.SystemSQLCodec), - Desc: desc, - Index: desc.GetPrimaryIndex(), - ColIdxMap: colIdxMap, - IsSecondaryIndex: false, - Cols: desc.PublicColumns(), - ValNeededForCol: valNeededForCol, - }) + desc := catalogkv.TestingGetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, `d`, `parent`) + var colIdxMap catalog.TableColMap + var valNeededForCol util.FastIntSet + for i, col := range desc.PublicColumns() { + colIdxMap.Set(col.GetID(), i) + valNeededForCol.Add(i) + } + table := row.FetcherTableArgs{ + Desc: desc, + Index: desc.GetPrimaryIndex(), + ColIdxMap: colIdxMap, + IsSecondaryIndex: false, + Cols: desc.PublicColumns(), + ValNeededForCol: valNeededForCol, } var rf row.Fetcher if err := rf.Init( @@ -112,7 +108,7 @@ func TestRowFetcherMVCCMetadata(t *testing.T) { true, /* isCheck */ &rowenc.DatumAlloc{}, nil, /* memMonitor */ - args..., + table, ); err != nil { t.Fatal(err) } diff --git a/pkg/sql/row/fetcher_test.go b/pkg/sql/row/fetcher_test.go index 1ec8e0e4d76e..09b5890e7b21 100644 --- a/pkg/sql/row/fetcher_test.go +++ b/pkg/sql/row/fetcher_test.go @@ -42,34 +42,27 @@ type initFetcherArgs struct { tableDesc catalog.TableDescriptor indexIdx int valNeededForCol util.FastIntSet - spans roachpb.Spans } -func makeFetcherArgs(entries []initFetcherArgs) []FetcherTableArgs { - fetcherArgs := make([]FetcherTableArgs, len(entries)) - - for i, entry := range entries { - index := entry.tableDesc.ActiveIndexes()[entry.indexIdx] - fetcherArgs[i] = FetcherTableArgs{ - Spans: entry.spans, - Desc: entry.tableDesc, - Index: index, - ColIdxMap: catalog.ColumnIDToOrdinalMap(entry.tableDesc.PublicColumns()), - IsSecondaryIndex: !index.Primary(), - Cols: entry.tableDesc.PublicColumns(), - ValNeededForCol: entry.valNeededForCol, - } +func makeFetcherArgs(entry initFetcherArgs) FetcherTableArgs { + index := entry.tableDesc.ActiveIndexes()[entry.indexIdx] + return FetcherTableArgs{ + Desc: entry.tableDesc, + Index: index, + ColIdxMap: catalog.ColumnIDToOrdinalMap(entry.tableDesc.PublicColumns()), + IsSecondaryIndex: !index.Primary(), + Cols: entry.tableDesc.PublicColumns(), + ValNeededForCol: entry.valNeededForCol, } - return fetcherArgs } func initFetcher( - entries []initFetcherArgs, reverseScan bool, alloc *rowenc.DatumAlloc, memMon *mon.BytesMonitor, + entry initFetcherArgs, reverseScan bool, alloc *rowenc.DatumAlloc, memMon *mon.BytesMonitor, ) (fetcher *Fetcher, err error) { fetcher = &Fetcher{} fetcherCodec := keys.SystemSQLCodec - fetcherArgs := makeFetcherArgs(entries) + fetcherArgs := makeFetcherArgs(entry) if err := fetcher.Init( context.Background(), @@ -81,7 +74,7 @@ func initFetcher( false, /* isCheck */ alloc, memMon, - fetcherArgs..., + fetcherArgs, ); err != nil { return nil, err } @@ -148,12 +141,10 @@ func TestNextRowSingle(t *testing.T) { var valNeededForCol util.FastIntSet valNeededForCol.AddRange(0, table.nCols-1) - args := []initFetcherArgs{ - { - tableDesc: tableDesc, - indexIdx: 0, - valNeededForCol: valNeededForCol, - }, + args := initFetcherArgs{ + tableDesc: tableDesc, + indexIdx: 0, + valNeededForCol: valNeededForCol, } rf, err := initFetcher(args, false /*reverseScan*/, alloc, nil /* memMon */) @@ -269,12 +260,10 @@ func TestNextRowBatchLimiting(t *testing.T) { var valNeededForCol util.FastIntSet valNeededForCol.AddRange(0, table.nCols-1) - args := []initFetcherArgs{ - { - tableDesc: tableDesc, - indexIdx: 0, - valNeededForCol: valNeededForCol, - }, + args := initFetcherArgs{ + tableDesc: tableDesc, + indexIdx: 0, + valNeededForCol: valNeededForCol, } rf, err := initFetcher(args, false /*reverseScan*/, alloc, nil /*memMon*/) @@ -369,12 +358,10 @@ func TestRowFetcherMemoryLimits(t *testing.T) { var valNeededForCol util.FastIntSet valNeededForCol.AddRange(0, 1) - args := []initFetcherArgs{ - { - tableDesc: tableDesc, - indexIdx: 0, - valNeededForCol: valNeededForCol, - }, + args := initFetcherArgs{ + tableDesc: tableDesc, + indexIdx: 0, + valNeededForCol: valNeededForCol, } alloc := &rowenc.DatumAlloc{} @@ -448,12 +435,10 @@ INDEX(c) var valNeededForCol util.FastIntSet valNeededForCol.AddRange(0, table.nCols-1) - args := []initFetcherArgs{ - { - tableDesc: tableDesc, - indexIdx: 0, - valNeededForCol: valNeededForCol, - }, + args := initFetcherArgs{ + tableDesc: tableDesc, + indexIdx: 0, + valNeededForCol: valNeededForCol, } rf, err := initFetcher(args, false /*reverseScan*/, alloc, nil /*memMon*/) @@ -628,13 +613,11 @@ func TestNextRowSecondaryIndex(t *testing.T) { var valNeededForCol util.FastIntSet valNeededForCol.AddRange(0, table.nVals-1) - args := []initFetcherArgs{ - { - tableDesc: tableDesc, - // We scan from the first secondary index. - indexIdx: 1, - valNeededForCol: valNeededForCol, - }, + args := initFetcherArgs{ + tableDesc: tableDesc, + // We scan from the first secondary index. + indexIdx: 1, + valNeededForCol: valNeededForCol, } rf, err := initFetcher(args, false /*reverseScan*/, alloc, nil /*memMon*/) @@ -756,12 +739,10 @@ func TestRowFetcherReset(t *testing.T) { tableDesc := catalogkv.TestingGetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "foo") var valNeededForCol util.FastIntSet valNeededForCol.AddRange(0, 1) - args := []initFetcherArgs{ - { - tableDesc: tableDesc, - indexIdx: 0, - valNeededForCol: valNeededForCol, - }, + args := initFetcherArgs{ + tableDesc: tableDesc, + indexIdx: 0, + valNeededForCol: valNeededForCol, } da := rowenc.DatumAlloc{} fetcher, err := initFetcher(args, false, &da, nil /*memMon*/) @@ -775,9 +756,6 @@ func TestRowFetcherReset(t *testing.T) { } resetFetcher.Reset() - if len(resetFetcher.tables) != 0 || cap(resetFetcher.tables) != 1 { - t.Fatal("Didn't find saved slice:", resetFetcher.tables) - } // Now re-init the reset fetcher and make sure its the same as the fetcher we // didn't reset. @@ -793,7 +771,7 @@ func TestRowFetcherReset(t *testing.T) { false, /* isCheck */ &da, nil, /* memMonitor */ - fetcherArgs..., + fetcherArgs, ); err != nil { t.Fatal(err) } From e855276e43c0242e98ba14240dd180c7857636a7 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Tue, 14 Sep 2021 20:03:59 -0400 Subject: [PATCH 031/205] colbuilder: remove the check for index join on the interleaved index Interleaved indexes are no more. Release note: None --- pkg/sql/colexec/colbuilder/execplan.go | 9 --------- pkg/sql/distsql_physical_planner.go | 3 --- pkg/sql/distsql_physical_planner_test.go | 24 +++--------------------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/pkg/sql/colexec/colbuilder/execplan.go b/pkg/sql/colexec/colbuilder/execplan.go index 8329518e2151..040c6ec43b65 100644 --- a/pkg/sql/colexec/colbuilder/execplan.go +++ b/pkg/sql/colexec/colbuilder/execplan.go @@ -181,14 +181,6 @@ func supportedNatively(spec *execinfrapb.ProcessorSpec) error { if spec.Core.JoinReader.LookupColumns != nil || !spec.Core.JoinReader.LookupExpr.Empty() { return errLookupJoinUnsupported } - for i := range spec.Core.JoinReader.Table.Indexes { - if spec.Core.JoinReader.Table.Indexes[i].IsInterleaved() { - // Interleaved indexes are going to be removed anyway, so there is no - // point in handling the extra complexity. Just let the row engine - // handle this. - return errInterleavedIndexJoin - } - } return nil case spec.Core.Filterer != nil: @@ -263,7 +255,6 @@ var ( errExperimentalWrappingProhibited = errors.New("wrapping for non-JoinReader and non-LocalPlanNode cores is prohibited in vectorize=experimental_always") errWrappedCast = errors.New("mismatched types in NewColOperator and unsupported casts") errLookupJoinUnsupported = errors.New("lookup join reader is unsupported in vectorized") - errInterleavedIndexJoin = errors.New("vectorized join reader is unsupported for interleaved indexes") ) func canWrap(mode sessiondatapb.VectorizeExecMode, spec *execinfrapb.ProcessorSpec) error { diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index 9d4619cadaf9..35012c50f4dd 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -3966,9 +3966,6 @@ func checkScanParallelizationIfLocal( } return true, nil case *indexJoinNode: - // Vectorized index join is only supported for non-interleaved - // tables. - prohibitParallelization = n.table.desc.TableDesc().PrimaryIndex.IsInterleaved() return true, nil case *joinNode: prohibitParallelization = n.pred.onCond != nil diff --git a/pkg/sql/distsql_physical_planner_test.go b/pkg/sql/distsql_physical_planner_test.go index 2b378ae521c2..26f64635ffd0 100644 --- a/pkg/sql/distsql_physical_planner_test.go +++ b/pkg/sql/distsql_physical_planner_test.go @@ -1316,19 +1316,10 @@ func TestCheckScanParallelizationIfLocal(t *testing.T) { ctx := context.Background() - makeTableDesc := func(interleaved bool) catalog.TableDescriptor { - var interleave descpb.InterleaveDescriptor - if interleaved { - interleave = descpb.InterleaveDescriptor{ - Ancestors: []descpb.InterleaveDescriptor_Ancestor{{}}, - } - } + makeTableDesc := func() catalog.TableDescriptor { tableDesc := descpb.TableDescriptor{ - PrimaryIndex: descpb.IndexDescriptor{ - Interleave: interleave, - }, + PrimaryIndex: descpb.IndexDescriptor{}, } - b := tabledesc.NewBuilder(&tableDesc) err := b.RunPostDeserializationChanges(ctx, nil /* DescGetter */) if err != nil { @@ -1386,19 +1377,10 @@ func TestCheckScanParallelizationIfLocal(t *testing.T) { { plan: planComponents{main: planMaybePhysical{planNode: &indexJoinNode{ input: scanToParallelize, - table: &scanNode{desc: makeTableDesc(false /* interleaved */)}, + table: &scanNode{desc: makeTableDesc()}, }}}, hasScanNodeToParallelize: true, }, - { - plan: planComponents{main: planMaybePhysical{planNode: &indexJoinNode{ - input: scanToParallelize, - table: &scanNode{desc: makeTableDesc(true /* interleaved */)}, - }}}, - // Vectorized index join is only supported for non-interleaved - // tables. - prohibitParallelization: true, - }, { plan: planComponents{main: planMaybePhysical{planNode: &limitNode{plan: scanToParallelize}}}, hasScanNodeToParallelize: true, From 948568c9348ddb7200e1be857c907df3a5c91614 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Tue, 14 Sep 2021 20:34:51 -0400 Subject: [PATCH 032/205] sql: remove the interleaved logic from fetchers This commit removes the remaining bits of handling the interleaved tables from both row fetcher and cFetcher. It also removes a test that verifies that we can read from the interleaved table restored from a backup. Release note: None --- pkg/ccl/backupccl/BUILD.bazel | 1 - .../backupccl/restore_old_versions_test.go | 78 ----- pkg/sql/colencoding/BUILD.bazel | 1 - pkg/sql/colencoding/key_encoding.go | 124 -------- pkg/sql/colfetcher/cfetcher.go | 106 +------ pkg/sql/colfetcher/fetcherstate_string.go | 13 +- pkg/sql/row/fetcher.go | 286 +++++++----------- 7 files changed, 116 insertions(+), 493 deletions(-) diff --git a/pkg/ccl/backupccl/BUILD.bazel b/pkg/ccl/backupccl/BUILD.bazel index ddaf38f08ec1..7692afaaa7a9 100644 --- a/pkg/ccl/backupccl/BUILD.bazel +++ b/pkg/ccl/backupccl/BUILD.bazel @@ -167,7 +167,6 @@ go_test( "//pkg/cloud/azure", "//pkg/cloud/impl:cloudimpl", "//pkg/cloud/nodelocal", - "//pkg/clusterversion", "//pkg/config", "//pkg/config/zonepb", "//pkg/jobs", diff --git a/pkg/ccl/backupccl/restore_old_versions_test.go b/pkg/ccl/backupccl/restore_old_versions_test.go index 88a4cc2fc206..2322fc866239 100644 --- a/pkg/ccl/backupccl/restore_old_versions_test.go +++ b/pkg/ccl/backupccl/restore_old_versions_test.go @@ -19,10 +19,8 @@ import ( "time" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/server" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" @@ -69,21 +67,9 @@ func TestRestoreOldVersions(t *testing.T) { clusterDirs = testdataBase + "/cluster" exceptionalDirs = testdataBase + "/exceptional" privilegeDirs = testdataBase + "/privileges" - interleavedDirs = testdataBase + "/interleaved" multiRegionDirs = testdataBase + "/multi-region" ) - t.Run("interleaved", func(t *testing.T) { - dirs, err := ioutil.ReadDir(interleavedDirs) - require.NoError(t, err) - for _, dir := range dirs { - require.True(t, dir.IsDir()) - interleavedDirs, err := filepath.Abs(filepath.Join(interleavedDirs, dir.Name())) - require.NoError(t, err) - t.Run(dir.Name(), restoreInterleavedTest(interleavedDirs)) - } - }) - t.Run("table-restore", func(t *testing.T) { dirs, err := ioutil.ReadDir(exportDirsWithoutInterleave) require.NoError(t, err) @@ -254,70 +240,6 @@ ORDER BY object_type, object_name`, [][]string{ }) } -func restoreInterleavedTest(exportDir string) func(t *testing.T) { - return func(t *testing.T) { - params := base.TestServerArgs{ - Knobs: base.TestingKnobs{ - Server: &server.TestingKnobs{ - DisableAutomaticVersionUpgrade: 1, - BinaryVersionOverride: clusterversion.ByKey(clusterversion.PreventNewInterleavedTables - 1), - }, - }, - } - const numAccounts = 1000 - _, _, sqlDB, dir, cleanup := backupRestoreTestSetupWithParams(t, singleNode, numAccounts, - InitManualReplication, base.TestClusterArgs{ServerArgs: params}) - defer cleanup() - err := os.Symlink(exportDir, filepath.Join(dir, "foo")) - require.NoError(t, err) - sqlDB.Exec(t, `CREATE DATABASE test`) - var unused string - var importedRows int - sqlDB.QueryRow(t, `RESTORE test.* FROM $1`, LocalFoo).Scan( - &unused, &unused, &unused, &importedRows, &unused, &unused, - ) - // No rows in the imported table. - const totalRows = 4 - if importedRows != totalRows { - t.Fatalf("expected %d rows, got %d", totalRows, importedRows) - } - // Validate an empty result set is generated. - orderResults := [][]string{ - {"1", "1", "20.00000"}, - {"2", "2", "30.00000"}, - } - customerResults := [][]string{ - {"1", "BOB"}, - {"2", "BILL"}, - } - sqlDB.CheckQueryResults(t, `SELECT * FROM test.orders`, orderResults) - sqlDB.CheckQueryResults(t, `SELECT * FROM test.customers`, customerResults) - // Result of show tables. - showTableResult := [][]string{ - {"test.public.orders", - "CREATE TABLE public.orders (\n\tcustomer INT8 NOT NULL,\n\tid INT8 NOT NULL,\n\ttotal DECIMAL(20,5) NULL,\n\tCONSTRAINT \"primary\" PRIMARY KEY (customer ASC, id ASC),\n\tCONSTRAINT fk_customer FOREIGN KEY (customer) REFERENCES public.customers(id),\n\tFAMILY \"primary\" (customer, id, total)\n) INTERLEAVE IN PARENT public.customers (customer)"}, - } - sqlDB.CheckQueryResults(t, "SHOW CREATE TABLE test.orders", showTableResult) - // We should still have interleaved tables. - interleavedResult := [][]string{ - {"test", "public", "orders", "primary", "test", "public", "customers"}} - sqlDB.ExecSucceedsSoon(t, "SET DATABASE = test") - sqlDB.CheckQueryResultsRetry(t, "SELECT * FROM crdb_internal.interleaved", interleavedResult) - // Next move to a version that blocks restores. - sqlDB.ExecSucceedsSoon(t, - "SET CLUSTER SETTING version = $1", - clusterversion.ByKey(clusterversion.PreventNewInterleavedTables).String()) - // Clean up the old database. - sqlDB.ExecSucceedsSoon(t, "SET DATABASE = defaultdb") - sqlDB.ExecSucceedsSoon(t, "set sql_safe_updates=false") - sqlDB.ExecSucceedsSoon(t, "DROP DATABASE TEST") - // Restore should now fail. - sqlDB.ExpectErr(t, - "pq: restoring interleaved tables is no longer allowed. table customers was found to be interleaved", - `RESTORE test.* FROM $1`, LocalFoo) - } -} - func restoreOldVersionTestWithInterleave(exportDir string) func(t *testing.T) { return func(t *testing.T) { params := base.TestServerArgs{} diff --git a/pkg/sql/colencoding/BUILD.bazel b/pkg/sql/colencoding/BUILD.bazel index b7683b766d87..f5f0f4903499 100644 --- a/pkg/sql/colencoding/BUILD.bazel +++ b/pkg/sql/colencoding/BUILD.bazel @@ -11,7 +11,6 @@ go_library( deps = [ "//pkg/col/coldata", "//pkg/roachpb:with-mocks", - "//pkg/sql/catalog", "//pkg/sql/catalog/descpb", "//pkg/sql/rowenc", "//pkg/sql/sem/tree", diff --git a/pkg/sql/colencoding/key_encoding.go b/pkg/sql/colencoding/key_encoding.go index 9339b90c4ae7..e64674b1c805 100644 --- a/pkg/sql/colencoding/key_encoding.go +++ b/pkg/sql/colencoding/key_encoding.go @@ -16,7 +16,6 @@ import ( "github.com/cockroachdb/apd/v2" "github.com/cockroachdb/cockroach/pkg/col/coldata" "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" @@ -28,125 +27,6 @@ import ( "github.com/cockroachdb/errors" ) -// DecodeIndexKeyToCols decodes an index key into the idx'th position of the -// provided slices of colexec.Vecs. The input index key must already have its -// tenant id and first table id / index id prefix removed. If matches is false, -// the key is from a different table, and the returned remainingKey indicates a -// "seek prefix": the next key that might be part of the table being searched -// for. See the analog in sqlbase/index_encoding.go. -// -// Sometimes it is necessary to determine if the value of a column is NULL even -// though the value itself is not needed. If checkAllColsForNull is true, then -// foundNull=true will be returned if any columns in the key are NULL, -// regardless of whether or not indexColIdx indicates that the column should be -// decoded. -func DecodeIndexKeyToCols( - da *rowenc.DatumAlloc, - vecs []coldata.Vec, - idx int, - desc catalog.TableDescriptor, - index catalog.Index, - indexColIdx []int, - checkAllColsForNull bool, - types []*types.T, - colDirs []descpb.IndexDescriptor_Direction, - key roachpb.Key, - invertedColIdx int, - scratch []byte, -) (remainingKey roachpb.Key, matches bool, foundNull bool, retScratch []byte, _ error) { - var decodedTableID descpb.ID - var decodedIndexID descpb.IndexID - var err error - - origKey := key - - if index.NumInterleaveAncestors() > 0 { - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - // Our input key had its first table id / index id chopped off, so - // don't try to decode those for the first ancestor. - if i != 0 { - lastKeyComponentLength := len(key) - key, decodedTableID, decodedIndexID, err = rowenc.DecodePartialTableIDIndexID(key) - if err != nil { - return nil, false, false, scratch, err - } - if decodedTableID != ancestor.TableID || decodedIndexID != ancestor.IndexID { - // We don't match. Return a key with the table ID / index ID we're - // searching for, so the caller knows what to seek to. - curPos := len(origKey) - lastKeyComponentLength - // Prevent unwanted aliasing on the origKey by setting the capacity. - key = rowenc.EncodePartialTableIDIndexID(origKey[:curPos:curPos], ancestor.TableID, ancestor.IndexID) - return key, false, false, scratch, nil - } - } - - length := int(ancestor.SharedPrefixLen) - // We don't care about whether this call to DecodeKeyVals found a null or not, because - // it is a interleaving ancestor. - var isNull bool - key, isNull, scratch, err = DecodeKeyValsToCols( - da, vecs, idx, indexColIdx[:length], checkAllColsForNull, types[:length], - colDirs[:length], nil /* unseen */, key, invertedColIdx, scratch, - ) - if err != nil { - return nil, false, false, scratch, err - } - indexColIdx, types, colDirs = indexColIdx[length:], types[length:], colDirs[length:] - foundNull = foundNull || isNull - - // Consume the interleaved sentinel. - var ok bool - key, ok = encoding.DecodeIfInterleavedSentinel(key) - if !ok { - // We're expecting an interleaved sentinel but didn't find one. Append - // one so the caller can seek to it. - curPos := len(origKey) - len(key) - // Prevent unwanted aliasing on the origKey by setting the capacity. - key = encoding.EncodeInterleavedSentinel(origKey[:curPos:curPos]) - return key, false, false, scratch, nil - } - } - - lastKeyComponentLength := len(key) - key, decodedTableID, decodedIndexID, err = rowenc.DecodePartialTableIDIndexID(key) - if err != nil { - return nil, false, false, scratch, err - } - if decodedTableID != desc.GetID() || decodedIndexID != index.GetID() { - // We don't match. Return a key with the table ID / index ID we're - // searching for, so the caller knows what to seek to. - curPos := len(origKey) - lastKeyComponentLength - // Prevent unwanted aliasing on the origKey by setting the capacity. - key = rowenc.EncodePartialTableIDIndexID(origKey[:curPos:curPos], desc.GetID(), index.GetID()) - return key, false, false, scratch, nil - } - } - - var isNull bool - key, isNull, scratch, err = DecodeKeyValsToCols( - da, vecs, idx, indexColIdx, checkAllColsForNull, types, colDirs, - nil /* unseen */, key, invertedColIdx, scratch, - ) - if err != nil { - return nil, false, false, scratch, err - } - foundNull = foundNull || isNull - - // We're expecting a column family id next (a varint). If - // interleavedSentinel is actually next, then this key is for a child - // table. - lastKeyComponentLength := len(key) - if _, ok := encoding.DecodeIfInterleavedSentinel(key); ok { - curPos := len(origKey) - lastKeyComponentLength - // Prevent unwanted aliasing on the origKey by setting the capacity. - key = encoding.EncodeNullDescending(origKey[:curPos:curPos]) - return key, false, false, scratch, nil - } - - return key, true, foundNull, scratch, nil -} - // DecodeKeyValsToCols decodes the values that are part of the key, writing the // result to the idx'th slot of the input slice of coldata.Vecs. // @@ -223,10 +103,6 @@ func decodeTableKeyToCol( vec.Nulls().SetNull(idx) return key, true, scratch, nil } - // We might have read a NULL value in the interleaved child table which - // would update the nulls vector, so we need to explicitly unset the null - // value here. - vec.Nulls().UnsetNull(idx) // Inverted columns should not be decoded, but should instead be // passed on as a DBytes datum. diff --git a/pkg/sql/colfetcher/cfetcher.go b/pkg/sql/colfetcher/cfetcher.go index a1b3d577bf7f..c80651b31459 100644 --- a/pkg/sql/colfetcher/cfetcher.go +++ b/pkg/sql/colfetcher/cfetcher.go @@ -268,9 +268,8 @@ type cFetcher struct { // firstBatchLimit. maxKeysPerRow int - // True if the index key must be decoded. - // This is only false if there are no needed columns, the table has no - // interleave children, and the tracing is not enabled. + // True if the index key must be decoded. This is only false if there are no + // needed columns and the tracing is not enabled. mustDecodeIndexKey bool // mvccDecodeStrategy controls whether or not MVCC timestamps should @@ -292,8 +291,6 @@ type cFetcher struct { rowIdx int // nextKV is the kv to process next. nextKV roachpb.KeyValue - // seekPrefix is the prefix to seek to in stateSeekPrefix. - seekPrefix roachpb.Key // limitHint is a hint as to the number of rows that the caller expects // to be returned from this fetch. It will be decremented whenever a @@ -547,15 +544,6 @@ func (rf *cFetcher) Init( } } - // - If there are interleaves, we need to read the index key in order to - // determine whether this row is actually part of the index we're scanning. - // - If there are needed columns from the index key, we need to read it. - // - // Otherwise, we can completely avoid decoding the index key. - if table.index.NumInterleavedBy() > 0 || table.index.NumInterleaveAncestors() > 0 { - rf.mustDecodeIndexKey = true - } - if table.isSecondaryIndex { colIDs := table.index.CollectKeyColumnIDs() colIDs.UnionWith(table.index.CollectSecondaryStoredColumnIDs()) @@ -725,27 +713,13 @@ const ( // set. // 1. skip common prefix // 2. parse key (past common prefix) into row buffer, setting last row prefix buffer - // 3. interleave detected? - // - set skip prefix - // -> seekPrefix(decodeFirstKVOfRow) - // 4. parse value into row buffer. - // 5. 1-cf or secondary index? + // 3. parse value into row buffer. + // 4. 1-cf or secondary index? // -> doneRow(initFetch) // else: // -> fetchNextKVWithUnfinishedRow stateDecodeFirstKVOfRow - // stateSeekPrefix is the state of skipping all keys that sort before - // (or after, in the case of a reverse scan) a prefix. s.machine.seekPrefix - // must be set to the prefix to seek to. state[1] must be set, and seekPrefix - // will transition to that state once it finds the first key with that prefix. - // 1. fetch next kv into nextKV buffer - // 2. kv doesn't match seek prefix? - // -> seekPrefix - // else: - // -> nextState - stateSeekPrefix - // stateFetchNextKVWithUnfinishedRow is the state of getting a new key for // the current row. The machine will read a new key from the underlying // fetcher, process it, and either add the results to the current row, or @@ -756,9 +730,6 @@ const ( // 4. no? // -> finalizeRow(decodeFirstKVOfRow) // 5. skip to end of last row prefix buffer - // 6. interleave detected? - // - set skip prefix - // -> finalizeRow(seekPrefix(decodeFirstKVOfRow)) // 6. parse value into row buffer // 7. -> fetchNextKVWithUnfinishedRow stateFetchNextKVWithUnfinishedRow @@ -876,25 +847,23 @@ func (rf *cFetcher) NextBatch(ctx context.Context) (coldata.Batch, error) { log.Infof(ctx, "decoding first key %s", rf.machine.nextKV.Key) } var ( - key []byte - matches bool - err error + key []byte + err error ) // For unique secondary indexes on tables with multiple column // families, we must check all columns for NULL values in order // to determine whether a KV belongs to the same row as the // previous KV or a different row. checkAllColsForNull := rf.table.isSecondaryIndex && rf.table.index.IsUnique() && rf.table.desc.NumFamilies() != 1 - key, matches, foundNull, rf.scratch, err = colencoding.DecodeIndexKeyToCols( + key, foundNull, rf.scratch, err = colencoding.DecodeKeyValsToCols( &rf.table.da, rf.machine.colvecs, rf.machine.rowIdx, - rf.table.desc, - rf.table.index, rf.table.indexColOrdinals, checkAllColsForNull, rf.table.keyValTypes, rf.table.indexColumnDirs, + nil, /* unseen */ rf.machine.nextKV.Key[rf.table.knownPrefixLength:], rf.table.invertedColOrdinal, rf.scratch, @@ -902,23 +871,9 @@ func (rf *cFetcher) NextBatch(ctx context.Context) (coldata.Batch, error) { if err != nil { return nil, err } - if !matches { - // We found an interleave. Set our skip prefix. - seekPrefix := rf.machine.nextKV.Key[:len(key)+rf.table.knownPrefixLength] - if debugState { - log.Infof(ctx, "setting seek prefix to %s", seekPrefix) - } - rf.machine.seekPrefix = seekPrefix - rf.machine.state[0] = stateSeekPrefix - rf.machine.state[1] = stateDecodeFirstKVOfRow - continue - } prefix := rf.machine.nextKV.Key[:len(rf.machine.nextKV.Key)-len(key)] rf.machine.lastRowPrefix = prefix } else { - // If mustDecodeIndexKey was false, we can't possibly have an - // interleaved row on our hands, so we can figure out our row prefix - // without parsing any keys by using GetRowPrefixLength. prefixLen, err := keys.GetRowPrefixLength(rf.machine.nextKV.Key) if err != nil { return nil, err @@ -993,39 +948,6 @@ func (rf *cFetcher) NextBatch(ctx context.Context) (coldata.Batch, error) { // If the table has more than one column family, then the next KV // may belong to the same row as the current KV. rf.machine.state[0] = stateFetchNextKVWithUnfinishedRow - case stateSeekPrefix: - // Note: seekPrefix is only used for interleaved tables. - for { - moreKVs, kv, finalReferenceToBatch, err := rf.fetcher.NextKV(ctx, rf.mvccDecodeStrategy) - if err != nil { - return nil, rf.convertFetchError(ctx, err) - } - if debugState { - log.Infof(ctx, "found kv %s, seeking to prefix %s", kv.Key, rf.machine.seekPrefix) - } - if !moreKVs { - // We ran out of data, so ignore whatever our next state was going to - // be and emit the final batch. - rf.machine.state[1] = stateEmitLastBatch - break - } - // The order we perform the comparison in depends on whether we are - // performing a reverse scan or not. If we are performing a reverse - // scan, then we want to seek until we find a key less than seekPrefix. - var comparison int - if rf.reverse { - comparison = bytes.Compare(rf.machine.seekPrefix, kv.Key) - } else { - comparison = bytes.Compare(kv.Key, rf.machine.seekPrefix) - } - // TODO(jordan): if nextKV returns newSpan = true, set the new span - // prefix and indicate that it needs decoding. - if comparison >= 0 { - rf.setNextKV(kv, finalReferenceToBatch) - break - } - } - rf.shiftState() case stateFetchNextKVWithUnfinishedRow: moreKVs, kv, finalReferenceToBatch, err := rf.fetcher.NextKV(ctx, rf.mvccDecodeStrategy) @@ -1054,18 +976,6 @@ func (rf *cFetcher) NextBatch(ctx context.Context) (coldata.Batch, error) { continue } - key := kv.Key[len(rf.machine.lastRowPrefix):] - _, foundInterleave := encoding.DecodeIfInterleavedSentinel(key) - - if foundInterleave { - // The key we just found isn't relevant to the current row, so finalize - // the current row, then skip all KVs with the current interleave prefix. - rf.machine.state[0] = stateFinalizeRow - rf.machine.state[1] = stateSeekPrefix - rf.machine.state[2] = stateDecodeFirstKVOfRow - continue - } - familyID, err := rf.getCurrentColumnFamilyID() if err != nil { return nil, err diff --git a/pkg/sql/colfetcher/fetcherstate_string.go b/pkg/sql/colfetcher/fetcherstate_string.go index 7ed041ee4496..63c72b198f7b 100644 --- a/pkg/sql/colfetcher/fetcherstate_string.go +++ b/pkg/sql/colfetcher/fetcherstate_string.go @@ -12,16 +12,15 @@ func _() { _ = x[stateInitFetch-1] _ = x[stateResetBatch-2] _ = x[stateDecodeFirstKVOfRow-3] - _ = x[stateSeekPrefix-4] - _ = x[stateFetchNextKVWithUnfinishedRow-5] - _ = x[stateFinalizeRow-6] - _ = x[stateEmitLastBatch-7] - _ = x[stateFinished-8] + _ = x[stateFetchNextKVWithUnfinishedRow-4] + _ = x[stateFinalizeRow-5] + _ = x[stateEmitLastBatch-6] + _ = x[stateFinished-7] } -const _fetcherState_name = "stateInvalidstateInitFetchstateResetBatchstateDecodeFirstKVOfRowstateSeekPrefixstateFetchNextKVWithUnfinishedRowstateFinalizeRowstateEmitLastBatchstateFinished" +const _fetcherState_name = "stateInvalidstateInitFetchstateResetBatchstateDecodeFirstKVOfRowstateFetchNextKVWithUnfinishedRowstateFinalizeRowstateEmitLastBatchstateFinished" -var _fetcherState_index = [...]uint8{0, 12, 26, 41, 64, 79, 112, 128, 146, 159} +var _fetcherState_index = [...]uint8{0, 12, 26, 41, 64, 97, 113, 131, 144} func (i fetcherState) String() string { if i < 0 || i >= fetcherState(len(_fetcherState_index)-1) { diff --git a/pkg/sql/row/fetcher.go b/pkg/sql/row/fetcher.go index a716d91bbfb9..c0e7fc2004a4 100644 --- a/pkg/sql/row/fetcher.go +++ b/pkg/sql/row/fetcher.go @@ -208,10 +208,8 @@ type Fetcher struct { // calculate the kvBatchFetcher's firstBatchLimit. numKeysPerRow int - // True if the index key must be decoded. - // If there is more than one table, the index key must always be decoded. - // This is only false if there are no needed columns and the (single) - // table has no interleave children. + // True if the index key must be decoded. This is only false if there are no + // needed columns. mustDecodeIndexKey bool // lockStrength represents the row-level locking mode to use when fetching @@ -399,16 +397,9 @@ func (rf *Fetcher) Init( } } - // - If there is more than one table, we have to decode the index key to - // figure out which table the row belongs to. - // - If there are interleaves, we need to read the index key in order to - // determine whether this row is actually part of the index we're scanning. - // - If there are needed columns from the index key, we need to read it. - // - // Otherwise, we can completely avoid decoding the index key. - if !rf.mustDecodeIndexKey && (neededIndexCols > 0 || table.index.NumInterleavedBy() > 0 || table.index.NumInterleaveAncestors() > 0) { - rf.mustDecodeIndexKey = true - } + // If there are needed columns from the index key, we need to read it; + // otherwise, we can completely avoid decoding the index key. + rf.mustDecodeIndexKey = neededIndexCols > 0 // The number of columns we need to read from the value part of the key. // It's the total number of needed columns minus the ones we read from the @@ -670,125 +661,107 @@ func (rf *Fetcher) setNextKV(kv roachpb.KeyValue, needsCopy bool) { // NextKey retrieves the next key/value and sets kv/kvEnd. Returns whether a row // has been completed. -func (rf *Fetcher) NextKey(ctx context.Context) (rowDone bool, err error) { - var moreKVs bool +func (rf *Fetcher) NextKey(ctx context.Context) (rowDone bool, _ error) { + moreKVs, kv, finalReferenceToBatch, err := rf.kvFetcher.NextKV(ctx, rf.mvccDecodeStrategy) + if err != nil { + return false, ConvertFetchError(ctx, rf, err) + } + rf.setNextKV(kv, finalReferenceToBatch) - for { - var finalReferenceToBatch bool - var kv roachpb.KeyValue - moreKVs, kv, finalReferenceToBatch, err = rf.kvFetcher.NextKV(ctx, rf.mvccDecodeStrategy) + rf.kvEnd = !moreKVs + if rf.kvEnd { + // No more keys in the scan. + // + // NB: this assumes that the KV layer will never split a range + // between column families, which is a brittle assumption. + // See: + // https://github.com/cockroachdb/cockroach/pull/42056 + return true, nil + } + + // foundNull is set when decoding a new index key for a row finds a NULL value + // in the index key. This is used when decoding unique secondary indexes in order + // to tell whether they have extra columns appended to the key. + var foundNull bool + + // unchangedPrefix will be set to true if we can skip decoding the index key + // completely, because the last key we saw has identical prefix to the + // current key. + // + // See Init() for a detailed description of when we can get away with not + // reading the index key. + unchangedPrefix := rf.indexKey != nil && bytes.HasPrefix(rf.kv.Key, rf.indexKey) + if unchangedPrefix { + // Skip decoding! + rf.keyRemainingBytes = rf.kv.Key[len(rf.indexKey):] + } else if rf.mustDecodeIndexKey || rf.traceKV { + rf.keyRemainingBytes, moreKVs, foundNull, err = rf.ReadIndexKey(rf.kv.Key) if err != nil { - return false, ConvertFetchError(ctx, rf, err) + return false, err } - rf.setNextKV(kv, finalReferenceToBatch) - - rf.kvEnd = !moreKVs - if rf.kvEnd { - // No more keys in the scan. - // - // NB: this assumes that the KV layer will never split a range - // between column families, which is a brittle assumption. - // See: - // https://github.com/cockroachdb/cockroach/pull/42056 - return true, nil + if !moreKVs { + return false, errors.AssertionFailedf("key did not match any of the table descriptors") } - - // foundNull is set when decoding a new index key for a row finds a NULL value - // in the index key. This is used when decoding unique secondary indexes in order - // to tell whether they have extra columns appended to the key. - var foundNull bool - - // unchangedPrefix will be set to true if we can skip decoding the index key - // completely, because the last key we saw has identical prefix to the - // current key. - unchangedPrefix := rf.indexKey != nil && bytes.HasPrefix(rf.kv.Key, rf.indexKey) - if unchangedPrefix { - keySuffix := rf.kv.Key[len(rf.indexKey):] - if _, foundSentinel := encoding.DecodeIfInterleavedSentinel(keySuffix); foundSentinel { - // We found an interleaved sentinel, which means that the key we just - // found belongs to a different interleave. That means we have to go - // through with index key decoding. - unchangedPrefix = false - } else { - rf.keyRemainingBytes = keySuffix - } + } else { + // We still need to consume the key until the family + // id, so processKV can know whether we've finished a + // row or not. + prefixLen, err := keys.GetRowPrefixLength(rf.kv.Key) + if err != nil { + return false, err } - // See Init() for a detailed description of when we can get away with not - // reading the index key. - if unchangedPrefix { - // Skip decoding! - } else if rf.mustDecodeIndexKey || rf.traceKV { - rf.keyRemainingBytes, moreKVs, foundNull, err = rf.ReadIndexKey(rf.kv.Key) - if err != nil { - return false, err - } - if !moreKVs { - // The key did not match any of the table - // descriptors, which means it's interleaved - // data from some other table or index. - continue - } - } else { - // We still need to consume the key until the family - // id, so processKV can know whether we've finished a - // row or not. - prefixLen, err := keys.GetRowPrefixLength(rf.kv.Key) - if err != nil { - return false, err - } - rf.keyRemainingBytes = rf.kv.Key[prefixLen:] - } + rf.keyRemainingBytes = rf.kv.Key[prefixLen:] + } - // For unique secondary indexes, the index-key does not distinguish one row - // from the next if both rows contain identical values along with a NULL. - // Consider the keys: - // - // /test/unique_idx/NULL/0 - // /test/unique_idx/NULL/1 - // - // The index-key extracted from the above keys is /test/unique_idx/NULL. The - // trailing /0 and /1 are the primary key used to unique-ify the keys when a - // NULL is present. When a null is present in the index key, we cut off more - // of the index key so that the prefix includes the primary key columns. - // - // Note that we do not need to do this for non-unique secondary indexes because - // the extra columns in the primary key will _always_ be there, so we can decode - // them when processing the index. The difference with unique secondary indexes - // is that the extra columns are not always there, and are used to unique-ify - // the index key, rather than provide the primary key column values. - if foundNull && rf.table.isSecondaryIndex && rf.table.index.IsUnique() && len(rf.table.desc.GetFamilies()) != 1 { - for i := 0; i < rf.table.index.NumKeySuffixColumns(); i++ { - var err error - // Slice off an extra encoded column from rf.keyRemainingBytes. - rf.keyRemainingBytes, err = rowenc.SkipTableKey(rf.keyRemainingBytes) - if err != nil { - return false, err - } + // For unique secondary indexes, the index-key does not distinguish one row + // from the next if both rows contain identical values along with a NULL. + // Consider the keys: + // + // /test/unique_idx/NULL/0 + // /test/unique_idx/NULL/1 + // + // The index-key extracted from the above keys is /test/unique_idx/NULL. The + // trailing /0 and /1 are the primary key used to unique-ify the keys when a + // NULL is present. When a null is present in the index key, we cut off more + // of the index key so that the prefix includes the primary key columns. + // + // Note that we do not need to do this for non-unique secondary indexes because + // the extra columns in the primary key will _always_ be there, so we can decode + // them when processing the index. The difference with unique secondary indexes + // is that the extra columns are not always there, and are used to unique-ify + // the index key, rather than provide the primary key column values. + if foundNull && rf.table.isSecondaryIndex && rf.table.index.IsUnique() && len(rf.table.desc.GetFamilies()) != 1 { + for i := 0; i < rf.table.index.NumKeySuffixColumns(); i++ { + var err error + // Slice off an extra encoded column from rf.keyRemainingBytes. + rf.keyRemainingBytes, err = rowenc.SkipTableKey(rf.keyRemainingBytes) + if err != nil { + return false, err } } + } - switch { - case len(rf.table.desc.GetFamilies()) == 1: - // If we only have one family, we know that there is only 1 k/v pair per row. - rowDone = true - case !unchangedPrefix: - // If the prefix of the key has changed, current key is from a different - // row than the previous one. - rowDone = true - default: - rowDone = false - } - - if rf.indexKey != nil && rowDone { - // The current key belongs to a new row. Output the - // current row. - rf.indexKey = nil - return true, nil - } + switch { + case len(rf.table.desc.GetFamilies()) == 1: + // If we only have one family, we know that there is only 1 k/v pair per row. + rowDone = true + case !unchangedPrefix: + // If the prefix of the key has changed, current key is from a different + // row than the previous one. + rowDone = true + default: + rowDone = false + } - return false, nil + if rf.indexKey != nil && rowDone { + // The current key belongs to a new row. Output the + // current row. + rf.indexKey = nil + return true, nil } + + return false, nil } func (rf *Fetcher) prettyEncDatums(types []*types.T, vals []rowenc.EncDatum) string { @@ -1485,12 +1458,15 @@ func (rf *Fetcher) PartialKey(nCols int) (roachpb.Key, error) { if rf.kv.Key == nil { return nil, nil } - n, err := consumeIndexKeyWithoutTableIDIndexIDPrefix( - rf.table.index, nCols, rf.kv.Key[rf.table.knownPrefixLength:]) - if err != nil { - return nil, err + partialKeyLength := rf.table.knownPrefixLength + for consumedCols := 0; consumedCols < nCols; consumedCols++ { + l, err := encoding.PeekLength(rf.kv.Key[partialKeyLength:]) + if err != nil { + return nil, err + } + partialKeyLength += l } - return rf.kv.Key[:n+rf.table.knownPrefixLength], nil + return rf.kv.Key[:partialKeyLength], nil } // GetBytesRead returns total number of bytes read by the underlying KVFetcher. @@ -1503,61 +1479,3 @@ func (rf *Fetcher) GetBytesRead() int64 { func hasExtraCols(table *tableInfo) bool { return table.isSecondaryIndex && table.index.IsUnique() } - -// consumeIndexKeyWithoutTableIDIndexIDPrefix consumes an index key that's -// already pre-stripped of its table ID index ID prefix, up to nCols columns, -// returning the number of bytes consumed. For example, given an input key -// with values (6,7,8,9) such as /Table/60/1/6/7/#/61/1/8/9, stripping 3 columns -// from this key would eat all but the final, 4th column 9 in this example, -// producing /Table/60/1/6/7/#/61/1/8. If nCols was 2, instead, the result -// would include the trailing table ID index ID pair, since that's a more -// precise key: /Table/60/1/6/7/#/61/1. -func consumeIndexKeyWithoutTableIDIndexIDPrefix( - index catalog.Index, nCols int, key []byte, -) (int, error) { - origKeyLen := len(key) - consumedCols := 0 - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - length := int(ancestor.SharedPrefixLen) - // Skip up to length values. - for j := 0; j < length; j++ { - if consumedCols == nCols { - // We're done early, in the middle of an interleave. - return origKeyLen - len(key), nil - } - l, err := encoding.PeekLength(key) - if err != nil { - return 0, err - } - key = key[l:] - consumedCols++ - } - var ok bool - key, ok = encoding.DecodeIfInterleavedSentinel(key) - if !ok { - return 0, errors.New("unexpected lack of sentinel key") - } - - // Skip the TableID/IndexID pair for each ancestor except for the - // first, which has already been skipped in our input. - for j := 0; j < 2; j++ { - idLen, err := encoding.PeekLength(key) - if err != nil { - return 0, err - } - key = key[idLen:] - } - } - - // Decode the remaining values in the key, in the final interleave. - for ; consumedCols < nCols; consumedCols++ { - l, err := encoding.PeekLength(key) - if err != nil { - return 0, err - } - key = key[l:] - } - - return origKeyLen - len(key), nil -} From 3b4274d598d24c5af6e75cab7c528a750efc5acd Mon Sep 17 00:00:00 2001 From: Nathan Stilwell Date: Mon, 25 Oct 2021 16:54:57 -0400 Subject: [PATCH 033/205] ui: Added informative warning for insufficient privileges Previously, `/#/reports/nodes` would seem to hang in a loading state indefinitely when a DB Console user wouldn't have admin privileges for that endpoint. This was due to nodes data being empty from a 403 response. An error is captured in the application state for this response, so mapping this error as a prop to the component, the UI can distinguish between the nodes data simply being empty and being empty due to a restriction error. Detecting this error, we render an `` to notify the user of their lack of privileges. Release note (ui change): All Nodes report notifies user is they need more privileges to view information. --- pkg/ui/workspaces/db-console/src/redux/nodes.ts | 6 ++++++ .../src/views/reports/containers/nodes/index.tsx | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/pkg/ui/workspaces/db-console/src/redux/nodes.ts b/pkg/ui/workspaces/db-console/src/redux/nodes.ts index d9958440c170..0a508bcf8a98 100644 --- a/pkg/ui/workspaces/db-console/src/redux/nodes.ts +++ b/pkg/ui/workspaces/db-console/src/redux/nodes.ts @@ -63,6 +63,9 @@ type NodeStatusState = Pick; export const nodeStatusesSelector = (state: NodeStatusState) => state.cachedData.nodes.data; +export const selectNodesLastError = (state: AdminUIState) => + state.cachedData.nodes.lastError; + /* * clusterSelector returns information about cluster. */ @@ -381,6 +384,7 @@ export const nodesSummarySelector = createSelector( livenessStatusByNodeIDSelector, livenessByNodeIDSelector, selectStoreIDsByNodeID, + selectNodesLastError, ( nodeStatuses, nodeIDs, @@ -390,6 +394,7 @@ export const nodesSummarySelector = createSelector( livenessStatusByNodeID, livenessByNodeID, storeIDsByNodeID, + nodeLastError, ) => { return { nodeStatuses, @@ -400,6 +405,7 @@ export const nodesSummarySelector = createSelector( livenessStatusByNodeID, livenessByNodeID, storeIDsByNodeID, + nodeLastError, }; }, ); diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/nodes/index.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/nodes/index.tsx index 4abba39b8f15..6321cb6eb722 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/nodes/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/nodes/index.tsx @@ -16,6 +16,7 @@ import React from "react"; import { Helmet } from "react-helmet"; import { connect } from "react-redux"; import { withRouter, RouteComponentProps } from "react-router-dom"; +import { InlineAlert } from "src/components"; import * as protos from "src/js/protos"; import { refreshLiveness, refreshNodes } from "src/redux/apiReducers"; @@ -325,9 +326,24 @@ export class Nodes extends React.Component { ); } + requiresAdmin() { + const { + nodesSummary: { nodeLastError }, + } = this.props; + + return nodeLastError?.message === "this operation requires admin privilege"; + } + render() { const { nodesSummary } = this.props; const { nodeStatusByID } = nodesSummary; + + if (this.requiresAdmin()) { + return ( + + ); + } + if (_.isEmpty(nodesSummary.nodeIDs)) { return loading; } From 5d5d4ce835089cb6b9fb911baaea51792417cd5a Mon Sep 17 00:00:00 2001 From: Andrei Matei Date: Mon, 25 Oct 2021 14:43:33 -0400 Subject: [PATCH 034/205] clusterversion: supply new cluster version on OnChange called It's very natural for the OnChange callback to want access to the new value, and I also need it in a future patch. This patch also makes the clusterversion.Handle type more testable by untangling it from a global setting. Release note: None --- pkg/clusterversion/BUILD.bazel | 7 ++- pkg/clusterversion/clusterversion.go | 32 ++++++++---- pkg/clusterversion/clusterversion_test.go | 54 +++++++++++++++++++++ pkg/spanconfig/spanconfigmanager/manager.go | 2 +- 4 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 pkg/clusterversion/clusterversion_test.go diff --git a/pkg/clusterversion/BUILD.bazel b/pkg/clusterversion/BUILD.bazel index 84f8db4faf4a..4b7d8b4d9825 100644 --- a/pkg/clusterversion/BUILD.bazel +++ b/pkg/clusterversion/BUILD.bazel @@ -30,11 +30,16 @@ go_library( go_test( name = "clusterversion_test", size = "small", - srcs = ["cockroach_versions_test.go"], + srcs = [ + "clusterversion_test.go", + "cockroach_versions_test.go", + ], embed = [":clusterversion"], deps = [ "//pkg/roachpb:with-mocks", + "//pkg/settings", "//pkg/util/leaktest", + "//pkg/util/protoutil", "@com_github_cockroachdb_redact//:redact", "@com_github_dustin_go_humanize//:go-humanize", "@com_github_stretchr_testify//require", diff --git a/pkg/clusterversion/clusterversion.go b/pkg/clusterversion/clusterversion.go index 3f7f27870289..1ecbbeb665ed 100644 --- a/pkg/clusterversion/clusterversion.go +++ b/pkg/clusterversion/clusterversion.go @@ -136,13 +136,15 @@ type Handle interface { // version changes. The callback should avoid doing long-running or blocking // work; it's called on the same goroutine handling all cluster setting // updates. - SetOnChange(fn func(context.Context)) + SetOnChange(fn func(ctx context.Context, newVersion ClusterVersion)) } // handleImpl is a concrete implementation of Handle. It mostly relegates to the // underlying cluster version setting, though provides a way for callers to // override the binary and minimum supported versions (for tests usually). type handleImpl struct { + // setting is the version that this handle operates on. + setting *clusterVersionSetting // sv captures the mutable state associated with usage of the otherwise // immutable cluster version setting. sv *settings.Values @@ -173,9 +175,17 @@ func MakeVersionHandle(sv *settings.Values) Handle { func MakeVersionHandleWithOverride( sv *settings.Values, binaryVersion, binaryMinSupportedVersion roachpb.Version, ) Handle { - return &handleImpl{ - sv: sv, + return newHandleImpl(version, sv, binaryVersion, binaryMinSupportedVersion) +} +func newHandleImpl( + setting *clusterVersionSetting, + sv *settings.Values, + binaryVersion, binaryMinSupportedVersion roachpb.Version, +) Handle { + return &handleImpl{ + setting: setting, + sv: sv, binaryVersion: binaryVersion, binaryMinSupportedVersion: binaryMinSupportedVersion, } @@ -183,12 +193,12 @@ func MakeVersionHandleWithOverride( // ActiveVersion implements the Handle interface. func (v *handleImpl) ActiveVersion(ctx context.Context) ClusterVersion { - return version.activeVersion(ctx, v.sv) + return v.setting.activeVersion(ctx, v.sv) } // ActiveVersionOrEmpty implements the Handle interface. func (v *handleImpl) ActiveVersionOrEmpty(ctx context.Context) ClusterVersion { - return version.activeVersionOrEmpty(ctx, v.sv) + return v.setting.activeVersionOrEmpty(ctx, v.sv) } // SetActiveVersion implements the Handle interface. @@ -198,7 +208,7 @@ func (v *handleImpl) SetActiveVersion(ctx context.Context, cv ClusterVersion) er // SETTING version` was originally called). The stricter form of validation // happens there. SetActiveVersion is simply the cluster version bump that // follows from it. - if err := version.validateBinaryVersions(cv.Version, v.sv); err != nil { + if err := v.setting.validateBinaryVersions(cv.Version, v.sv); err != nil { return err } @@ -207,18 +217,20 @@ func (v *handleImpl) SetActiveVersion(ctx context.Context, cv ClusterVersion) er return err } - version.SetInternal(ctx, v.sv, encoded) + v.setting.SetInternal(ctx, v.sv, encoded) return nil } // SetOnChange implements the Handle interface. -func (v *handleImpl) SetOnChange(fn func(ctx context.Context)) { - version.SetOnChange(v.sv, fn) +func (v *handleImpl) SetOnChange(fn func(ctx context.Context, newVersion ClusterVersion)) { + v.setting.SetOnChange(v.sv, func(ctx context.Context) { + fn(ctx, v.ActiveVersion(ctx)) + }) } // IsActive implements the Handle interface. func (v *handleImpl) IsActive(ctx context.Context, key Key) bool { - return version.isActive(ctx, v.sv, key) + return v.setting.isActive(ctx, v.sv, key) } // BinaryVersion implements the Handle interface. diff --git a/pkg/clusterversion/clusterversion_test.go b/pkg/clusterversion/clusterversion_test.go new file mode 100644 index 000000000000..b1365355f002 --- /dev/null +++ b/pkg/clusterversion/clusterversion_test.go @@ -0,0 +1,54 @@ +// Copyright 2017 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package clusterversion + +import ( + "context" + "testing" + + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/settings" + "github.com/cockroachdb/cockroach/pkg/util/protoutil" + "github.com/stretchr/testify/require" +) + +// Test that the OnChange callback is called for the cluster version with the +// right argument. +func TestClusterVersionOnChange(t *testing.T) { + ctx := context.Background() + var sv settings.Values + + cvs := &clusterVersionSetting{} + cvs.VersionSetting = settings.MakeVersionSetting(cvs) + settings.RegisterVersionSetting( + "dummy version key", + "test description", + &cvs.VersionSetting) + + handle := newHandleImpl(cvs, &sv, binaryVersion, binaryMinSupportedVersion) + newCV := ClusterVersion{ + Version: roachpb.Version{ + Major: 1, + Minor: 2, + Patch: 3, + Internal: 4, + }, + } + encoded, err := protoutil.Marshal(&newCV) + require.NoError(t, err) + + var capturedV ClusterVersion + handle.SetOnChange(func(ctx context.Context, newVersion ClusterVersion) { + capturedV = newVersion + }) + cvs.SetInternal(ctx, &sv, encoded) + require.Equal(t, newCV, capturedV) +} diff --git a/pkg/spanconfig/spanconfigmanager/manager.go b/pkg/spanconfig/spanconfigmanager/manager.go index defe3b3f7cc5..0cda68a7d3bc 100644 --- a/pkg/spanconfig/spanconfigmanager/manager.go +++ b/pkg/spanconfig/spanconfigmanager/manager.go @@ -124,7 +124,7 @@ func (m *Manager) run(ctx context.Context) { checkReconciliationJobInterval.SetOnChange(&m.settings.SV, func(ctx context.Context) { triggerJobCheck() }) - m.settings.Version.SetOnChange(func(ctx context.Context) { + m.settings.Version.SetOnChange(func(_ context.Context, _ clusterversion.ClusterVersion) { triggerJobCheck() }) From 9d85ef33926264e03cc8a40cf2950cf3efbdc94a Mon Sep 17 00:00:00 2001 From: Andrei Matei Date: Mon, 25 Oct 2021 12:43:02 -0400 Subject: [PATCH 035/205] util/tracing: remove handling for impossible error InjectMetaInto can no longer return an error (and I don't forsee it ever returning an error again), so this patch cleans up the unused retval. Release note: None --- pkg/kv/kvserver/replica_proposal.go | 5 +---- pkg/util/tracing/grpc_interceptor.go | 6 +----- pkg/util/tracing/span_test.go | 2 +- pkg/util/tracing/tracer.go | 8 +++----- pkg/util/tracing/tracer_test.go | 16 +++++----------- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/pkg/kv/kvserver/replica_proposal.go b/pkg/kv/kvserver/replica_proposal.go index 2e3181f3ac4c..e1c6eaeb1f38 100644 --- a/pkg/kv/kvserver/replica_proposal.go +++ b/pkg/kv/kvserver/replica_proposal.go @@ -957,9 +957,6 @@ func (r *Replica) getTraceData(ctx context.Context) map[string]string { traceCarrier := tracing.MapCarrier{ Map: make(map[string]string), } - if err := r.AmbientContext.Tracer.InjectMetaInto(sp.Meta(), traceCarrier); err != nil { - log.Errorf(ctx, "failed to inject sp context (%+v) as trace data: %s", sp.Meta(), err) - return nil - } + r.AmbientContext.Tracer.InjectMetaInto(sp.Meta(), traceCarrier) return traceCarrier.Map } diff --git a/pkg/util/tracing/grpc_interceptor.go b/pkg/util/tracing/grpc_interceptor.go index 9676cf5c966c..2c183b753655 100644 --- a/pkg/util/tracing/grpc_interceptor.go +++ b/pkg/util/tracing/grpc_interceptor.go @@ -207,11 +207,7 @@ func injectSpanMeta(ctx context.Context, tracer *Tracer, clientSpan *Span) conte } else { md = md.Copy() } - - if err := tracer.InjectMetaInto(clientSpan.Meta(), metadataCarrier{md}); err != nil { - // We have no better place to record an error than the Span itself. - clientSpan.Recordf("error: %s", err) - } + tracer.InjectMetaInto(clientSpan.Meta(), metadataCarrier{md}) return metadata.NewOutgoingContext(ctx, md) } diff --git a/pkg/util/tracing/span_test.go b/pkg/util/tracing/span_test.go index c800c9a452fd..dc85c60a87a8 100644 --- a/pkg/util/tracing/span_test.go +++ b/pkg/util/tracing/span_test.go @@ -50,7 +50,7 @@ func TestRecordingString(t *testing.T) { time.Sleep(10 * time.Millisecond) carrier := metadataCarrier{MD: metadata.MD{}} - require.NoError(t, tr.InjectMetaInto(root.Meta(), carrier)) + tr.InjectMetaInto(root.Meta(), carrier) wireSpanMeta, err := tr2.ExtractMetaFrom(carrier) require.NoError(t, err) diff --git a/pkg/util/tracing/tracer.go b/pkg/util/tracing/tracer.go index f420ca58a951..b204720112cd 100644 --- a/pkg/util/tracing/tracer.go +++ b/pkg/util/tracing/tracer.go @@ -788,17 +788,17 @@ func (c MapCarrier) ForEach(fn func(key, val string) error) error { // InjectMetaInto is used to serialize the given span metadata into the given // Carrier. This, alongside ExtractMetaFrom, can be used to carry span metadata // across process boundaries. See serializationFormat for more details. -func (t *Tracer) InjectMetaInto(sm SpanMeta, carrier Carrier) error { +func (t *Tracer) InjectMetaInto(sm SpanMeta, carrier Carrier) { if sm.Empty() { // Fast path when tracing is disabled. ExtractMetaFrom will accept an // empty map as a noop context. - return nil + return } // If the span has been marked as not wanting children, we don't propagate any // information about it through the carrier (the point of propagating span // info is to create a child from it). if sm.sterile { - return nil + return } carrier.Set(fieldNameTraceID, strconv.FormatUint(uint64(sm.traceID), 16)) @@ -811,8 +811,6 @@ func (t *Tracer) InjectMetaInto(sm SpanMeta, carrier Carrier) error { carrier.Set(fieldNameOtelTraceID, sm.otelCtx.TraceID().String()) carrier.Set(fieldNameOtelSpanID, sm.otelCtx.SpanID().String()) } - - return nil } var noopSpanMeta = SpanMeta{} diff --git a/pkg/util/tracing/tracer_test.go b/pkg/util/tracing/tracer_test.go index 43ee7c4602cd..cc98c1ef3b75 100644 --- a/pkg/util/tracing/tracer_test.go +++ b/pkg/util/tracing/tracer_test.go @@ -253,7 +253,7 @@ func TestSterileSpan(t *testing.T) { // Check that the meta of a sterile span doesn't get injected into carriers. carrier := metadataCarrier{metadata.MD{}} - require.NoError(t, tr.InjectMetaInto(sp1.Meta(), carrier)) + tr.InjectMetaInto(sp1.Meta(), carrier) require.Len(t, carrier.MD, 0) } @@ -268,9 +268,7 @@ func TestTracerInjectExtract(t *testing.T) { t.Fatalf("expected noop Span: %+v", noop1) } carrier := metadataCarrier{metadata.MD{}} - if err := tr.InjectMetaInto(noop1.Meta(), carrier); err != nil { - t.Fatal(err) - } + tr.InjectMetaInto(noop1.Meta(), carrier) if len(carrier.MD) != 0 { t.Errorf("noop Span has carrier: %+v", carrier) } @@ -296,9 +294,7 @@ func TestTracerInjectExtract(t *testing.T) { s1.SetVerbose(true) carrier = metadataCarrier{metadata.MD{}} - if err := tr.InjectMetaInto(s1.Meta(), carrier); err != nil { - t.Fatal(err) - } + tr.InjectMetaInto(s1.Meta(), carrier) wireSpanMeta, err = tr2.ExtractMetaFrom(carrier) if err != nil { @@ -355,7 +351,7 @@ func TestTracer_PropagateNonRecordingRealSpanAcrossRPCBoundaries(t *testing.T) { defer sp1.Finish() carrier := metadataCarrier{MD: metadata.MD{}} require.True(t, spanInclusionFuncForClient(sp1)) - require.NoError(t, tr1.InjectMetaInto(sp1.Meta(), carrier)) + tr1.InjectMetaInto(sp1.Meta(), carrier) require.Equal(t, 2, carrier.Len(), "%+v", carrier) // trace id and span id tr2 := NewTracer() @@ -390,9 +386,7 @@ func TestOtelTracer(t *testing.T) { s.SetBaggageItem(testBaggageKey, testBaggageVal) carrier := metadataCarrier{metadata.MD{}} - if err := tr.InjectMetaInto(s.Meta(), carrier); err != nil { - t.Fatal(err) - } + tr.InjectMetaInto(s.Meta(), carrier) // ExtractMetaFrom also extracts the embedded OpenTelemetry context. wireSpanMeta, err := tr.ExtractMetaFrom(carrier) From e90a35f78dfdee223bbedede560c8d9142445851 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 25 Oct 2021 21:23:00 +0000 Subject: [PATCH 036/205] backup: mark some settings public This marks some of BACKUP's cluster settings as public as they are intended for user-tuning to match their desired workload / requirements, such as the delay before invoking priority reads or the target file size. Release note (ops change): Some existing settings related to BACKUP execution are now listed by SHOW CLUSTER SETTINGS. --- docs/generated/settings/settings-for-tenants.txt | 3 +++ docs/generated/settings/settings.html | 3 +++ pkg/ccl/backupccl/backup_processor.go | 15 ++++++++------- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index bd4a20708c5b..1309a367e609 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -2,6 +2,9 @@ Setting Type Default Description admission.kv.enabled boolean false when true, work performed by the KV layer is subject to admission control admission.sql_kv_response.enabled boolean false when true, work performed by the SQL layer when receiving a KV response is subject to admission control admission.sql_sql_response.enabled boolean false when true, work performed by the SQL layer when receiving a DistSQL response is subject to admission control +bulkio.backup.file_size byte size 128 MiB target size for individual data files produced during BACKUP +bulkio.backup.read_timeout duration 5m0s amount of time after which a read attempt is considered timed out, which causes the backup to fail +bulkio.backup.read_with_priority_after duration 1m0s amount of time since the read-as-of time above which a BACKUP should use priority when retrying reads bulkio.stream_ingestion.minimum_flush_interval duration 5s the minimum timestamp between flushes; flushes may still occur if internal buffers fill up changefeed.node_throttle_config string specifies node level throttling configuration for all changefeeeds cloudstorage.http.custom_ca string custom root CA (appended to system's default CAs) for verifying certificates when interacting with HTTPS storage diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 8588c8a00fbd..abcbb3808757 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -4,6 +4,9 @@ admission.kv.enabledbooleanfalsewhen true, work performed by the KV layer is subject to admission control admission.sql_kv_response.enabledbooleanfalsewhen true, work performed by the SQL layer when receiving a KV response is subject to admission control admission.sql_sql_response.enabledbooleanfalsewhen true, work performed by the SQL layer when receiving a DistSQL response is subject to admission control +bulkio.backup.file_sizebyte size128 MiBtarget size for individual data files produced during BACKUP +bulkio.backup.read_timeoutduration5m0samount of time after which a read attempt is considered timed out, which causes the backup to fail +bulkio.backup.read_with_priority_afterduration1m0samount of time since the read-as-of time above which a BACKUP should use priority when retrying reads bulkio.stream_ingestion.minimum_flush_intervalduration5sthe minimum timestamp between flushes; flushes may still occur if internal buffers fill up changefeed.node_throttle_configstringspecifies node level throttling configuration for all changefeeeds cloudstorage.http.custom_castringcustom root CA (appended to system's default CAs) for verifying certificates when interacting with HTTPS storage diff --git a/pkg/ccl/backupccl/backup_processor.go b/pkg/ccl/backupccl/backup_processor.go index 10d6d2664301..445933511f27 100644 --- a/pkg/ccl/backupccl/backup_processor.go +++ b/pkg/ccl/backupccl/backup_processor.go @@ -54,10 +54,10 @@ var ( ) priorityAfter = settings.RegisterDurationSetting( "bulkio.backup.read_with_priority_after", - "age of read-as-of time above which a BACKUP should read with priority", + "amount of time since the read-as-of time above which a BACKUP should use priority when retrying reads", time.Minute, settings.NonNegativeDuration, - ) + ).WithPublic() delayPerAttmpt = settings.RegisterDurationSetting( "bulkio.backup.read_retry_delay", "amount of time since the read-as-of time, per-prior attempt, to wait before making another attempt", @@ -66,22 +66,23 @@ var ( ) timeoutPerAttempt = settings.RegisterDurationSetting( "bulkio.backup.read_timeout", - "amount of time after which a read attempt is considered timed out and is canceled. "+ - "Hitting this timeout will cause the backup job to fail.", + "amount of time after which a read attempt is considered timed out, which causes the backup to fail", time.Minute*5, settings.NonNegativeDuration, - ) + ).WithPublic() targetFileSize = settings.RegisterByteSizeSetting( "bulkio.backup.file_size", - "target file size", + "target size for individual data files produced during BACKUP", 128<<20, - ) + ).WithPublic() + smallFileBuffer = settings.RegisterByteSizeSetting( "bulkio.backup.merge_file_buffer_size", "size limit used when buffering backup files before merging them", 16<<20, settings.NonNegativeInt, ) + splitKeysOnTimestamps = settings.RegisterBoolSetting( "bulkio.backup.split_keys_on_timestamps", "split backup data on timestamps when writing revision history", From 39067de03ec046656a80c1aedd78acb222b80c07 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Mon, 25 Oct 2021 12:40:40 -0400 Subject: [PATCH 037/205] sql/pgwire: handle broken connection better in processing loop Broken connections were not beinf detected correctly. Now, netutil checks for all net.Errors more broadly. There's also a defensive check in the pgwire loop so that if an error is mis-classified, we bail out after a large number of repeated errors. Release note (bug fix): Previously, some instances of a broken client connection could cause an infinite loop while processing commands from the client. This is fixed now. --- pkg/sql/pgwire/conn.go | 17 +++++-- pkg/util/netutil/BUILD.bazel | 9 +++- pkg/util/netutil/net.go | 9 ++-- pkg/util/netutil/net_test.go | 93 ++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 pkg/util/netutil/net_test.go diff --git a/pkg/sql/pgwire/conn.go b/pkg/sql/pgwire/conn.go index 7ea89d543978..c4600b690417 100644 --- a/pkg/sql/pgwire/conn.go +++ b/pkg/sql/pgwire/conn.go @@ -341,8 +341,8 @@ func (c *conn) serveImpl( } var terminateSeen bool - var authDone, ignoreUntilSync bool + var repeatedErrorCount int for { breakLoop, err := func() (bool, error) { typ, n, err := c.readBuf.ReadTypedMsg(&c.rd) @@ -482,11 +482,20 @@ func (c *conn) serveImpl( if err != nil { log.VEventf(ctx, 1, "pgwire: error processing message: %s", err) ignoreUntilSync = true - // If we can't read data because the connection was closed or the context - // was canceled (e.g. during authentication), then we should break. - if netutil.IsClosedConnection(err) || errors.Is(err, context.Canceled) { + repeatedErrorCount++ + const maxRepeatedErrorCount = 1 << 15 + // If we can't read data because of any one of the following conditions, + // then we should break: + // 1. the connection was closed. + // 2. the context was canceled (e.g. during authentication). + // 3. we reached an arbitrary threshold of repeated errors. + if netutil.IsClosedConnection(err) || + errors.Is(err, context.Canceled) || + repeatedErrorCount > maxRepeatedErrorCount { break } + } else { + repeatedErrorCount = 0 } if breakLoop { break diff --git a/pkg/util/netutil/BUILD.bazel b/pkg/util/netutil/BUILD.bazel index 5505ba9762af..561667b727fd 100644 --- a/pkg/util/netutil/BUILD.bazel +++ b/pkg/util/netutil/BUILD.bazel @@ -24,10 +24,17 @@ go_library( go_test( name = "netutil_test", - srcs = ["srv_test.go"], + srcs = [ + "net_test.go", + "srv_test.go", + ], embed = [":netutil"], deps = [ + "//pkg/util/contextutil", + "@com_github_cockroachdb_cmux//:cmux", "@com_github_cockroachdb_errors//:errors", + "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", + "@org_golang_google_grpc//:go_default_library", ], ) diff --git a/pkg/util/netutil/net.go b/pkg/util/netutil/net.go index ffdfbb09323d..f729dee7d110 100644 --- a/pkg/util/netutil/net.go +++ b/pkg/util/netutil/net.go @@ -159,10 +159,13 @@ func (s *Server) ServeWith( } } -// IsClosedConnection returns true if err is cmux.ErrListenerClosed, -// grpc.ErrServerStopped, io.EOF, or the net package's errClosed. +// IsClosedConnection returns true if err is a non-temporary net.Error or is +// cmux.ErrListenerClosed, grpc.ErrServerStopped, io.EOF, or net.ErrClosed. func IsClosedConnection(err error) bool { - return errors.IsAny(err, cmux.ErrListenerClosed, grpc.ErrServerStopped, io.EOF) || + if netError := net.Error(nil); errors.As(err, &netError) { + return !netError.Temporary() + } + return errors.IsAny(err, cmux.ErrListenerClosed, grpc.ErrServerStopped, io.EOF, net.ErrClosed) || strings.Contains(err.Error(), "use of closed network connection") } diff --git a/pkg/util/netutil/net_test.go b/pkg/util/netutil/net_test.go new file mode 100644 index 000000000000..2cc492689313 --- /dev/null +++ b/pkg/util/netutil/net_test.go @@ -0,0 +1,93 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package netutil + +import ( + "fmt" + "io" + "net" + "syscall" + "testing" + + "github.com/cockroachdb/cmux" + "github.com/cockroachdb/cockroach/pkg/util/contextutil" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +func TestIsClosedConnection(t *testing.T) { + for _, tc := range []struct { + err error + isClosedError bool + }{ + { + fmt.Errorf("an error"), + false, + }, + { + net.ErrClosed, + true, + }, + { + cmux.ErrListenerClosed, + true, + }, + { + grpc.ErrServerStopped, + true, + }, + { + io.EOF, + true, + }, + { + // TODO(rafi): should this be treated the same as EOF? + io.ErrUnexpectedEOF, + false, + }, + { + &net.AddrError{Err: "addr", Addr: "err"}, + true, + }, + { + syscall.ECONNRESET, + true, + }, + { + syscall.EADDRINUSE, + true, + }, + { + syscall.ECONNABORTED, + true, + }, + { + syscall.ECONNREFUSED, + true, + }, + { + syscall.EBADMSG, + true, + }, + { + syscall.EINTR, + false, + }, + { + &contextutil.TimeoutError{}, + false, + }, + } { + assert.Equalf(t, tc.isClosedError, IsClosedConnection(tc.err), + "expected %q to be evaluated as %v", tc.err, tc.isClosedError, + ) + } +} From 2db1e8b3c38c17e27e3e9d671bbb6571527ee0c4 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Mon, 25 Oct 2021 17:07:47 +0000 Subject: [PATCH 038/205] kvserver: apply gossip side-effects under Raft Following 584fb97, `Replica.handleReadWriteLocalEvalResult()` may take out `raftMu` when applying gossip triggers. However, this was called from `Replica.evalAndPropose()` via `Replica.executeWriteBatch()` which already held `readOnlyCmdMu`. This violates the `Store.mu` locking protocol since `raftMu` must be taken out before `readOnlyCmdMu`, which may lead to deadlocks. The motivation for that change was to avoid sending noop `EndTxn` requests via Raft, even when they result in gossip triggers that require `raftMu`. However, taking out `raftMu` above Raft complicates the locking model, which can result in bugs like this one, and the commit message outlines an alternative solution: do not omit Raft when a local result has a gossip trigger. Since noop `EndTxn` requests with gossip triggers are fairly rare, and are not significantly impacted by the additional Raft latency, this patch reverts 584fb97 and instead sends these results through Raft. Release note: None --- pkg/kv/kvserver/batcheval/result/result.go | 9 ++++++ .../replica_application_state_machine.go | 2 +- pkg/kv/kvserver/replica_proposal.go | 28 +++++++------------ pkg/kv/kvserver/replica_raft.go | 6 +++- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/pkg/kv/kvserver/batcheval/result/result.go b/pkg/kv/kvserver/batcheval/result/result.go index 3ac37d887c40..d68f5432adac 100644 --- a/pkg/kv/kvserver/batcheval/result/result.go +++ b/pkg/kv/kvserver/batcheval/result/result.go @@ -103,6 +103,15 @@ func (lResult *LocalResult) String() string { lResult.MaybeGossipNodeLiveness) } +// RequiresRaft returns true if the local result needs to go via Raft, e.g. in +// order to apply side effects under Raft. +func (lResult *LocalResult) RequiresRaft() bool { + // Gossip triggers require raftMu to be held. + return lResult.MaybeGossipNodeLiveness != nil || + lResult.MaybeGossipSystemConfig || + lResult.MaybeGossipSystemConfigIfHaveFailure +} + // DetachEncounteredIntents returns (and removes) those encountered // intents from the LocalEvalResult which are supposed to be handled. func (lResult *LocalResult) DetachEncounteredIntents() []roachpb.Intent { diff --git a/pkg/kv/kvserver/replica_application_state_machine.go b/pkg/kv/kvserver/replica_application_state_machine.go index c5112c60e764..60500f72887b 100644 --- a/pkg/kv/kvserver/replica_application_state_machine.go +++ b/pkg/kv/kvserver/replica_application_state_machine.go @@ -1202,7 +1202,7 @@ func (sm *replicaStateMachine) ApplySideEffects( if cmd.IsLocal() { // Handle the LocalResult. if cmd.localResult != nil { - sm.r.handleReadWriteLocalEvalResult(ctx, *cmd.localResult, true /* raftMuHeld */) + sm.r.handleReadWriteLocalEvalResult(ctx, *cmd.localResult) } rejected := cmd.Rejected() diff --git a/pkg/kv/kvserver/replica_proposal.go b/pkg/kv/kvserver/replica_proposal.go index 2e3181f3ac4c..056a1437a9b4 100644 --- a/pkg/kv/kvserver/replica_proposal.go +++ b/pkg/kv/kvserver/replica_proposal.go @@ -634,9 +634,7 @@ func addSSTablePreApply( return copied } -func (r *Replica) handleReadWriteLocalEvalResult( - ctx context.Context, lResult result.LocalResult, raftMuHeld bool, -) { +func (r *Replica) handleReadWriteLocalEvalResult(ctx context.Context, lResult result.LocalResult) { // Fields for which no action is taken in this method are zeroed so that // they don't trigger an assertion at the end of the method (which checks // that all fields were handled). @@ -702,19 +700,13 @@ func (r *Replica) handleReadWriteLocalEvalResult( lResult.MaybeAddToSplitQueue = false } - // The following three triggers require the raftMu to be held. If a - // trigger is present, acquire the mutex if it is not held already. - maybeAcquireRaftMu := func() func() { - if raftMuHeld { - return func() {} - } - raftMuHeld = true - r.raftMu.Lock() - return r.raftMu.Unlock - } - + // The gossip triggers below require raftMu to be held, but + // handleReadWriteLocalEvalResult() may be called from non-Raft code paths (in + // particular for noop proposals). LocalResult.RequiresRaft() will force + // results that set these gossip triggers to always go via Raft such that + // raftMu is held. The triggers assert that callers hold the mutex during race + // tests via raftMu.AssertHeld(). if lResult.MaybeGossipSystemConfig { - defer maybeAcquireRaftMu()() if err := r.MaybeGossipSystemConfigRaftMuLocked(ctx); err != nil { log.Errorf(ctx, "%v", err) } @@ -722,7 +714,6 @@ func (r *Replica) handleReadWriteLocalEvalResult( } if lResult.MaybeGossipSystemConfigIfHaveFailure { - defer maybeAcquireRaftMu()() if err := r.MaybeGossipSystemConfigIfHaveFailureRaftMuLocked(ctx); err != nil { log.Errorf(ctx, "%v", err) } @@ -730,7 +721,6 @@ func (r *Replica) handleReadWriteLocalEvalResult( } if lResult.MaybeGossipNodeLiveness != nil { - defer maybeAcquireRaftMu()() if err := r.MaybeGossipNodeLivenessRaftMuLocked(ctx, *lResult.MaybeGossipNodeLiveness); err != nil { log.Errorf(ctx, "%v", err) } @@ -833,10 +823,12 @@ func (r *Replica) evaluateProposal( // even with an empty write batch when stats are recomputed. // 3. the request has replicated side-effects. // 4. the request is of a type that requires consensus (eg. Barrier). + // 5. the request has side-effects that must be applied under Raft. needConsensus := !batch.Empty() || ms != (enginepb.MVCCStats{}) || !res.Replicated.IsZero() || - ba.RequiresConsensus() + ba.RequiresConsensus() || + res.Local.RequiresRaft() if needConsensus { log.VEventf(ctx, 2, "need consensus on write batch with op count=%d", batch.Count()) diff --git a/pkg/kv/kvserver/replica_raft.go b/pkg/kv/kvserver/replica_raft.go index eca997e7b53a..320bd71a4528 100644 --- a/pkg/kv/kvserver/replica_raft.go +++ b/pkg/kv/kvserver/replica_raft.go @@ -113,9 +113,13 @@ func (r *Replica) evalAndPropose( // 2. pErr != nil corresponds to a failed proposal - the command resulted // in an error. if proposal.command == nil { + if proposal.Local.RequiresRaft() { + return nil, nil, "", roachpb.NewError(errors.AssertionFailedf( + "proposal resulting from batch %s erroneously bypassed Raft", ba)) + } intents := proposal.Local.DetachEncounteredIntents() endTxns := proposal.Local.DetachEndTxns(pErr != nil /* alwaysOnly */) - r.handleReadWriteLocalEvalResult(ctx, *proposal.Local, false /* raftMuHeld */) + r.handleReadWriteLocalEvalResult(ctx, *proposal.Local) pr := proposalResult{ Reply: proposal.Local.Reply, From d82f194307e14fc76be7830ddd23ae9f6c7ea508 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Mon, 18 Oct 2021 19:14:36 +0000 Subject: [PATCH 039/205] kvserver: declare lock spans for `AddSSTable` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `AddSSTable` did not declare lock spans, even though it can return `WriteIntentError` when encountering unresolved intents (e.g. when checking for key collisions with `DisallowShadowing` set). This would cause the concurrency manager to error with e.g.: ``` cannot handle WriteIntentError ‹conflicting intents on /Table/84/1/99/49714/0› for request without lockTableGuard; were lock spans declared for this request? ``` This patch makes `AddSSTable` take out lock spans via `DefaultDeclareIsolatedKeys` if `DisallowShadowing` is set. This will automatically handle any unresolved intents via the concurrency manager. Release note (bug fix): `IMPORT INTO` no longer crashes when encountering unresolved write intents. --- pkg/kv/kvserver/batcheval/cmd_add_sstable.go | 70 ++++++++++++++++- .../batcheval/cmd_add_sstable_test.go | 77 +++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/pkg/kv/kvserver/batcheval/cmd_add_sstable.go b/pkg/kv/kvserver/batcheval/cmd_add_sstable.go index dab9cb4a4862..d4af1e6ea930 100644 --- a/pkg/kv/kvserver/batcheval/cmd_add_sstable.go +++ b/pkg/kv/kvserver/batcheval/cmd_add_sstable.go @@ -17,6 +17,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" + "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/storage/enginepb" @@ -29,7 +30,74 @@ import ( ) func init() { - RegisterReadWriteCommand(roachpb.AddSSTable, DefaultDeclareKeys, EvalAddSSTable) + RegisterReadWriteCommand(roachpb.AddSSTable, declareKeysAddSSTable, EvalAddSSTable) +} + +func declareKeysAddSSTable( + rs ImmutableRangeState, + header roachpb.Header, + req roachpb.Request, + latchSpans, lockSpans *spanset.SpanSet, +) { + // AddSSTable violates MVCC and closed timestamp invariants, so the + // concurrency semantics deserve special attention. + // + // AddSSTable cannot be in a transaction, cannot write intents or tombstones, + // cannot be split across ranges, and is always alone in a batch. + // + // The KV pairs in the SST already have fixed MVCC timestamps, independent of + // the batch timestamp. Pushes by other txns or the closed timestamp do not + // affect the MVCC timestamps. They can be at any time (past or future), even + // below the closed timestamp, and by default they can replace existing + // versions or write below existing versions and intents. This violates MVCC, + // because history must be immutable, and the closed timestamp, because writes + // should never happen below it. + // + // DisallowShadowing=true will prevent writing to keys that already exist + // (with any timestamp), returning an error -- except if the last version is a + // tombstone with a timestamp below the written key or if the timestamp and + // value exactly match the incoming write (for idempotency). If an intent is + // found, WriteIntentError will be returned in order to resolve it and retry: + // if the intent was aborted or a tombstone the request may succeed, but if it + // was a committed value the request will fail. This still violates MVCC (it + // may write a key in the past whose absence has already been observed by a + // reader) and the closed timestamp (it may write a key below it). + // + // The request header's Key and EndKey are set to cover the first and last key + // in the SST. Below, we always declare write latches across this span for + // isolation from concurrent requests. If DisallowShadowing=true, we must also + // declare lock spans over this span for isolation from concurrent + // transactions, and return WriteIntentError for any encountered intents to + // resolve them. This is particularly relevant for IMPORT INTO, which imports + // into an offline table that may contain unresolved intents from previous + // transactions. + // + // Taking out latches/locks across the entire SST span is very coarse, and we + // could instead iterate over the SST and take out point latches/locks, but + // the cost is likely not worth it since AddSSTable is often used with + // unpopulated spans. + // + // AddSSTable callers must take extreme care to only write into key/time spans + // that have never been accessed by a past transaction, and will not be + // accessed by a concurrent transaction, or to make sure these accesses are + // safe. Below is a list of current operations that use AddSSTable and their + // characteristics: + // + // | Operation | DisallowShadowing | Timestamp | Isolation via | + // |------------------------|-------------------|--------------|-------------------| + // | Import | true | Now | Offline table | + // | CREATE TABLE AS SELECT | true | Read TS | Table descriptor | + // | Materialized views | true | Read TS | Table descriptor | + // | Index backfills | false | Now | Index descriptor | + // | Restore (backup) | true | Key TS | Table descriptor | + // | Streaming replication | false | Key TS | Offline tenant | + // + args := req.(*roachpb.AddSSTableRequest) + if args.DisallowShadowing { + DefaultDeclareIsolatedKeys(rs, header, req, latchSpans, lockSpans) + } else { + DefaultDeclareKeys(rs, header, req, latchSpans, lockSpans) + } } // EvalAddSSTable evaluates an AddSSTable command. diff --git a/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go b/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go index 35d07bf256e2..e9b622f100b7 100644 --- a/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go +++ b/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go @@ -36,6 +36,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/tracing" "github.com/cockroachdb/errors" "github.com/kr/pretty" + "github.com/stretchr/testify/require" ) var engineImpls = []struct { @@ -1056,3 +1057,79 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { }) } } + +func TestAddSSTableDisallowShadowingIntentResolution(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + ctx := context.Background() + s, _, db := serverutils.StartServer(t, base.TestServerArgs{}) + defer s.Stopper().Stop(ctx) + + // Start a transaction that writes an intent at b. + txn := db.NewTxn(ctx, "intent") + require.NoError(t, txn.Put(ctx, "b", "intent")) + + // Generate an SSTable that covers keys a, b, and c, and submit it with high + // priority. This is going to abort the transaction above, encounter its + // intent, and resolve it. + sst := makeSST(t, s.Clock().Now(), map[string]string{ + "a": "1", + "b": "2", + "c": "3", + }) + stats := sstStats(t, sst) + + ba := roachpb.BatchRequest{} + ba.Header.UserPriority = roachpb.MaxUserPriority + ba.Add(&roachpb.AddSSTableRequest{ + RequestHeader: roachpb.RequestHeader{Key: roachpb.Key("a"), EndKey: roachpb.Key("d")}, + Data: sst, + MVCCStats: stats, + DisallowShadowing: true, + }) + _, pErr := db.NonTransactionalSender().Send(ctx, ba) + require.Nil(t, pErr) + + // The transaction should now be aborted. + err := txn.Commit(ctx) + require.Error(t, err) + require.Contains(t, err.Error(), "TransactionRetryWithProtoRefreshError: TransactionAbortedError") +} + +func makeSST(t *testing.T, ts hlc.Timestamp, kvs map[string]string) []byte { + t.Helper() + + sstFile := &storage.MemFile{} + writer := storage.MakeBackupSSTWriter(sstFile) + defer writer.Close() + + keys := make([]string, 0, len(kvs)) + for key := range kvs { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, k := range keys { + key := storage.MVCCKey{Key: roachpb.Key(k), Timestamp: ts} + value := roachpb.Value{} + value.SetString(kvs[k]) + value.InitChecksum(key.Key) + require.NoError(t, writer.Put(key, value.RawBytes)) + } + require.NoError(t, writer.Finish()) + writer.Close() + return sstFile.Data() +} + +func sstStats(t *testing.T, sst []byte) *enginepb.MVCCStats { + t.Helper() + + iter, err := storage.NewMemSSTIterator(sst, true) + require.NoError(t, err) + defer iter.Close() + + stats, err := storage.ComputeStatsForRange(iter, keys.MinKey, keys.MaxKey, 0) + require.NoError(t, err) + return &stats +} From 213f71ca2301961e39ac90efb52ec697922e217f Mon Sep 17 00:00:00 2001 From: Faizan Qazi Date: Wed, 13 Oct 2021 14:09:32 -0400 Subject: [PATCH 040/205] sql: remove sql.defaults.interleaved_tables.enabled Fixes: #71460 Previously, sql.defaults.interleaved_tables.enabled was used to control if interleaved table support was allowed as a part of our deprecation strategy. Enabling this setting would allow users to create interleaved tables again. This was inadequate because interleaved support is now fully removed, so this setting serves no purpose. To address, this patch removes this non-functional setting. Release note (sql change): Remove the cluster setting sql.defaults.interleaved_tables.enabled, since interleaved support is fully removed now. --- .../settings/settings-for-tenants.txt | 1 - docs/generated/settings/settings.html | 1 - .../testdata/logic_test/partitioning_index | 1 - pkg/ccl/workloadccl/roachmartccl/roachmart.go | 7 --- pkg/internal/sqlsmith/setup.go | 1 - pkg/jobs/registry_external_test.go | 1 - .../settings_watcher_external_test.go | 4 -- pkg/server/testserver.go | 4 -- pkg/settings/registry.go | 9 +-- pkg/sql/create_index.go | 11 ---- pkg/sql/exec_util.go | 8 --- pkg/sql/logictest/logic.go | 4 -- .../logictest/testdata/logic_test/event_log | 1 - pkg/sql/logictest/testdata/logic_test/system | 2 - pkg/sql/schema_changer_test.go | 59 ------------------- pkg/workload/sqlsmith/sqlsmith.go | 11 +--- 16 files changed, 6 insertions(+), 119 deletions(-) diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index bd4a20708c5b..5a326d33c8a9 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -96,7 +96,6 @@ sql.defaults.idle_in_session_timeout duration 0s default value for the idle_in_s sql.defaults.idle_in_transaction_session_timeout duration 0s default value for the idle_in_transaction_session_timeout; controls the duration a session is permitted to idle in a transaction before the session is terminated; if set to 0, there is no timeout sql.defaults.implicit_select_for_update.enabled boolean true default value for enable_implicit_select_for_update session setting; enables FOR UPDATE locking during the row-fetch phase of mutation statements sql.defaults.insert_fast_path.enabled boolean true default value for enable_insert_fast_path session setting; enables a specialized insert path -sql.defaults.interleaved_tables.enabled boolean false allows creation of interleaved tables or indexes sql.defaults.intervalstyle enumeration postgres default value for IntervalStyle session setting [postgres = 0, iso_8601 = 1, sql_standard = 2] sql.defaults.intervalstyle.enabled boolean false default value for intervalstyle_enabled session setting sql.defaults.large_full_scan_rows float 1000 default value for large_full_scan_rows session setting which determines the maximum table size allowed for a full scan when disallow_full_table_scans is set to true diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 8588c8a00fbd..21a08c8be955 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -101,7 +101,6 @@ sql.defaults.idle_in_transaction_session_timeoutduration0sdefault value for the idle_in_transaction_session_timeout; controls the duration a session is permitted to idle in a transaction before the session is terminated; if set to 0, there is no timeout sql.defaults.implicit_select_for_update.enabledbooleantruedefault value for enable_implicit_select_for_update session setting; enables FOR UPDATE locking during the row-fetch phase of mutation statements sql.defaults.insert_fast_path.enabledbooleantruedefault value for enable_insert_fast_path session setting; enables a specialized insert path -sql.defaults.interleaved_tables.enabledbooleanfalseallows creation of interleaved tables or indexes sql.defaults.intervalstyleenumerationpostgresdefault value for IntervalStyle session setting [postgres = 0, iso_8601 = 1, sql_standard = 2] sql.defaults.intervalstyle.enabledbooleanfalsedefault value for intervalstyle_enabled session setting sql.defaults.large_full_scan_rowsfloat1000default value for large_full_scan_rows session setting which determines the maximum table size allowed for a full scan when disallow_full_table_scans is set to true diff --git a/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index b/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index index 7ed4f11e0003..81aaf77cf214 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index +++ b/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index @@ -216,7 +216,6 @@ CREATE TABLE public.t60019 ( # Regression test for #60699. Do not allow creation of interleaved partitioned # indexes. statement ok -SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled = true; CREATE TABLE t60699_a (a INT PRIMARY KEY); CREATE TABLE t60699_b (b INT PRIMARY KEY, a INT REFERENCES t60699_a (a)); diff --git a/pkg/ccl/workloadccl/roachmartccl/roachmart.go b/pkg/ccl/workloadccl/roachmartccl/roachmart.go index ff22c4054aae..27288e589c81 100644 --- a/pkg/ccl/workloadccl/roachmartccl/roachmart.go +++ b/pkg/ccl/workloadccl/roachmartccl/roachmart.go @@ -131,13 +131,6 @@ func (m *roachmart) Hooks() workload.Hooks { return nil }, - PreCreate: func(db *gosql.DB) error { - if _, err := db.Exec(`SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled = true`); err != nil { - return err - } - return nil - }, - PreLoad: func(db *gosql.DB) error { if _, err := db.Exec(zoneLocationsStmt); err != nil { return err diff --git a/pkg/internal/sqlsmith/setup.go b/pkg/internal/sqlsmith/setup.go index 8b0327c3d7f8..df95e613996f 100644 --- a/pkg/internal/sqlsmith/setup.go +++ b/pkg/internal/sqlsmith/setup.go @@ -80,7 +80,6 @@ func randTablesN(r *rand.Rand, n int) string { sb.WriteString(` SET CLUSTER SETTING sql.stats.automatic_collection.enabled = false; SET CLUSTER SETTING sql.stats.histogram_collection.enabled = false; - SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled = true; `) // Create the random tables. diff --git a/pkg/jobs/registry_external_test.go b/pkg/jobs/registry_external_test.go index 41340d381912..ac2c4ee1b0f5 100644 --- a/pkg/jobs/registry_external_test.go +++ b/pkg/jobs/registry_external_test.go @@ -846,7 +846,6 @@ func TestMixedVersionRevertFailed(t *testing.T) { settings := cluster.MakeTestingClusterSettingsWithVersions( v21_1, v21_1, true, /* initializeVersion */ ) - sql.InterleavedTablesEnabled.Override(ctx, &settings.SV, true) s, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{ Settings: settings, Knobs: base.TestingKnobs{ diff --git a/pkg/server/settingswatcher/settings_watcher_external_test.go b/pkg/server/settingswatcher/settings_watcher_external_test.go index d404ae151c1c..030dda42d5e1 100644 --- a/pkg/server/settingswatcher/settings_watcher_external_test.go +++ b/pkg/server/settingswatcher/settings_watcher_external_test.go @@ -43,10 +43,6 @@ func TestSettingWatcher(t *testing.T) { tdb := sqlutils.MakeSQLRunner(tc.ServerConn(0)) - // Interleaved tables are overridden to be on in testservers even though - // that is not the default value. - tdb.Exec(t, "SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled = false") - toSet := map[string][]interface{}{ "sql.defaults.experimental_hash_sharded_indexes.enabled": {true, false}, "kv.queue.process.guaranteed_time_budget": {"17s", "20s"}, diff --git a/pkg/server/testserver.go b/pkg/server/testserver.go index 1ec0728c9f97..d93410d49fde 100644 --- a/pkg/server/testserver.go +++ b/pkg/server/testserver.go @@ -281,10 +281,6 @@ func makeTestConfigFromParams(params base.TestServerArgs) Config { cfg.TestingKnobs.SQLExecutor = &sql.ExecutorTestingKnobs{} } - // For test servers, leave interleaved tables enabled by default. We'll remove - // this when we remove interleaved tables altogether. - sql.InterleavedTablesEnabled.Override(context.Background(), &cfg.Settings.SV, true) - return cfg } diff --git a/pkg/settings/registry.go b/pkg/settings/registry.go index cf08141bd38a..af0b864af94e 100644 --- a/pkg/settings/registry.go +++ b/pkg/settings/registry.go @@ -105,10 +105,11 @@ var retiredSettings = map[string]struct{}{ "sql.telemetry.query_sampling.sample_rate": {}, // removed as of 22.1. - "sql.defaults.drop_enum_value.enabled": {}, - "trace.lightstep.token": {}, - "trace.datadog.agent": {}, - "trace.datadog.project": {}, + "sql.defaults.drop_enum_value.enabled": {}, + "trace.lightstep.token": {}, + "trace.datadog.agent": {}, + "trace.datadog.project": {}, + "sql.defaults.interleaved_tables.enabled": {}, } // register adds a setting to the registry. diff --git a/pkg/sql/create_index.go b/pkg/sql/create_index.go index 983c3de25acd..57bc03d2812f 100644 --- a/pkg/sql/create_index.go +++ b/pkg/sql/create_index.go @@ -606,14 +606,6 @@ var interleavedTableDeprecationError = errors.WithIssueLink( errors.IssueLink{IssueURL: build.MakeIssueURL(52009)}, ) -var interleavedTableDisabledError = errors.WithIssueLink( - pgerror.New(pgcode.WarningDeprecatedFeature, - "interleaved tables and interleaved indexes are disabled due to the sql.defaults."+ - "interleaved_tables.enabled cluster setting. Note that interleaved tables and interleaved indexes will be "+ - "removed in a future release. For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations"), - errors.IssueLink{IssueURL: build.MakeIssueURL(52009)}, -) - var interleavedTableDisabledMigrationError = pgnotice.Newf( "creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored." + " For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations") @@ -631,9 +623,6 @@ func interleavedTableDeprecationAction(params runParams) (ignoreInterleave bool, ) return true, nil } - if !InterleavedTablesEnabled.Get(params.p.execCfg.SV()) { - return false, interleavedTableDisabledError - } params.p.BufferClientNotice( params.ctx, interleavedTableDeprecationError, diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index b6050ec6b4a2..b549d597111c 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -319,14 +319,6 @@ var preferLookupJoinsForFKs = settings.RegisterBoolSetting( false, ).WithPublic() -// InterleavedTablesEnabled is the setting that controls whether it's possible -// to create interleaved indexes or tables. -var InterleavedTablesEnabled = settings.RegisterBoolSetting( - "sql.defaults.interleaved_tables.enabled", - "allows creation of interleaved tables or indexes", - false, -).WithPublic() - // optUseHistogramsClusterMode controls the cluster default for whether // histograms are used by the optimizer for cardinality estimation. // Note that it does not control histogram collection; regardless of the diff --git a/pkg/sql/logictest/logic.go b/pkg/sql/logictest/logic.go index de1c7750cf34..12e8f1a39efd 100644 --- a/pkg/sql/logictest/logic.go +++ b/pkg/sql/logictest/logic.go @@ -1597,10 +1597,6 @@ func (t *logicTest) newCluster(serverArgs TestServerArgs) { } } - if _, err := conn.Exec("SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled = true"); err != nil { - t.Fatal(err) - } - // Update the default AS OF time for querying the system.table_statistics // table to create the crdb_internal.table_row_statistics table. if _, err := conn.Exec( diff --git a/pkg/sql/logictest/testdata/logic_test/event_log b/pkg/sql/logictest/testdata/logic_test/event_log index 85646c63f5f5..8caccf9c522b 100644 --- a/pkg/sql/logictest/testdata/logic_test/event_log +++ b/pkg/sql/logictest/testdata/logic_test/event_log @@ -469,7 +469,6 @@ ORDER BY "timestamp", info 0 1 {"ApplicationName": "$ internal-optInToDiagnosticsStatReporting", "EventType": "set_cluster_setting", "SettingName": "diagnostics.reporting.enabled", "Statement": "SET CLUSTER SETTING \"diagnostics.reporting.enabled\" = true", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "true"} 0 1 {"EventType": "set_cluster_setting", "SettingName": "kv.range_merge.queue_enabled", "Statement": "SET CLUSTER SETTING \"kv.range_merge.queue_enabled\" = false", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "false"} 0 1 {"EventType": "set_cluster_setting", "PlaceholderValues": ["5"], "SettingName": "sql.stats.automatic_collection.min_stale_rows", "Statement": "SET CLUSTER SETTING \"sql.stats.automatic_collection.min_stale_rows\" = $1::INT8", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "5"} -0 1 {"EventType": "set_cluster_setting", "SettingName": "sql.defaults.interleaved_tables.enabled", "Statement": "SET CLUSTER SETTING \"sql.defaults.interleaved_tables.enabled\" = true", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "true"} 0 1 {"EventType": "set_cluster_setting", "SettingName": "sql.crdb_internal.table_row_statistics.as_of_time", "Statement": "SET CLUSTER SETTING \"sql.crdb_internal.table_row_statistics.as_of_time\" = e'-1\\u00B5s'", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "-00:00:00.000001"} 0 1 {"EventType": "set_cluster_setting", "SettingName": "kv.allocator.load_based_lease_rebalancing.enabled", "Statement": "SET CLUSTER SETTING \"kv.allocator.load_based_lease_rebalancing.enabled\" = false", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "false"} 0 1 {"EventType": "set_cluster_setting", "SettingName": "kv.allocator.load_based_lease_rebalancing.enabled", "Statement": "SET CLUSTER SETTING \"kv.allocator.load_based_lease_rebalancing.enabled\" = DEFAULT", "Tag": "SET CLUSTER SETTING", "User": "root", "Value": "DEFAULT"} diff --git a/pkg/sql/logictest/testdata/logic_test/system b/pkg/sql/logictest/testdata/logic_test/system index 2b2e4b1768ac..c5f3209ac876 100644 --- a/pkg/sql/logictest/testdata/logic_test/system +++ b/pkg/sql/logictest/testdata/logic_test/system @@ -658,7 +658,6 @@ cluster.secret diagnostics.reporting.enabled kv.range_merge.queue_enabled sql.crdb_internal.table_row_statistics.as_of_time -sql.defaults.interleaved_tables.enabled sql.stats.automatic_collection.min_stale_rows version @@ -677,7 +676,6 @@ diagnostics.reporting.enabled true kv.range_merge.queue_enabled false somesetting somevalue sql.crdb_internal.table_row_statistics.as_of_time -1µs -sql.defaults.interleaved_tables.enabled true sql.stats.automatic_collection.min_stale_rows 5 user testuser diff --git a/pkg/sql/schema_changer_test.go b/pkg/sql/schema_changer_test.go index 293df2462480..2ae8c1862ba7 100644 --- a/pkg/sql/schema_changer_test.go +++ b/pkg/sql/schema_changer_test.go @@ -24,7 +24,6 @@ import ( "time" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/config/zonepb" "github.com/cockroachdb/cockroach/pkg/jobs" "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" @@ -34,7 +33,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/server" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog" @@ -7537,60 +7535,3 @@ func TestJobsWithoutMutationsAreCancelable(t *testing.T) { ).Scan(&id) require.Equal(t, scJobID, id) } - -// TestDeinterleaveRevert tests that schema changes which fail during an alter -// primary key to remove an interleave successfully reverts. -func TestDeinterleaveRevert(t *testing.T) { - defer leaktest.AfterTest(t)() - defer log.Scope(t).Close(t) - ctx := context.Background() - - // Override the version because in 21.2, interleaves are not permitted. - v21_1 := clusterversion.ByKey(clusterversion.V21_1) - settings := cluster.MakeTestingClusterSettingsWithVersions( - v21_1, v21_1, true, /* initializeVersion */ - ) - sql.InterleavedTablesEnabled.Override(ctx, &settings.SV, true) - const shortInterval = 100 * time.Millisecond - s, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{ - Settings: settings, - Knobs: base.TestingKnobs{ - JobsTestingKnobs: jobs.NewTestingKnobsWithIntervals( - shortInterval, shortInterval, time.Second, time.Hour, // retryMaxDelay - ), - DistSQL: &execinfra.TestingKnobs{ - RunBeforeBackfillChunk: func(sp roachpb.Span) error { - return errors.New("boom") - }, - }, - Server: &server.TestingKnobs{ - BinaryVersionOverride: v21_1, - DisableAutomaticVersionUpgrade: 1, - }, - }, - }) - defer s.Stopper().Stop(ctx) - tdb := sqlutils.MakeSQLRunner(sqlDB) - tdb.Exec(t, ` -CREATE TABLE parent (i INT PRIMARY KEY); -CREATE TABLE child (i INT, j INT, k INT, PRIMARY KEY (i, j)) INTERLEAVE IN PARENT "parent" (i); -INSERT INTO parent VALUES (1); -INSERT INTO child VALUES (1, 1, 1); -`) - - errCh := make(chan error) - go func() { - _, err := sqlDB.Exec(`ALTER TABLE child ALTER PRIMARY KEY USING COLUMNS (i, j)`) - errCh <- err - }() - - const errMsg = `failed to construct index entries during backfill: boom` - tdb.CheckQueryResultsRetry(t, ` -SELECT status, error - FROM crdb_internal.jobs - WHERE job_type = 'SCHEMA CHANGE' AND description LIKE '%ALTER PRIMARY KEY%'; -`, [][]string{ - {"failed", errMsg}, - }) - require.EqualError(t, <-errCh, `pq: `+errMsg) -} diff --git a/pkg/workload/sqlsmith/sqlsmith.go b/pkg/workload/sqlsmith/sqlsmith.go index e9e08aad362e..5e95daf879a8 100644 --- a/pkg/workload/sqlsmith/sqlsmith.go +++ b/pkg/workload/sqlsmith/sqlsmith.go @@ -70,16 +70,7 @@ func (*sqlSmith) Meta() workload.Meta { return sqlSmithMeta } func (g *sqlSmith) Flags() workload.Flags { return g.flags } func (g *sqlSmith) Hooks() workload.Hooks { - return workload.Hooks{ - PreCreate: func(db *gosql.DB) error { - if _, err := db.Exec(` -SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled = true; -`); err != nil { - return err - } - return nil - }, - } + return workload.Hooks{} } // Tables implements the Generator interface. From d8893d76d4ea3b510f7266c9be9690aba7ab4c41 Mon Sep 17 00:00:00 2001 From: richardjcai Date: Mon, 25 Oct 2021 21:21:14 -0400 Subject: [PATCH 041/205] sql: fix comment on constraint for tables in a schema Release note (sql change): Previously if you did comment on constraint on a table in a schema the command would succeed but the comment would not actually created. Now the comment is successfully created. --- pkg/sql/comment_on_constraint.go | 15 ++++++++------- pkg/sql/comment_on_constraint_test.go | 9 ++++++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/sql/comment_on_constraint.go b/pkg/sql/comment_on_constraint.go index ff498b3e9951..2feea4f12e21 100644 --- a/pkg/sql/comment_on_constraint.go +++ b/pkg/sql/comment_on_constraint.go @@ -17,7 +17,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/security" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemadesc" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/privilege" @@ -62,8 +61,10 @@ func (n *commentOnConstraintNode) startExec(params runParams) error { if err != nil { return err } - cSchema, ok := schemadesc.GetVirtualSchemaByID(n.tableDesc.GetParentSchemaID()) - if !ok { + schema, err := params.p.Descriptors().GetImmutableSchemaByID( + params.ctx, params.extendedEvalCtx.Txn, n.tableDesc.GetParentSchemaID(), tree.SchemaLookupFlags{}, + ) + if err != nil { return err } @@ -78,16 +79,16 @@ func (n *commentOnConstraintNode) startExec(params runParams) error { switch kind := constraint.Kind; kind { case descpb.ConstraintTypePK: constraintDesc := constraint.Index - n.oid = hasher.PrimaryKeyConstraintOid(n.tableDesc.GetParentID(), cSchema.GetName(), n.tableDesc.GetID(), constraintDesc) + n.oid = hasher.PrimaryKeyConstraintOid(n.tableDesc.GetParentID(), schema.GetName(), n.tableDesc.GetID(), constraintDesc) case descpb.ConstraintTypeFK: constraintDesc := constraint.FK - n.oid = hasher.ForeignKeyConstraintOid(n.tableDesc.GetParentID(), cSchema.GetName(), n.tableDesc.GetID(), constraintDesc) + n.oid = hasher.ForeignKeyConstraintOid(n.tableDesc.GetParentID(), schema.GetName(), n.tableDesc.GetID(), constraintDesc) case descpb.ConstraintTypeUnique: constraintDesc := constraint.Index.ID - n.oid = hasher.UniqueConstraintOid(n.tableDesc.GetParentID(), cSchema.GetName(), n.tableDesc.GetID(), constraintDesc) + n.oid = hasher.UniqueConstraintOid(n.tableDesc.GetParentID(), schema.GetName(), n.tableDesc.GetID(), constraintDesc) case descpb.ConstraintTypeCheck: constraintDesc := constraint.CheckConstraint - n.oid = hasher.CheckConstraintOid(n.tableDesc.GetParentID(), cSchema.GetName(), n.tableDesc.GetID(), constraintDesc) + n.oid = hasher.CheckConstraintOid(n.tableDesc.GetParentID(), schema.GetName(), n.tableDesc.GetID(), constraintDesc) } // Setting the comment to NULL is the diff --git a/pkg/sql/comment_on_constraint_test.go b/pkg/sql/comment_on_constraint_test.go index 9e0265db231c..4ea597c3d18e 100644 --- a/pkg/sql/comment_on_constraint_test.go +++ b/pkg/sql/comment_on_constraint_test.go @@ -33,7 +33,9 @@ func TestCommentOnConstraint(t *testing.T) { CREATE DATABASE d; SET DATABASE = d; CREATE TABLE t ( a int UNIQUE, b numeric CONSTRAINT positive_price CHECK (b > 0), c int CHECK (b > c), CONSTRAINT pkey PRIMARY KEY (a,c)); - CREATE TABLE t2 (a UUID PRIMARY KEY, b int NOT NULL REFERENCES t (a)) + CREATE TABLE t2 (a UUID PRIMARY KEY, b int NOT NULL REFERENCES t (a)); + CREATE SCHEMA s; + CREATE TABLE s.t ( a int UNIQUE, b numeric CONSTRAINT positive_price CHECK (b > 0), c int CHECK (b > c), CONSTRAINT pkey PRIMARY KEY (a,c)); `); err != nil { t.Fatal(err) } @@ -48,6 +50,11 @@ func TestCommentOnConstraint(t *testing.T) { `SELECT obj_description(oid, 'pg_constraint') FROM pg_constraint WHERE conname='t_a_key'`, gosql.NullString{String: `unique_comment`, Valid: true}, }, + { + `COMMENT ON CONSTRAINT t_a_key ON s.t IS 'unique_comment'`, + `SELECT obj_description(oid, 'pg_constraint') FROM pg_constraint WHERE conname='t_a_key'`, + gosql.NullString{String: `unique_comment`, Valid: true}, + }, { `COMMENT ON CONSTRAINT positive_price ON t IS 'check_comment'`, `SELECT obj_description(oid, 'pg_constraint') FROM pg_constraint WHERE conname='positive_price'`, From 9c4a38c75da99ff51f5fd5c6c83451c4e6a56b38 Mon Sep 17 00:00:00 2001 From: Jackson Owens Date: Tue, 26 Oct 2021 10:55:09 -0400 Subject: [PATCH 042/205] sql: skip TestSavepoints Refs: #70220 Reason: flaky test Generated by bin/skip-test. Release justification: non-production code changes Release note: None --- pkg/sql/conn_executor_savepoints_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/sql/conn_executor_savepoints_test.go b/pkg/sql/conn_executor_savepoints_test.go index 9298908f63be..fc07c4b66b43 100644 --- a/pkg/sql/conn_executor_savepoints_test.go +++ b/pkg/sql/conn_executor_savepoints_test.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/skip" "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" @@ -28,6 +29,7 @@ import ( func TestSavepoints(t *testing.T) { defer leaktest.AfterTest(t)() + skip.WithIssue(t, 70220, "flaky test") defer log.Scope(t).Close(t) ctx := context.Background() From 8bf5d1c3c4152d0e5fe70c6eb8ee81879e90f336 Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Sun, 25 Jul 2021 19:56:08 -0400 Subject: [PATCH 043/205] sql: implement a fast compressed logical plan mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a plan "gist" serializer piggy backing on the exec gen/explain factory infrastructure so that we can always know what the logical plan was and can do historical and statistical tracking. Logically its like an explain (SHAPE) but is even more stripped down. A gist is a sequence of bytes representing the flattened tree of operators and various operator specific metadata. The goal is to record every logical plan we use for every query to have historical data on which plans are used possibly linked up to statistics so we know which stats go with which logical plan. Also implement a decoder to turn the serialized plan back into a tree of explain.Node's that can be displayed using existing explain code. Currently this functionality is only exposed via a new EXPLAIN mode and via a crdb_internal "decoder" SRF. EXPLAIN (GIST) takes a query and returns a single string which is the encoded gist. crdb_internal.decode_plan_gist() takes an encoded gist string and writes out the logical plan one row per line. For performance numbers of the ExecBuild comparing a StubFactory to a PlanGistFactory wrapped around a StubFactory see the PR. Fixes: #63885 Release note (sql change): Record compressed plan gist for all queries. For example, a query like this: SELECT * FROM abc UNION SELECT * FROM abc ORDER BY b,a Produces the following plan according to EXPLAIN (SHAPE) • distinct │ distinct on: a │ └── • union all │ ├── • sort │ │ order: +b,+a │ │ │ └── • scan │ missing stats │ table: abc@primary │ spans: FULL SCAN │ └── • sort │ order: +b,+a │ └── • scan missing stats table: abc@primary spans: FULL SCAN produces the following "gist": AgFuAgAHAAAAEQFuAgAHAAAAERANAAYGAA== The "gist" can be turned back into the following plan: • distinct │ distinct on │ └── • union all │ ├── • sort │ │ order │ │ │ └── • scan │ table: abc@primary │ spans: FULL SCAN │ └── • sort │ order │ └── • scan table: abc@primary spans: FULL SCAN --- Makefile | 6 +- docs/generated/sql/functions.md | 2 + pkg/sql/distsql_spec_exec_factory.go | 3 +- pkg/sql/explain_plan.go | 146 +-- pkg/sql/faketreeeval/evalctx.go | 5 + pkg/sql/instrumentation.go | 4 + pkg/sql/opt/bench/BUILD.bazel | 1 + pkg/sql/opt/exec/execbuilder/BUILD.bazel | 1 + pkg/sql/opt/exec/execbuilder/statement.go | 34 +- .../exec/execbuilder/testdata/explain_gist | 38 + pkg/sql/opt/exec/explain/.gitignore | 1 + pkg/sql/opt/exec/explain/BUILD.bazel | 21 + pkg/sql/opt/exec/explain/emit.go | 28 +- pkg/sql/opt/exec/explain/explain_factory.go | 5 +- pkg/sql/opt/exec/explain/plan_gist_factory.go | 430 +++++++ pkg/sql/opt/exec/explain/plan_gist_test.go | 147 +++ pkg/sql/opt/exec/explain/result_columns.go | 18 +- pkg/sql/opt/exec/explain/testdata/gists | 1112 +++++++++++++++++ pkg/sql/opt/exec/explain/testdata/gists_tpce | 511 ++++++++ pkg/sql/opt/exec/factory.go | 4 +- pkg/sql/opt/optbuilder/explain.go | 3 + pkg/sql/opt/optgen/cmd/optgen/BUILD.bazel | 1 + .../opt/optgen/cmd/optgen/exec_explain_gen.go | 3 +- .../opt/optgen/cmd/optgen/exec_factory_gen.go | 7 + .../optgen/cmd/optgen/exec_plan_gist_gen.go | 199 +++ pkg/sql/opt/optgen/cmd/optgen/main.go | 22 +- .../optgen/cmd/optgen/testdata/execexplain | 7 +- .../optgen/cmd/optgen/testdata/execfactory | 7 + .../optgen/cmd/optgen/testdata/execplangist | 141 +++ pkg/sql/opt/optgen/cmd/optgen/testdata/usage | 45 +- pkg/sql/opt/testutils/opttester/BUILD.bazel | 6 + pkg/sql/opt/testutils/opttester/opt_tester.go | 23 +- pkg/sql/opt_exec_factory.go | 3 +- pkg/sql/plan_opt.go | 13 +- pkg/sql/sem/builtins/generator_builtins.go | 59 + pkg/sql/sem/tree/eval.go | 3 + pkg/sql/sem/tree/explain.go | 4 + pkg/sql/sqltelemetry/planning.go | 4 + pkg/sql/testdata/telemetry/planning | 6 + pkg/util/fast_int_set.go | 65 + pkg/util/hash.go | 5 + 41 files changed, 3012 insertions(+), 131 deletions(-) create mode 100644 pkg/sql/opt/exec/execbuilder/testdata/explain_gist create mode 100644 pkg/sql/opt/exec/explain/plan_gist_factory.go create mode 100644 pkg/sql/opt/exec/explain/plan_gist_test.go create mode 100644 pkg/sql/opt/exec/explain/testdata/gists create mode 100644 pkg/sql/opt/exec/explain/testdata/gists_tpce create mode 100644 pkg/sql/opt/optgen/cmd/optgen/exec_plan_gist_gen.go create mode 100644 pkg/sql/opt/optgen/cmd/optgen/testdata/execplangist diff --git a/Makefile b/Makefile index 3fbe841cb189..e91828bc7ce8 100644 --- a/Makefile +++ b/Makefile @@ -891,7 +891,8 @@ OPTGEN_TARGETS = \ pkg/sql/opt/rule_name.og.go \ pkg/sql/opt/rule_name_string.go \ pkg/sql/opt/exec/factory.og.go \ - pkg/sql/opt/exec/explain/explain_factory.og.go + pkg/sql/opt/exec/explain/explain_factory.og.go \ + pkg/sql/opt/exec/explain/plan_gist_factory.og.go \ # removed-files is a list of files that used to exist in the # repository that need to be explicitly cleaned up to prevent build @@ -1654,6 +1655,9 @@ pkg/sql/opt/exec/factory.og.go: $(optgen-defs) $(optgen-exec-defs) bin/optgen pkg/sql/opt/exec/explain/explain_factory.og.go: $(optgen-defs) $(optgen-exec-defs) bin/optgen optgen -out $@ execexplain $(optgen-exec-defs) +pkg/sql/opt/exec/explain/plan_gist_factory.og.go: $(optgen-defs) $(optgen-exec-defs) bin/optgen + optgen -out $@ execplangist $(optgen-exec-defs) + .PHONY: clean-c-deps clean-c-deps: rm -rf $(JEMALLOC_DIR) diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index 49577638a9e7..c91aaddfe7be 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -2533,6 +2533,8 @@ The swap_ordinate_string parameter is a 2-character string naming the ordinates convert_to(str: string, enc: string) → bytes

Encode the string str as a byte array using encoding enc. Supports encodings ‘UTF8’ and ‘LATIN1’.

+crdb_internal.decode_plan_gist(gist: string) → string

Returns rows of output similar to EXPLAIN from a gist created by EXPLAIN (GIST)

+
crdb_internal.show_create_all_schemas(database_name: string) → string

Returns rows of CREATE schema statements. The output can be used to recreate a database.’

diff --git a/pkg/sql/distsql_spec_exec_factory.go b/pkg/sql/distsql_spec_exec_factory.go index 12bc449d4182..906d27de6a83 100644 --- a/pkg/sql/distsql_spec_exec_factory.go +++ b/pkg/sql/distsql_spec_exec_factory.go @@ -806,8 +806,7 @@ func (e *distSQLSpecExecFactory) ConstructExplain( // We cannot create the explained plan in the same PlanInfrastructure with the // "outer" plan. Create a separate factory. newFactory := newDistSQLSpecExecFactory(e.planner, e.planningMode) - explainFactory := explain.NewFactory(newFactory) - plan, err := buildFn(explainFactory) + plan, err := buildFn(newFactory) // Release the resources acquired during the physical planning right away. newFactory.(*distSQLSpecExecFactory).planCtx.getCleanupFunc()() if err != nil { diff --git a/pkg/sql/explain_plan.go b/pkg/sql/explain_plan.go index 2433e3ec1a0e..08217ce58314 100644 --- a/pkg/sql/explain_plan.go +++ b/pkg/sql/explain_plan.go @@ -50,86 +50,90 @@ func (e *explainPlanNode) startExec(params runParams) error { ob := explain.NewOutputBuilder(e.flags) plan := e.plan.WrappedPlan.(*planComponents) - // Determine the "distribution" and "vectorized" values, which we will emit as - // special rows. - - // Note that we delay adding the annotation about the distribution until - // after the plan is finalized (when the physical plan is successfully - // created). - distribution := getPlanDistribution( - params.ctx, params.p, params.extendedEvalCtx.ExecCfg.NodeID, - params.extendedEvalCtx.SessionData().DistSQLMode, plan.main, - ) - - outerSubqueries := params.p.curPlan.subqueryPlans - distSQLPlanner := params.extendedEvalCtx.DistSQLPlanner - planCtx := newPlanningCtxForExplainPurposes(distSQLPlanner, params, plan.subqueryPlans, distribution) - defer func() { - planCtx.planner.curPlan.subqueryPlans = outerSubqueries - }() - physicalPlan, err := newPhysPlanForExplainPurposes(planCtx, distSQLPlanner, plan.main) - var diagramURL url.URL - var diagramJSON string - if err != nil { - if e.options.Mode == tree.ExplainDistSQL { - if len(plan.subqueryPlans) > 0 { - return errors.New("running EXPLAIN (DISTSQL) on this query is " + - "unsupported because of the presence of subqueries") - } - return err - } - ob.AddDistribution(distribution.String()) - // For regular EXPLAIN, simply skip emitting the "vectorized" information. + var rows []string + if e.options.Mode == tree.ExplainGist { + rows = []string{e.plan.Gist.String()} } else { - // There might be an issue making the physical plan, but that should not - // cause an error or panic, so swallow the error. See #40677 for example. - distSQLPlanner.finalizePlanWithRowCount(planCtx, physicalPlan, plan.mainRowCount) - ob.AddDistribution(physicalPlan.Distribution.String()) - flows := physicalPlan.GenerateFlowSpecs() - flowCtx := newFlowCtxForExplainPurposes(planCtx, params.p) - - ctxSessionData := flowCtx.EvalCtx.SessionData() - var willVectorize bool - if ctxSessionData.VectorizeMode == sessiondatapb.VectorizeOff { - willVectorize = false + // Determine the "distribution" and "vectorized" values, which we will emit as + // special rows. + + // Note that we delay adding the annotation about the distribution until + // after the plan is finalized (when the physical plan is successfully + // created). + distribution := getPlanDistribution( + params.ctx, params.p, params.extendedEvalCtx.ExecCfg.NodeID, + params.extendedEvalCtx.SessionData().DistSQLMode, plan.main, + ) + + outerSubqueries := params.p.curPlan.subqueryPlans + distSQLPlanner := params.extendedEvalCtx.DistSQLPlanner + planCtx := newPlanningCtxForExplainPurposes(distSQLPlanner, params, plan.subqueryPlans, distribution) + defer func() { + planCtx.planner.curPlan.subqueryPlans = outerSubqueries + }() + physicalPlan, err := newPhysPlanForExplainPurposes(planCtx, distSQLPlanner, plan.main) + var diagramURL url.URL + var diagramJSON string + if err != nil { + if e.options.Mode == tree.ExplainDistSQL { + if len(plan.subqueryPlans) > 0 { + return errors.New("running EXPLAIN (DISTSQL) on this query is " + + "unsupported because of the presence of subqueries") + } + return err + } + ob.AddDistribution(distribution.String()) + // For regular EXPLAIN, simply skip emitting the "vectorized" information. } else { - willVectorize = true - for _, flow := range flows { - if err := colflow.IsSupported(ctxSessionData.VectorizeMode, flow); err != nil { - willVectorize = false - break + // There might be an issue making the physical plan, but that should not + // cause an error or panic, so swallow the error. See #40677 for example. + distSQLPlanner.finalizePlanWithRowCount(planCtx, physicalPlan, plan.mainRowCount) + ob.AddDistribution(physicalPlan.Distribution.String()) + flows := physicalPlan.GenerateFlowSpecs() + flowCtx := newFlowCtxForExplainPurposes(planCtx, params.p) + + ctxSessionData := flowCtx.EvalCtx.SessionData() + var willVectorize bool + if ctxSessionData.VectorizeMode == sessiondatapb.VectorizeOff { + willVectorize = false + } else { + willVectorize = true + for _, flow := range flows { + if err := colflow.IsSupported(ctxSessionData.VectorizeMode, flow); err != nil { + willVectorize = false + break + } } } - } - ob.AddVectorized(willVectorize) + ob.AddVectorized(willVectorize) - if e.options.Mode == tree.ExplainDistSQL { - flags := execinfrapb.DiagramFlags{ - ShowInputTypes: e.options.Flags[tree.ExplainFlagTypes], - } - diagram, err := execinfrapb.GeneratePlanDiagram(params.p.stmt.String(), flows, flags) - if err != nil { - return err - } + if e.options.Mode == tree.ExplainDistSQL { + flags := execinfrapb.DiagramFlags{ + ShowInputTypes: e.options.Flags[tree.ExplainFlagTypes], + } + diagram, err := execinfrapb.GeneratePlanDiagram(params.p.stmt.String(), flows, flags) + if err != nil { + return err + } - diagramJSON, diagramURL, err = diagram.ToURL() - if err != nil { - return err + diagramJSON, diagramURL, err = diagram.ToURL() + if err != nil { + return err + } } } - } - var rows []string - if e.options.Flags[tree.ExplainFlagJSON] { - // For the JSON flag, we only want to emit the diagram JSON. - rows = []string{diagramJSON} - } else { - if err := emitExplain(ob, params.EvalContext(), params.p.ExecCfg().Codec, e.plan); err != nil { - return err - } - rows = ob.BuildStringRows() - if e.options.Mode == tree.ExplainDistSQL { - rows = append(rows, "", fmt.Sprintf("Diagram: %s", diagramURL.String())) + if e.options.Flags[tree.ExplainFlagJSON] { + // For the JSON flag, we only want to emit the diagram JSON. + rows = []string{diagramJSON} + } else { + if err := emitExplain(ob, params.EvalContext(), params.p.ExecCfg().Codec, e.plan); err != nil { + return err + } + rows = ob.BuildStringRows() + if e.options.Mode == tree.ExplainDistSQL { + rows = append(rows, "", fmt.Sprintf("Diagram: %s", diagramURL.String())) + } } } v := params.p.newContainerValuesNode(colinfo.ExplainPlanColumns, 0) diff --git a/pkg/sql/faketreeeval/evalctx.go b/pkg/sql/faketreeeval/evalctx.go index ba0ecd328098..50275c974280 100644 --- a/pkg/sql/faketreeeval/evalctx.go +++ b/pkg/sql/faketreeeval/evalctx.go @@ -232,6 +232,11 @@ func (*DummyEvalPlanner) ExternalWriteFile(ctx context.Context, uri string, cont return errors.WithStack(errEvalPlanner) } +// DecodeGist is part of the EvalPlanner interface. +func (*DummyEvalPlanner) DecodeGist(gist string) ([]string, error) { + return nil, errors.WithStack(errEvalPlanner) +} + var _ tree.EvalPlanner = &DummyEvalPlanner{} var errEvalPlanner = pgerror.New(pgcode.ScalarOperationCannotRunWithoutFullSessionContext, diff --git a/pkg/sql/instrumentation.go b/pkg/sql/instrumentation.go index d078ee6ed96e..38bd125ea23e 100644 --- a/pkg/sql/instrumentation.go +++ b/pkg/sql/instrumentation.go @@ -114,6 +114,10 @@ type instrumentationHelper struct { // regions used only on EXPLAIN ANALYZE to be displayed as top-level stat. regions []string + + // planGist is a compressed version of plan that can be converted (lossily) + // back into a logical plan or be used to get a plan hash. + planGist explain.PlanGist } // outputMode indicates how the statement output needs to be populated (for diff --git a/pkg/sql/opt/bench/BUILD.bazel b/pkg/sql/opt/bench/BUILD.bazel index eea1cc3cb06f..d9e615de5a98 100644 --- a/pkg/sql/opt/bench/BUILD.bazel +++ b/pkg/sql/opt/bench/BUILD.bazel @@ -24,6 +24,7 @@ go_test( "//pkg/sql/catalog/schemaexpr", "//pkg/sql/opt/exec", "//pkg/sql/opt/exec/execbuilder", + "//pkg/sql/opt/exec/explain", "//pkg/sql/opt/memo", "//pkg/sql/opt/optbuilder", "//pkg/sql/opt/testutils/testcat", diff --git a/pkg/sql/opt/exec/execbuilder/BUILD.bazel b/pkg/sql/opt/exec/execbuilder/BUILD.bazel index 141f7dfa8895..82fb41f257c7 100644 --- a/pkg/sql/opt/exec/execbuilder/BUILD.bazel +++ b/pkg/sql/opt/exec/execbuilder/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "//pkg/sql/opt/cat", "//pkg/sql/opt/constraint", "//pkg/sql/opt/exec", + "//pkg/sql/opt/exec/explain", "//pkg/sql/opt/memo", "//pkg/sql/opt/norm", "//pkg/sql/opt/ordering", diff --git a/pkg/sql/opt/exec/execbuilder/statement.go b/pkg/sql/opt/exec/execbuilder/statement.go index 83ef2910f1fa..c3a493758a85 100644 --- a/pkg/sql/opt/exec/execbuilder/statement.go +++ b/pkg/sql/opt/exec/execbuilder/statement.go @@ -18,6 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/opt" "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/explain" "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" "github.com/cockroachdb/cockroach/pkg/sql/opt/xform" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" @@ -119,27 +120,40 @@ func (b *Builder) buildExplainOpt(explain *memo.ExplainExpr) (execPlan, error) { return planWithColumns(node, explain.ColList), nil } -func (b *Builder) buildExplain(explain *memo.ExplainExpr) (execPlan, error) { - if explain.Options.Mode == tree.ExplainOpt { - return b.buildExplainOpt(explain) +func (b *Builder) buildExplain(explainExpr *memo.ExplainExpr) (execPlan, error) { + if explainExpr.Options.Mode == tree.ExplainOpt { + return b.buildExplainOpt(explainExpr) } node, err := b.factory.ConstructExplain( - &explain.Options, - explain.StmtType, - func(ef exec.ExplainFactory) (exec.Plan, error) { - // Create a separate builder for the explain query. + &explainExpr.Options, + explainExpr.StmtType, + func(f exec.Factory) (exec.Plan, error) { + // Create a separate builder for the explain query. buildRelational + // annotates nodes with extra information when the factory is an + // exec.ExplainFactory so it must be the outer factory and the gist + // factory must be the inner factory. + gf := explain.NewPlanGistFactory(f) + ef := explain.NewFactory(gf) + explainBld := New( - ef, b.optimizer, b.mem, b.catalog, explain.Input, b.evalCtx, b.initialAllowAutoCommit, + ef, b.optimizer, b.mem, b.catalog, explainExpr.Input, b.evalCtx, b.initialAllowAutoCommit, ) explainBld.disableTelemetry = true - return explainBld.Build() + plan, err := explainBld.Build() + if err != nil { + return nil, err + } + explainPlan := plan.(*explain.Plan) + explainPlan.Gist = gf.PlanGist() + return plan, nil }, ) if err != nil { return execPlan{}, err } - return planWithColumns(node, explain.ColList), nil + + return planWithColumns(node, explainExpr.ColList), nil } func (b *Builder) buildShowTrace(show *memo.ShowTraceForSessionExpr) (execPlan, error) { diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain_gist b/pkg/sql/opt/exec/execbuilder/testdata/explain_gist new file mode 100644 index 000000000000..2791f9f813eb --- /dev/null +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain_gist @@ -0,0 +1,38 @@ + +statement ok +CREATE TABLE t (a INT PRIMARY KEY) + +let $gist +EXPLAIN (GIST) SELECT * FROM t + +query T +SELECT * FROM crdb_internal.decode_plan_gist('$gist') +---- +• scan + table: t@t_pkey + spans: FULL SCAN + +query T +SELECT crdb_internal.decode_plan_gist('$gist') +---- +• scan + table: t@t_pkey + spans: FULL SCAN + +statement error pq: unknown signature: crdb_internal\.decode_plan_gist\(int\) +SELECT * FROM crdb_internal.decode_plan_gist(10) + +statement error pq: unknown signature: crdb_internal\.decode_plan_gist\(int\) +SELECT crdb_internal.decode_plan_gist(10) + +statement error pq: illegal base64 data at input byte 0 +SELECT crdb_internal.decode_plan_gist('a') + +# ConstructOpaque +let $gist +EXPLAIN (GIST) ALTER TABLE t SCATTER FROM (0) TO (1) + +query T +SELECT crdb_internal.decode_plan_gist('$gist') +---- +• diff --git a/pkg/sql/opt/exec/explain/.gitignore b/pkg/sql/opt/exec/explain/.gitignore index 338716dec5c3..9e31fb577864 100644 --- a/pkg/sql/opt/exec/explain/.gitignore +++ b/pkg/sql/opt/exec/explain/.gitignore @@ -1 +1,2 @@ explain_factory.og.go +plan_gist_factory.og.go \ No newline at end of file diff --git a/pkg/sql/opt/exec/explain/BUILD.bazel b/pkg/sql/opt/exec/explain/BUILD.bazel index 37f88a75f1c4..d04daf1c3240 100644 --- a/pkg/sql/opt/exec/explain/BUILD.bazel +++ b/pkg/sql/opt/exec/explain/BUILD.bazel @@ -7,8 +7,10 @@ go_library( "explain_factory.go", "flags.go", "output.go", + "plan_gist_factory.go", "result_columns.go", ":gen-explain-factory", # keep + ":gen-gist-factory", # keep ], importpath = "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/explain", visibility = ["//visibility:public"], @@ -42,6 +44,7 @@ go_test( "explain_factory_test.go", "main_test.go", "output_test.go", + "plan_gist_test.go", ], data = glob(["testdata/**"]), embed = [":explain"], @@ -50,7 +53,11 @@ go_test( "//pkg/server", "//pkg/sql/catalog/colinfo", "//pkg/sql/execinfra", + "//pkg/sql/opt/cat", "//pkg/sql/opt/exec", + "//pkg/sql/opt/memo", + "//pkg/sql/opt/testutils/opttester", + "//pkg/sql/opt/testutils/testcat", "//pkg/sql/sem/tree", "//pkg/sql/types", "//pkg/testutils/serverutils", @@ -78,3 +85,17 @@ genrule( """, exec_tools = ["//pkg/sql/opt/optgen/cmd/optgen"], ) + +# Define a generator for gist factory code. +genrule( + name = "gen-gist-factory", + srcs = [ + "//pkg/sql/opt:ops", + "//pkg/sql/opt/exec:defs", + ], + outs = ["plan_gist_factory.og.go"], + cmd = """ + $(location //pkg/sql/opt/optgen/cmd/optgen) -out $@ execplangist $(locations //pkg/sql/opt/exec:defs) + """, + exec_tools = ["//pkg/sql/opt/optgen/cmd/optgen"], +) diff --git a/pkg/sql/opt/exec/explain/emit.go b/pkg/sql/opt/exec/explain/emit.go index e7ba86f05511..5abd834dab32 100644 --- a/pkg/sql/opt/exec/explain/emit.go +++ b/pkg/sql/opt/exec/explain/emit.go @@ -179,6 +179,9 @@ func (e *emitter) nodeName(n *Node) (string, error) { switch n.op { case scanOp: a := n.args.(*scanArgs) + if a.Table == nil { + return "unknown table", nil + } if a.Table.IsVirtualTable() { return "virtual table", nil } @@ -242,6 +245,9 @@ func (e *emitter) nodeName(n *Node) (string, error) { case opaqueOp: a := n.args.(*opaqueArgs) + if a.Metadata == nil { + return "", nil + } return strings.ToLower(a.Metadata.String()), nil } @@ -457,7 +463,7 @@ func (e *emitter) emitNodeAttributes(n *Node) error { a := n.args.(*scanArgs) e.emitTableAndIndex("table", a.Table, a.Index) // Omit spans for virtual tables, unless we actually have a constraint. - if !(a.Table.IsVirtualTable() && a.Params.IndexConstraint == nil) { + if a.Table != nil && !(a.Table.IsVirtualTable() && a.Params.IndexConstraint == nil) { e.emitSpans("spans", a.Table, a.Index, a.Params) } @@ -507,7 +513,9 @@ func (e *emitter) emitNodeAttributes(n *Node) error { case topKOp: a := n.args.(*topKArgs) ob.Attr("order", colinfo.ColumnOrdering(a.Ordering).String(n.Columns())) - ob.Attr("k", a.K) + if a.K > 0 { + ob.Attr("k", a.K) + } case unionAllOp: a := n.args.(*unionAllArgs) @@ -521,7 +529,11 @@ func (e *emitter) emitNodeAttributes(n *Node) error { cols := make([]string, len(a.KeyCols)) inputCols := a.Input.Columns() for i, c := range a.KeyCols { - cols[i] = inputCols[c].Name + if len(inputCols) > int(c) { + cols[i] = inputCols[c].Name + } else { + cols[i] = "_" + } } ob.VAttr("key columns", strings.Join(cols, ", ")) @@ -829,6 +841,10 @@ func (e *emitter) emitNodeAttributes(n *Node) error { } func (e *emitter) emitTableAndIndex(field string, table cat.Table, index cat.Index) { + if table == nil || index == nil { + e.ob.Attr(field, "?@?") + return + } partial := "" if _, isPartial := index.Predicate(); isPartial { partial = " (partial index)" @@ -975,7 +991,11 @@ func printColumnList(inputCols colinfo.ResultColumns, cols []exec.NodeColumnOrdi if i > 0 { buf.WriteString(", ") } - buf.WriteString(inputCols[col].Name) + if len(inputCols) > 0 && len(inputCols[col].Name) > 0 { + buf.WriteString(inputCols[col].Name) + } else { + buf.WriteString("_") + } } return buf.String() } diff --git a/pkg/sql/opt/exec/explain/explain_factory.go b/pkg/sql/opt/exec/explain/explain_factory.go index 3c798cabff10..c6b3fda95c87 100644 --- a/pkg/sql/opt/exec/explain/explain_factory.go +++ b/pkg/sql/opt/exec/explain/explain_factory.go @@ -30,7 +30,6 @@ var _ exec.ExplainFactory = &Factory{} // factory method and provides access to the corresponding exec.Node (produced // by the wrapped factory). type Node struct { - f *Factory op execOperator args interface{} columns colinfo.ResultColumns @@ -80,7 +79,7 @@ func (n *Node) Annotate(id exec.ExplainAnnotationID, value interface{}) { n.annotations[id] = value } -func (f *Factory) newNode( +func newNode( op execOperator, args interface{}, ordering exec.OutputOrdering, children ...*Node, ) (*Node, error) { inputNodeCols := make([]colinfo.ResultColumns, len(children)) @@ -92,7 +91,6 @@ func (f *Factory) newNode( return nil, err } return &Node{ - f: f, op: op, args: args, columns: columns, @@ -109,6 +107,7 @@ type Plan struct { Cascades []exec.Cascade Checks []*Node WrappedPlan exec.Plan + Gist PlanGist } var _ exec.Plan = &Plan{} diff --git a/pkg/sql/opt/exec/explain/plan_gist_factory.go b/pkg/sql/opt/exec/explain/plan_gist_factory.go new file mode 100644 index 000000000000..93b2b916f70b --- /dev/null +++ b/pkg/sql/opt/exec/explain/plan_gist_factory.go @@ -0,0 +1,430 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package explain + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/binary" + "io" + + "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/inverted" + "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" + "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/util" + "github.com/cockroachdb/cockroach/pkg/util/errorutil" + "github.com/cockroachdb/errors" +) + +func init() { + if numOperators != 57 { + // If this error occurs please make sure the new op is the last one in order + // to not invalidate existing plan gists/hashes. If we are just adding an + // operator at the end there's no need to update version below and we can + // just bump the hardcoded literal here. + panic(errors.AssertionFailedf("Operator field changed (%d), please update check and consider incrementing version", numOperators)) + } +} + +// version tracks major changes to how we encode plans or to the operator set. +// It isn't necessary to increment it when adding a single operator but if we +// remove an operator or change the operator set or decide to use a more +// efficient encoding version should be incremented. +var version = 1 + +// PlanGist is a compact representation of a logical plan meant to be used as +// a key and log for different plans used to implement a particular query. A +// gist doesn't change for the following: +// +// - literal constant values +// - alias names +// - grouping column names +// - constraint values +// - estimated rows stats +// +// The notion is that the gist is the rough shape of the plan that represents +// the way the plan operators are put together and what tables and indexes they +// use. +type PlanGist struct { + gist string + hash uint64 +} + +// String returns the gist bytes as a base64 encoded string. +func (fp PlanGist) String() string { + return fp.gist +} + +// Hash returns a 64 bit hash of the gist. Note that this is symbolically stable +// across table/index ids, i.e. indexes from two different databases with +// different ids but the same name will have the same hash. +func (fp PlanGist) Hash() uint64 { + return fp.hash +} + +// PlanGistFactory is an exec.Factory that produces a gist by eaves +// dropping on the exec builder phase of compilation. +type PlanGistFactory struct { + // PlanGistFactory implements a writer interface in order to write to the + // gist buffer and maintain a hash. io.MultiWriter could work but this is more + // efficient. + io.Writer + + wrappedFactory exec.Factory + // buffer is used for reading and writing (i.e. decoding and encoding) but on + // on the write path it is used via the writer field which is a multi-writer + // writing to the buffer and to the hash. The exception is when we're dealing + // with ids where we will write the id to the buffer and the "string" to the + // hash. This allows the hash to be id agnostic (ie hash's will be stable + // across plans from different databases with different DDL history). + buffer bytes.Buffer + hash util.FNV64 + + nodeStack []*Node + catalog cat.Catalog +} + +var _ exec.Factory = &PlanGistFactory{} +var _ io.Writer = &PlanGistFactory{} + +func (f *PlanGistFactory) Write(data []byte) (int, error) { + i, err := f.buffer.Write(data) + f.writeHash(data) + return i, err +} + +func (f *PlanGistFactory) writeHash(data []byte) { + for _, b := range data { + f.hash.Add(uint64(b)) + } +} + +// NewPlanGistFactory creates a new PlanGistFactory. +func NewPlanGistFactory(wrappedFactory exec.Factory) *PlanGistFactory { + f := new(PlanGistFactory) + f.wrappedFactory = wrappedFactory + f.hash.Init() + f.encodeInt(version) + return f +} + +// ConstructPlan delegates to the wrapped factory. +func (f *PlanGistFactory) ConstructPlan( + root exec.Node, + subqueries []exec.Subquery, + cascades []exec.Cascade, + checks []exec.Node, + rootRowCount int64, +) (exec.Plan, error) { + plan, err := f.wrappedFactory.ConstructPlan(root, subqueries, cascades, checks, rootRowCount) + return plan, err +} + +// PlanGist returns a pointer to a PlanGist. +func (f *PlanGistFactory) PlanGist() PlanGist { + return PlanGist{gist: base64.StdEncoding.EncodeToString(f.buffer.Bytes()), + hash: f.hash.Sum()} +} + +// DecodePlanGistToRows converts a gist to a logical plan and returns the rows. +func DecodePlanGistToRows(gist string, catalog cat.Catalog) ([]string, error) { + flags := Flags{HideValues: true, Redact: RedactAll} + ob := NewOutputBuilder(flags) + explainPlan, err := DecodePlanGistToPlan(gist, catalog) + if err != nil { + return nil, err + } + err = Emit(explainPlan, ob, func(table cat.Table, index cat.Index, scanParams exec.ScanParams) string { return "" }) + if err != nil { + return nil, err + } + return ob.BuildStringRows(), nil +} + +// DecodePlanGistToPlan constructs an explain.Node tree from a gist. +func DecodePlanGistToPlan(s string, cat cat.Catalog) (plan *Plan, retErr error) { + f := NewPlanGistFactory(exec.StubFactory{}) + f.catalog = cat + bytes, err := base64.StdEncoding.DecodeString(s) + + if err != nil { + return nil, err + } + + // Clear out buffer which will have version in it from NewPlanGistFactory. + f.buffer.Reset() + f.buffer.Write(bytes) + plan = &Plan{} + + defer func() { + if r := recover(); r != nil { + // This code allows us to propagate internal errors without having to add + // error checks everywhere throughout the code. This is only possible + // because the code does not update shared state and does not manipulate + // locks. + if ok, e := errorutil.ShouldCatch(r); ok { + retErr = e + } else { + // Other panic objects can't be considered "safe" and thus are + // propagated as crashes that terminate the session. + panic(r) + } + } + }() + + ver := f.decodeInt() + if ver != version { + return nil, errors.Errorf("unsupported old plan gist version %d", ver) + } + + for { + op := f.decodeOp() + if op == unknownOp { + break + } + switch op { + case errorIfRowsOp: + plan.Checks = append(plan.Checks, f.popChild()) + } + } + + plan.Root = f.popChild() + + for _, n := range f.nodeStack { + subquery := exec.Subquery{ + Root: n, + } + plan.Subqueries = append(plan.Subqueries, subquery) + } + + return plan, nil +} + +func (f *PlanGistFactory) decodeOp() execOperator { + val, err := f.buffer.ReadByte() + if err != nil || val == 0 { + return unknownOp + } + n, err := f.decodeOperatorBody(execOperator(val)) + if err != nil { + panic(err) + } + f.nodeStack = append(f.nodeStack, n) + + return n.op +} + +func (f *PlanGistFactory) popChild() *Node { + l := len(f.nodeStack) + n := f.nodeStack[l-1] + f.nodeStack = f.nodeStack[:l-1] + + return n +} + +func (f *PlanGistFactory) encodeOperator(op execOperator) { + f.encodeByte(byte(op)) +} + +func (f *PlanGistFactory) encodeInt(i int) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], int64(i)) + _, err := f.Write(buf[:n]) + if err != nil { + panic(err) + } +} + +func (f *PlanGistFactory) decodeInt() int { + val, err := binary.ReadVarint(&f.buffer) + if err != nil { + panic(err) + } + + return int(val) +} + +// encodeDataSource encodes tables and indexes and does a numeric id based +// encoding to the gist and a symbolic encoding to the hash. +func (f *PlanGistFactory) encodeDataSource(id cat.StableID, name tree.Name) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], int64(id)) + _, err := f.buffer.Write(buf[:n]) + if err != nil { + panic(err) + } + f.writeHash([]byte(string(name))) +} + +func (f *PlanGistFactory) encodeID(id cat.StableID) { + f.encodeInt(int(id)) +} + +func (f *PlanGistFactory) decodeID() cat.StableID { + return cat.StableID(f.decodeInt()) +} + +func (f *PlanGistFactory) decodeTable() cat.Table { + id := f.decodeID() + ds, _, err := f.catalog.ResolveDataSourceByID(context.TODO(), cat.Flags{}, id) + // If we can't resolve the id just return nil, this will result in the plan + // showing "unknown table". + if err != nil { + return nil + } + + return ds.(cat.Table) +} + +func (f *PlanGistFactory) decodeIndex(tbl cat.Table) cat.Index { + id := f.decodeID() + // If tbl is null just return nil after consuming the id, plan will display + // "unknown table". + if tbl == nil { + return nil + } + for i, n := 0, tbl.IndexCount(); i < n; i++ { + if tbl.Index(i).ID() == id { + return tbl.Index(i) + } + } + + return nil +} + +// TODO: implement this and figure out how to test... +func (f *PlanGistFactory) decodeSchema() cat.Schema { + id := f.decodeID() + _ = id + return nil +} + +func (f *PlanGistFactory) encodeNodeColumnOrdinals(vals []exec.NodeColumnOrdinal) { + f.encodeInt(len(vals)) +} + +func (f *PlanGistFactory) decodeNodeColumnOrdinals() []exec.NodeColumnOrdinal { + l := f.decodeInt() + vals := make([]exec.NodeColumnOrdinal, l) + return vals +} + +func (f *PlanGistFactory) encodeResultColumns(vals colinfo.ResultColumns) { + f.encodeInt(len(vals)) +} + +func (f *PlanGistFactory) decodeResultColumns() colinfo.ResultColumns { + numCols := f.decodeInt() + return make(colinfo.ResultColumns, numCols) +} + +func (f *PlanGistFactory) encodeByte(b byte) { + _, err := f.Write([]byte{b}) + if err != nil { + panic(err) + } +} + +func (f *PlanGistFactory) decodeByte() byte { + val, err := f.buffer.ReadByte() + if err != nil { + panic(err) + } + return val +} + +func (f *PlanGistFactory) decodeJoinType() descpb.JoinType { + val := f.decodeByte() + return descpb.JoinType(val) +} + +func (f *PlanGistFactory) encodeBool(b bool) { + if b { + f.encodeByte(1) + } else { + f.encodeByte(0) + } +} + +func (f *PlanGistFactory) decodeBool() bool { + val := f.decodeByte() + return val != 0 +} + +// TODO: enable this or remove it... +func (f *PlanGistFactory) encodeColumnOrdering(cols colinfo.ColumnOrdering) { +} + +func (f *PlanGistFactory) decodeColumnOrdering() colinfo.ColumnOrdering { + return nil +} + +func (f *PlanGistFactory) encodeScanParams(params exec.ScanParams) { + err := params.NeededCols.Encode(f) + if err != nil { + panic(err) + } + + if params.IndexConstraint != nil { + f.encodeInt(params.IndexConstraint.Spans.Count()) + } else { + f.encodeInt(0) + } + + if params.InvertedConstraint != nil { + f.encodeInt(params.InvertedConstraint.Len()) + } else { + f.encodeInt(0) + } + + f.encodeInt(int(params.HardLimit)) +} + +func (f *PlanGistFactory) decodeScanParams() exec.ScanParams { + neededCols := util.FastIntSet{} + err := neededCols.Decode(&f.buffer) + if err != nil { + panic(err) + } + + var idxConstraint *constraint.Constraint + l := f.decodeInt() + if l > 0 { + idxConstraint = new(constraint.Constraint) + idxConstraint.Spans.Alloc(l) + var sp constraint.Span + idxConstraint.Spans.Append(&sp) + } + + var invertedConstraint inverted.Spans + l = f.decodeInt() + if l > 0 { + invertedConstraint = make([]inverted.Span, l) + } + + hardLimit := f.decodeInt() + + return exec.ScanParams{NeededCols: neededCols, IndexConstraint: idxConstraint, InvertedConstraint: invertedConstraint, HardLimit: int64(hardLimit)} +} + +func (f *PlanGistFactory) encodeRows(rows [][]tree.TypedExpr) { + f.encodeInt(len(rows)) +} + +func (f *PlanGistFactory) decodeRows() [][]tree.TypedExpr { + numRows := f.decodeInt() + return make([][]tree.TypedExpr, numRows) +} diff --git a/pkg/sql/opt/exec/explain/plan_gist_test.go b/pkg/sql/opt/exec/explain/plan_gist_test.go new file mode 100644 index 000000000000..a30533b07881 --- /dev/null +++ b/pkg/sql/opt/exec/explain/plan_gist_test.go @@ -0,0 +1,147 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package explain_test + +import ( + "fmt" + "testing" + + "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/explain" + "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" + "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/opttester" + "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat" + "github.com/cockroachdb/datadriven" +) + +func makeGist(ot *opttester.OptTester, t *testing.T) explain.PlanGist { + f := explain.NewPlanGistFactory(exec.StubFactory{}) + expr, err := ot.Optimize() + if err != nil { + t.Error(err) + } + var mem *memo.Memo + if rel, ok := expr.(memo.RelExpr); ok { + mem = rel.Memo() + } + _, err = ot.ExecBuild(f, mem, expr) + if err != nil { + t.Error(err) + } + return f.PlanGist() +} + +func explainGist(gist string, catalog cat.Catalog) string { + flags := explain.Flags{HideValues: true, Redact: explain.RedactAll} + ob := explain.NewOutputBuilder(flags) + explainPlan, err := explain.DecodePlanGistToPlan(gist, catalog) + if err != nil { + panic(err) + } + err = explain.Emit(explainPlan, ob, func(table cat.Table, index cat.Index, scanParams exec.ScanParams) string { return "" }) + if err != nil { + panic(err) + } + return ob.BuildString() +} + +func plan(ot *opttester.OptTester, t *testing.T) string { + f := explain.NewFactory(exec.StubFactory{}) + expr, err := ot.Optimize() + if err != nil { + t.Error(err) + } + if expr == nil { + t.Error("Optimize failed, use a logictest instead?") + } + var mem *memo.Memo + if rel, ok := expr.(memo.RelExpr); ok { + mem = rel.Memo() + } + explainPlan, err := ot.ExecBuild(f, mem, expr) + if err != nil { + t.Error(err) + } + if explainPlan == nil { + t.Error("Couldn't ExecBuild memo, use a logictest instead?") + } + flags := explain.Flags{HideValues: true, Redact: explain.RedactAll} + ob := explain.NewOutputBuilder(flags) + err = explain.Emit(explainPlan.(*explain.Plan), ob, func(table cat.Table, index cat.Index, scanParams exec.ScanParams) string { return "" }) + if err != nil { + t.Error(err) + } + str := ob.BuildString() + fmt.Printf("%s\n", str) + return str +} + +func TestPlanGistBuilder(t *testing.T) { + catalog := testcat.New() + testGists := func(t *testing.T, d *datadriven.TestData) string { + ot := opttester.New(catalog, d.Input) + + for _, a := range d.CmdArgs { + if err := ot.Flags.Set(a); err != nil { + d.Fatalf(t, "%+v", err) + } + } + switch d.Cmd { + case "gist-explain-roundtrip": + plan := plan(ot, t) + pg := makeGist(ot, t) + fmt.Printf("%s\n", d.Input) + pgplan := explainGist(pg.String(), catalog) + return fmt.Sprintf("hash: %d\nplan-gist: %s\nexplain(shape):\n%sexplain(gist):\n%s", pg.Hash(), pg.String(), plan, pgplan) + case "plan-gist": + return fmt.Sprintf("%s\n", makeGist(ot, t).String()) + // Take gist string and display plan + case "explain-plan-gist": + return explainGist(d.Input, catalog) + case "plan": + return plan(ot, t) + case "hash": + return fmt.Sprintf("%d\n", makeGist(ot, t).Hash()) + default: + return ot.RunCommand(t, d) + } + } + // RFC: should I move this to opt_tester? + datadriven.RunTest(t, "testdata/gists", testGists) + datadriven.RunTest(t, "testdata/gists_tpce", testGists) +} + +func TestPlanGistHashEquivalency(t *testing.T) { + catalog := testcat.New() + _, err := catalog.ExecuteDDL("CREATE TABLE foo (x int);") + if err != nil { + t.Error(err) + } + ot := opttester.New(catalog, "SELECT * FROM foo;") + gist1 := makeGist(ot, t) + _, err = catalog.ExecuteDDL("DROP TABLE foo;") + if err != nil { + t.Error(err) + } + _, err = catalog.ExecuteDDL("CREATE TABLE foo (x int);") + if err != nil { + t.Error(err) + } + gist2 := makeGist(ot, t) + + if gist1.Hash() != gist2.Hash() { + t.Errorf("gist hashes should be identical! %d != %d", gist1.Hash(), gist2.Hash()) + } + if gist1.String() == gist2.String() { + t.Errorf("gists should be different! %s == %s", gist1.String(), gist2.String()) + } +} diff --git a/pkg/sql/opt/exec/explain/result_columns.go b/pkg/sql/opt/exec/explain/result_columns.go index 88eac1b64ff1..1945780a9fd0 100644 --- a/pkg/sql/opt/exec/explain/result_columns.go +++ b/pkg/sql/opt/exec/explain/result_columns.go @@ -118,6 +118,10 @@ func getResultColumns( case scanBufferOp: a := args.(*scanBufferArgs) + // TODO: instead of nil check can we put in a fake value? + if a.Ref == nil { + return nil, nil + } return a.Ref.Columns(), nil case insertOp: @@ -144,7 +148,10 @@ func getResultColumns( return tableColumns(a.Table, a.ReturnCols), nil case opaqueOp: - return args.(*opaqueArgs).Metadata.Columns(), nil + if args.(*opaqueArgs).Metadata != nil { + return args.(*opaqueArgs).Metadata.Columns(), nil + } + return nil, nil case alterTableSplitOp: return colinfo.AlterTableSplitColumns, nil @@ -212,6 +219,9 @@ func projectCols( ) colinfo.ResultColumns { columns := make(colinfo.ResultColumns, len(ordinals)) for i, ord := range ordinals { + if int(ord) >= len(input) { + continue + } columns[i] = input[ord] if colNames != nil { columns[i].Name = colNames[i] @@ -224,8 +234,10 @@ func groupByColumns( inputCols colinfo.ResultColumns, groupCols []exec.NodeColumnOrdinal, aggregations []exec.AggInfo, ) colinfo.ResultColumns { columns := make(colinfo.ResultColumns, 0, len(groupCols)+len(aggregations)) - for _, col := range groupCols { - columns = append(columns, inputCols[col]) + if inputCols != nil { + for _, col := range groupCols { + columns = append(columns, inputCols[col]) + } } for _, agg := range aggregations { columns = append(columns, colinfo.ResultColumn{ diff --git a/pkg/sql/opt/exec/explain/testdata/gists b/pkg/sql/opt/exec/explain/testdata/gists new file mode 100644 index 000000000000..b2503fc51cd2 --- /dev/null +++ b/pkg/sql/opt/exec/explain/testdata/gists @@ -0,0 +1,1112 @@ + +exec-ddl +CREATE TABLE foo (a INT PRIMARY KEY, b INT[], c STRING, INVERTED INDEX b_inverted_index(b), UNIQUE INDEX c_idx(c)) +---- + +exec-ddl +CREATE TABLE bar (ba INT PRIMARY KEY) +---- + +exec-ddl +CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT, INDEX(b), INDEX(c)) +---- + +exec-ddl +CREATE TABLE xyz (x INT PRIMARY KEY, y INT, z INT) +---- + +# ConstructScan/ConstructSerializingProject +gist-explain-roundtrip +SELECT * from foo LIMIT 1 +---- +hash: 12007264394566425707 +plan-gist: AgFqAgAHAAACBgY= +explain(shape): +• scan + missing stats + table: foo@foo_pkey + spans: LIMITED SCAN + limit: 1 +explain(gist): +• scan + table: foo@foo_pkey + spans: LIMITED SCAN + limit: 1 + +# ConstructFilter +gist-explain-roundtrip +SELECT * from foo WHERE a = 1 +---- +hash: 16614037866652733243 +plan-gist: AgFqAgAHAgAABgY= +explain(shape): +• scan + missing stats + table: foo@foo_pkey + spans: 1 span +explain(gist): +• scan + table: foo@foo_pkey + spans: 1 span + +# ConstructInvertedFilter/ConstructIndexJoin +gist-explain-roundtrip +SELECT * from foo WHERE b @> ARRAY[1] +---- +hash: 6532565644912077743 +plan-gist: AgFqBAABAAIAE2oCBgY= +explain(shape): +• index join +│ table: foo@foo_pkey +│ +└── • scan + missing stats + table: foo@b_inverted_index + spans: 1 span +explain(gist): +• index join +│ table: foo@foo_pkey +│ +└── • scan + table: foo@b_inverted_index + spans: 1 span + +# ConstructSimpleProjectOp +gist-explain-roundtrip +select a,b from foo@c_idx where c = 'bar' +---- +hash: 10892661656543179042 +plan-gist: AgFqBgAFAgAAE2oCBQQGBA== +explain(shape): +• index join +│ table: foo@foo_pkey +│ +└── • scan + missing stats + table: foo@c_idx + spans: 1 span +explain(gist): +• index join +│ table: foo@foo_pkey +│ +└── • scan + table: foo@c_idx + spans: 1 span + +# ConstructRender +gist-explain-roundtrip +select a + 1 from foo +---- +hash: 17636707451215040371 +plan-gist: AgFqBgABAAAABwIGAg== +explain(shape): +• render +│ +└── • scan + missing stats + table: foo@c_idx + spans: FULL SCAN +explain(gist): +• render +│ +└── • scan + table: foo@c_idx + spans: FULL SCAN + +# ConstructApplyJoin +gist-explain-roundtrip +SELECT * FROM abc WHERE EXISTS(SELECT * FROM (VALUES (a), (b)) WHERE column1=a) +---- +hash: 2592820622583619685 +plan-gist: AgFuAgAHAAAACAQCBgY= +explain(shape): +• apply join (semi) +│ pred: column1 = a +│ +└── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• apply join (semi) +│ +└── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructHashJoin +gist-explain-roundtrip +SELECT * FROM foo INNER HASH JOIN bar ON a = ba +---- +hash: 1156976949248385142 +plan-gist: AgFqAgAHAAAAAWwCAAEAAAAJAAICAQEGCA== +explain(shape): +• hash join +│ equality: (a) = (ba) +│ left cols are key +│ right cols are key +│ +├── • scan +│ missing stats +│ table: foo@foo_pkey +│ spans: FULL SCAN +│ +└── • scan + missing stats + table: bar@bar_pkey + spans: FULL SCAN +explain(gist): +• hash join +│ equality: (a) = (ba) +│ left cols are key +│ right cols are key +│ +├── • scan +│ table: foo@foo_pkey +│ spans: FULL SCAN +│ +└── • scan + table: bar@bar_pkey + spans: FULL SCAN + +# ConstructMergeJoin +gist-explain-roundtrip +SELECT * FROM foo JOIN bar ON a = ba +---- +hash: 244718973694736415 +plan-gist: AgFqAgAHAAAAAWwCAAEAAAAKAAEBBgg= +explain(shape): +• merge join +│ equality: (a) = (ba) +│ left cols are key +│ right cols are key +│ +├── • scan +│ missing stats +│ table: foo@foo_pkey +│ spans: FULL SCAN +│ +└── • scan + missing stats + table: bar@bar_pkey + spans: FULL SCAN +explain(gist): +• merge join +│ +├── • scan +│ table: foo@foo_pkey +│ spans: FULL SCAN +│ +└── • scan + table: bar@bar_pkey + spans: FULL SCAN + +# ConstructGroupBy +gist-explain-roundtrip +SELECT c, count(*) FROM foo, bar WHERE a = 1 GROUP BY c +---- +hash: 6889256940365688364 +plan-gist: AgFsAgAAAAAAAWoCAAUCAAAJAAAAAAEFAgsABgQ= +explain(shape): +• group +│ +└── • cross join + │ + ├── • scan + │ missing stats + │ table: bar@bar_pkey + │ spans: FULL SCAN + │ + └── • scan + missing stats + table: foo@foo_pkey + spans: 1 span +explain(gist): +• group +│ +└── • cross join + │ + ├── • scan + │ table: bar@bar_pkey + │ spans: FULL SCAN + │ + └── • scan + table: foo@foo_pkey + spans: 1 span + +# ConstructScalarGroupBy +gist-explain-roundtrip +SELECT sum(a),max(c) FROM foo +---- +hash: 4607100648356393974 +plan-gist: AgFqBgAFAAAADAYE +explain(shape): +• group (scalar) +│ +└── • scan + missing stats + table: foo@c_idx + spans: FULL SCAN +explain(gist): +• group (scalar) +│ +└── • scan + table: foo@c_idx + spans: FULL SCAN + +# ConstructInsert +gist-explain-roundtrip +INSERT INTO foo VALUES (1,ARRAY[1,2],'str') +---- +hash: 10922761883291699574 +plan-gist: AiACagE= +explain(shape): +• insert fast path + into: foo(a, b, c) + auto commit + size: 3 columns, 1 row +explain(gist): +• insert fast path + into: foo() + auto commit + size: 0 columns, 1 row + +# ConstructDistinct +gist-explain-roundtrip +SELECT DISTINCT ON (b,c) b,c from abc +---- +hash: 8133419258077586677 +plan-gist: AgFuAgAGAAAADQYE +explain(shape): +• distinct +│ distinct on: b, c +│ +└── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• distinct +│ distinct on +│ +└── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructHashSetOpOp +gist-explain-roundtrip +SELECT * FROM abc INTERSECT ALL SELECT * FROM abc +---- +hash: 10183959987953600009 +plan-gist: AgFuAgAHAAAAAW4CAAcAAAAPAQYG +explain(shape): +• intersect all +│ +├── • scan +│ missing stats +│ table: abc@abc_pkey +│ spans: FULL SCAN +│ +└── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• union all +│ +├── • scan +│ table: abc@abc_pkey +│ spans: FULL SCAN +│ +└── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructStreamingSetOpOp +gist-explain-roundtrip +SELECT * FROM abc UNION SELECT * FROM abc ORDER BY b,a +---- +hash: 15661179085895519156 +plan-gist: AgFuAgAHAAAAEQFuAgAHAAAAERANBgY= +explain(shape): +• distinct +│ distinct on: a +│ +└── • union all + │ + ├── • sort + │ │ order: +b,+a + │ │ + │ └── • scan + │ missing stats + │ table: abc@abc_pkey + │ spans: FULL SCAN + │ + └── • sort + │ order: +b,+a + │ + └── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• distinct +│ distinct on +│ +└── • union all + │ + ├── • sort + │ │ order + │ │ + │ └── • scan + │ table: abc@abc_pkey + │ spans: FULL SCAN + │ + └── • sort + │ order + │ + └── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructUnionAllOp +gist-explain-roundtrip +SELECT * FROM abc UNION ALL SELECT * FROM abc +---- +hash: 13533026552697659671 +plan-gist: AgFuAgAHAAAAAW4CAAcAAAAQBgY= +explain(shape): +• union all +│ +├── • scan +│ missing stats +│ table: abc@abc_pkey +│ spans: FULL SCAN +│ +└── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• union all +│ +├── • scan +│ table: abc@abc_pkey +│ spans: FULL SCAN +│ +└── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructOrdinality +gist-explain-roundtrip +SELECT * FROM abc WITH ORDINALITY +---- +hash: 5582638262284834071 +plan-gist: AgFuAgAHAAAAEgYI +explain(shape): +• ordinality +│ +└── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• ordinality +│ +└── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructLookupJoin +gist-explain-roundtrip +SELECT * FROM foo INNER LOOKUP JOIN bar ON a = ba +---- +hash: 2902055674207466370 +plan-gist: AgFqAgAHAAAAFABsAgIBBgg= +explain(shape): +• lookup join +│ table: bar@bar_pkey +│ equality: (a) = (ba) +│ equality cols are key +│ +└── • scan + missing stats + table: foo@foo_pkey + spans: FULL SCAN +explain(gist): +• lookup join +│ table: bar@bar_pkey +│ equality: (a) = (ba) +│ equality cols are key +│ +└── • scan + table: foo@foo_pkey + spans: FULL SCAN + +# ConstructInvertedJoin +gist-explain-roundtrip +SELECT * FROM foo JOIN bar ON b @> ARRAY[1,2] +---- +hash: 11584704012200057713 +plan-gist: AgFsAgABAAAAFmoEagQUAGoCAgEJAAAAAAAGCA== +explain(shape): +• cross join +│ +├── • scan +│ missing stats +│ table: bar@bar_pkey +│ spans: FULL SCAN +│ +└── • lookup join + │ table: foo@foo_pkey + │ equality: (a) = (a) + │ equality cols are key + │ pred: b @> _ + │ + └── • zigzag join + left table: foo@b_inverted_index + left columns: (a) + left fixed values: 1 column + right table: foo@b_inverted_index + right columns: () + right fixed values: 1 column +explain(gist): +• cross join +│ +├── • scan +│ table: bar@bar_pkey +│ spans: FULL SCAN +│ +└── • lookup join + │ table: foo@foo_pkey + │ equality: (_) = (a) + │ equality cols are key + │ + └── • zigzag join + left table: foo@b_inverted_index + left columns: () + right table: foo@b_inverted_index + right columns: () + +# ConstructZigzagJoin +gist-explain-roundtrip +SELECT * FROM abc WHERE b = 2 AND c = 3 +---- +hash: 8337208311400991564 +plan-gist: AhZuBG4GBgY= +explain(shape): +• zigzag join + pred: (b = _) AND (c = _) + left table: abc@abc_b_idx + left columns: (a, b) + left fixed values: 1 column + right table: abc@abc_c_idx + right columns: (c) + right fixed values: 1 column +explain(gist): +• zigzag join + left table: abc@abc_b_idx + left columns: () + right table: abc@abc_c_idx + right columns: () + +# ConstructMax1Row +gist-explain-roundtrip +SELECT (SELECT a FROM abc FOR UPDATE) +---- +hash: 9076831683509001217 +plan-gist: AgFuBAABAAAAGQICAgYC +explain(shape): +• root +│ +├── • values +│ size: 1 column, 1 row +│ +└── • subquery + │ id: @S1 + │ original sql: (SELECT a FROM abc FOR UPDATE) + │ exec mode: one row + │ + └── • max1row + │ estimated row count: 1 + │ + └── • scan + missing stats + table: abc@abc_b_idx + spans: FULL SCAN + locking strength: for update +explain(gist): +• root +│ +├── • values +│ size: 1 column, 1 row +│ +└── • subquery + │ id: @S1 + │ exec mode: exists + │ + └── • max1row + │ + └── • scan + table: abc@abc_b_idx + spans: FULL SCAN + +# ConstructProjectSet +gist-explain-roundtrip +SELECT * FROM generate_series(1, 3) +---- +hash: 3230693242836472611 +plan-gist: AgICABoCBgI= +explain(shape): +• project set +│ estimated row count: 10 +│ +└── • emptyrow +explain(gist): +• project set +│ +└── • emptyrow + +# ConstructWindow +gist-explain-roundtrip +SELECT * FROM (SELECT avg(a) OVER () FROM abc) +---- +hash: 4492463608196347762 +plan-gist: AgFuBAABAAAAGwUCBgI= +explain(shape): +• window +│ +└── • scan + missing stats + table: abc@abc_b_idx + spans: FULL SCAN +explain(gist): +• window +│ +└── • scan + table: abc@abc_b_idx + spans: FULL SCAN + +# ConstructExplainOpt +gist-explain-roundtrip +EXPLAIN (OPT) SELECT 1 +---- +hash: 4329787178175946759 +plan-gist: AhwGAg== +explain(shape): +• explain +explain(gist): +• explain + +# ConstructExplain +gist-explain-roundtrip +EXPLAIN SELECT 1 +---- +hash: 4328817408920053882 +plan-gist: Ah0GAg== +explain(shape): +• explain +explain(gist): +• explain + +# ConstructShowTrace +gist-explain-roundtrip +SHOW TRACE FOR SESSION +---- +hash: 4327869629896725225 +plan-gist: Ah4GDg== +explain(shape): +• show trace +explain(gist): +• show trace + +# ConstructInsert +gist-explain-roundtrip +INSERT INTO abc SELECT * from abc +---- +hash: 16772965416642504445 +plan-gist: AgFuAgAHAAAAH24B +explain(shape): +• insert +│ into: abc(a, b, c) +│ auto commit +│ +└── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• insert +│ into: abc() +│ auto commit +│ +└── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructUpdate +gist-explain-roundtrip +UPDATE abc SET b = 2 WHERE a = 1 +---- +hash: 5613817878279489287 +plan-gist: AgFuAgAHAgAABwgFCCFuAAE= +explain(shape): +• update +│ table: abc +│ set: b +│ auto commit +│ +└── • render + │ + └── • scan + missing stats + table: abc@abc_pkey + spans: 1 span +explain(gist): +• update +│ table: abc +│ set +│ auto commit +│ +└── • render + │ + └── • scan + table: abc@abc_pkey + spans: 1 span + +# ConstructUpsert +gist-explain-roundtrip +UPSERT INTO abc (a,b,c) VALUES (1,2,3) +---- +hash: 11199340879865746945 +plan-gist: AgICBgFuAgAHAgAACQEAAAEBBRIibgE= +explain(shape): +• upsert +│ into: abc(a, b, c) +│ auto commit +│ arbiter indexes: abc_pkey +│ +└── • cross join (left outer) + │ + ├── • values + │ size: 3 columns, 1 row + │ + └── • scan + missing stats + table: abc@abc_pkey + spans: 1 span +explain(gist): +• upsert +│ into: abc() +│ auto commit +│ +└── • cross join (left outer) + │ + ├── • values + │ size: 3 columns, 1 row + │ + └── • scan + table: abc@abc_pkey + spans: 1 span + +# ConstructDeleteRange +# FIXME: how to test? +gist-explain-roundtrip +DELETE FROM foo +---- +hash: 5369057709634423529 +plan-gist: AgFqAgAHAAAAI2oB +explain(shape): +• delete +│ from: foo +│ auto commit +│ +└── • scan + missing stats + table: foo@foo_pkey + spans: FULL SCAN +explain(gist): +• delete +│ from: foo +│ auto commit +│ +└── • scan + table: foo@foo_pkey + spans: FULL SCAN + +# ConstructDelete +gist-explain-roundtrip +DELETE FROM foo WHERE a = 1 +---- +hash: 7691685103096689151 +plan-gist: AgFqAgAHAgAAI2oB +explain(shape): +• delete +│ from: foo +│ auto commit +│ +└── • scan + missing stats + table: foo@foo_pkey + spans: 1 span +explain(gist): +• delete +│ from: foo +│ auto commit +│ +└── • scan + table: foo@foo_pkey + spans: 1 span + +# createTableOp +gist-explain-roundtrip +CREATE TABLE t1 (x int) +---- +hash: 15656307912119785284 +plan-gist: AiUC +explain(shape): +• create table +explain(gist): +• create table + +# createTableAsOp +gist-explain-roundtrip +CREATE TABLE t2 AS SELECT * FROM abc +---- +hash: 12916714787348577326 +plan-gist: AgFuAgAHAAAABwgGCCYC +explain(shape): +• create table as +│ +└── • render + │ + └── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• create table as +│ +└── • render + │ + └── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# createViewOp +gist-explain-roundtrip +CREATE VIEW v AS SELECT 1 +---- +hash: 4334565655711205636 +plan-gist: AicCAg== +explain(shape): +• create view +explain(gist): +• create view + +exec-ddl +CREATE SEQUENCE select_test +---- + +# ConstructTopK +gist-explain-roundtrip +SELECT * FROM xyz ORDER BY y LIMIT 10 +---- +hash: 944221815887888565 +plan-gist: AgFwAgAHAAAAGAYG +explain(shape): +• top-k +│ order: +y +│ k: 10 +│ +└── • scan + missing stats + table: xyz@xyz_pkey + spans: FULL SCAN +explain(gist): +• top-k +│ order +│ +└── • scan + table: xyz@xyz_pkey + spans: FULL SCAN + + +# ConstructSequenceSelect +gist-explain-roundtrip +SELECT * FROM select_test +---- +hash: 4348909884410007975 +plan-gist: AigGBg== +explain(shape): +• sequence select + estimated row count: 1 +explain(gist): +• sequence select + +# ConstructSaveTable +# saveTableOp +# FIXME: couldn't find any examples of this, some internal thing? + + +# alterTableSplitOp +gist-explain-roundtrip +ALTER TABLE foo SPLIT AT VALUES(1) +---- +hash: 12193483658718214527 +plan-gist: AgICAgYCLGoCBgY= +explain(shape): +• split +│ +└── • values + size: 1 column, 1 row +explain(gist): +• split +│ +└── • values + size: 1 column, 1 row + +# alterTableUnsplitOp +gist-explain-roundtrip +ALTER TABLE foo UNSPLIT AT VALUES(1) +---- +hash: 8861506885543083786 +plan-gist: AgICAgYCLWoCBgQ= +explain(shape): +• unsplit +│ +└── • values + size: 1 column, 1 row +explain(gist): +• unsplit +│ +└── • values + size: 1 column, 1 row + + +# alterTableUnsplitAllOp +gist-explain-roundtrip +ALTER TABLE foo UNSPLIT ALL +---- +hash: 16533768908468588919 +plan-gist: Ai5qAgYE +explain(shape): +• unsplit all +explain(gist): +• unsplit all + +# alterTableRelocateOp +gist-explain-roundtrip +ALTER TABLE abc EXPERIMENTAL_RELOCATE VALUES (ARRAY[1],1) +---- +hash: 8656743312841134920 +plan-gist: AgICBAYEL24CBgQ= +explain(shape): +• relocate +│ +└── • values + size: 2 columns, 1 row +explain(gist): +• relocate +│ +└── • values + size: 2 columns, 1 row + +# ConstructBuffer/ConstructScanBuffer +gist-explain-roundtrip +SELECT * FROM [INSERT INTO abc SELECT a, b, c FROM abc RETURNING a] ORDER BY a +---- +hash: 15302623232936960462 +plan-gist: AgFuAgAHAAAAH24AMDERBgI= +explain(shape): +• root +│ +├── • sort +│ │ order: +a +│ │ +│ └── • scan buffer +│ label: buffer 1 +│ +└── • subquery + │ id: @S1 + │ original sql: INSERT INTO abc SELECT a, b, c FROM abc RETURNING a + │ exec mode: all rows + │ + └── • buffer + │ label: buffer 1 + │ + └── • insert + │ into: abc(a, b, c) + │ + └── • scan + missing stats + table: abc@abc_pkey + spans: FULL SCAN +explain(gist): +• root +│ +├── • sort +│ │ order +│ │ +│ └── • scan buffer +│ label +│ +└── • subquery + │ id: @S1 + │ exec mode: exists + │ + └── • buffer + │ label + │ + └── • insert + │ into: abc() + │ + └── • scan + table: abc@abc_pkey + spans: FULL SCAN + +# ConstructRecursiveCTE/recursiveCTEOp +gist-explain-roundtrip +WITH RECURSIVE cte(x) AS (VALUES (1) UNION ALL SELECT x+1 FROM cte WHERE x < 10) SELECT * FROM cte +---- +hash: 14841964197367358450 +plan-gist: AgICAjIHAgYC +explain(shape): +• render +│ +└── • recursive cte + │ + └── • values + size: 1 column, 1 row +explain(gist): +• render +│ +└── • recursive cte + │ + └── • values + size: 1 column, 1 row + +# controlSchedulesOp +gist-explain-roundtrip +PAUSE SCHEDULE 123 +---- +hash: 10623204031984216363 +plan-gist: AgICAjQ= +explain(shape): +• control schedules +│ +└── • values + size: 1 column, 1 row +explain(gist): +• control schedules +│ +└── • values + size: 1 column, 1 row + +# controlJobsOp +gist-explain-roundtrip +CANCEL JOBS SELECT 1 +---- +hash: 6026897435584380072 +plan-gist: AgICAgYCMw== +explain(shape): +• control jobs +│ +└── • values + size: 1 column, 1 row +explain(gist): +• control jobs +│ +└── • values + size: 1 column, 1 row + +# cancelQueriesOp +gist-explain-roundtrip +CANCEL QUERIES SELECT '1' +---- +hash: 6026897435584380078 +plan-gist: AgICAgYCNQ== +explain(shape): +• cancel queries +│ +└── • values + size: 1 column, 1 row +explain(gist): +• cancel queries +│ +└── • values + size: 1 column, 1 row + +# cancelSessionsOp +gist-explain-roundtrip +CANCEL SESSIONS SELECT '1' +---- +hash: 6026897435584380077 +plan-gist: AgICAgYCNg== +explain(shape): +• cancel sessions +│ +└── • values + size: 1 column, 1 row +explain(gist): +• cancel sessions +│ +└── • values + size: 1 column, 1 row + +# ConstructCreateStatistics +gist-explain-roundtrip +CREATE STATISTICS s1 FROM foo +---- +hash: 590681868797177008 +plan-gist: Ajc= +explain(shape): +• create statistics +explain(gist): +• create statistics + +# ConstructExport +gist-explain-roundtrip +EXPORT INTO CSV "tmp" FROM SELECT * FROM foo +---- +hash: 4828533627625221211 +plan-gist: AgFqAgAHAAAABgY4BgY= +explain(shape): +• export +│ +└── • scan + missing stats + table: foo@foo_pkey + spans: FULL SCAN +explain(gist): +• export +│ +└── • scan + table: foo@foo_pkey + spans: FULL SCAN + +# ConstructValues +gist-explain-roundtrip +SELECT 1 +---- +hash: 7870065175766586745 +plan-gist: AgICAgYC +explain(shape): +• values + size: 1 column, 1 row +explain(gist): +• values + size: 1 column, 1 row + + +# See what gist does on an explain query +gist-explain-roundtrip +EXPLAIN SELECT * from foo LIMIT 1 +---- +hash: 4328817408920053882 +plan-gist: Ah0GAg== +explain(shape): +• explain +explain(gist): +• explain diff --git a/pkg/sql/opt/exec/explain/testdata/gists_tpce b/pkg/sql/opt/exec/explain/testdata/gists_tpce new file mode 100644 index 000000000000..4ba1a3576ea0 --- /dev/null +++ b/pkg/sql/opt/exec/explain/testdata/gists_tpce @@ -0,0 +1,511 @@ +import file=tpce_schema +---- + +gist-explain-roundtrip +SELECT b_name, sum(tr_qty * tr_bid_price)::FLOAT8 +FROM sector, industry, company, security, trade_request, broker +WHERE tr_b_id = b_id + AND s_symb = tr_s_symb + AND co_id = s_co_id + AND in_id = co_in_id + AND sc_id = in_sc_id + AND b_name = ANY ARRAY[ + 'Broker1', 'Broker2', 'Broker3', 'Broker4', 'Broker5', + 'Broker6', 'Broker7', 'Broker8', 'Broker9', 'Broker10', + 'Broker11', 'Broker12', 'Broker13', 'Broker14', 'Broker15', + 'Broker16', 'Broker17', 'Broker18', 'Broker19', 'Broker20', + 'Broker21', 'Broker22', 'Broker23', 'Broker24', 'Broker25', + 'Broker26', 'Broker27', 'Broker28', 'Broker29', 'Broker30' + ] + AND sc_name = 'Energy' +GROUP BY b_name +ORDER BY 2 DESC +---- +hash: 14732118561700026222 +plan-gist: AgGUAQQAPAAAAAGqAQQAAwIAABQAogEEAgAUAJgBBAIAFACsAQQCAAkAAgIAARQAhgECAgEHBAsCBwQRBgQ= +explain(shape): +• sort +│ order: -sum +│ +└── • render + │ + └── • group + │ group by: b_name + │ + └── • render + │ + └── • lookup join + │ table: broker@broker_pkey + │ equality: (tr_b_id) = (b_id) + │ equality cols are key + │ pred: b_name IN _ + │ + └── • hash join + │ equality: (tr_s_symb) = (s_symb) + │ right cols are key + │ + ├── • scan + │ missing stats + │ table: trade_request@trade_request_tr_b_id_tr_s_symb_idx + │ spans: FULL SCAN + │ + └── • lookup join + │ table: security@security_s_co_id_s_issue_key + │ equality: (co_id) = (s_co_id) + │ + └── • lookup join + │ table: company@company_co_in_id_idx + │ equality: (in_id) = (co_in_id) + │ + └── • lookup join + │ table: industry@industry_in_sc_id_idx + │ equality: (sc_id) = (in_sc_id) + │ + └── • scan + missing stats + table: sector@sector_sc_name_key + spans: 1 span +explain(gist): +• sort +│ order +│ +└── • render + │ + └── • group + │ group by: _ + │ + └── • render + │ + └── • lookup join + │ table: broker@broker_pkey + │ equality: (tr_s_symb) = (b_id) + │ equality cols are key + │ + └── • hash join + │ equality: (tr_s_symb) = (sc_id) + │ right cols are key + │ + ├── • scan + │ table: trade_request@trade_request_tr_b_id_tr_s_symb_idx + │ spans: FULL SCAN + │ + └── • lookup join + │ table: security@security_s_co_id_s_issue_key + │ equality: (sc_id) = (s_co_id) + │ + └── • lookup join + │ table: company@company_co_in_id_idx + │ equality: (sc_id) = (co_in_id) + │ + └── • lookup join + │ table: industry@industry_in_sc_id_idx + │ equality: (sc_id) = (in_sc_id) + │ + └── • scan + table: sector@sector_sc_name_key + spans: 1 span + +gist-explain-roundtrip +SELECT ca_id, + ca_bal::FLOAT8, + IFNULL((sum(hs_qty * lt_price)), 0)::FLOAT8 + FROM customer_account +LEFT JOIN holding_summary ON hs_ca_id = ca_id +LEFT JOIN last_trade ON lt_s_symb = hs_s_symb + WHERE ca_c_id = 0 + GROUP BY ca_id, ca_bal + ORDER BY 3 ASC + LIMIT 10; +---- +hash: 10378570089558454947 +plan-gist: AgF4BAAFAgAAE3gCFAGAAQICABQBpAECAgEHBgsCBwYYBgY= +explain(shape): +• top-k +│ order: +"coalesce" +│ k: 10 +│ +└── • render + │ + └── • group + │ group by: ca_id + │ ordered: +ca_id + │ + └── • render + │ + └── • lookup join (left outer) + │ table: last_trade@last_trade_pkey + │ equality: (hs_s_symb) = (lt_s_symb) + │ equality cols are key + │ + └── • lookup join (left outer) + │ table: holding_summary@holding_summary_pkey + │ equality: (ca_id) = (hs_ca_id) + │ + └── • index join + │ table: customer_account@customer_account_pkey + │ + └── • scan + missing stats + table: customer_account@customer_account_ca_c_id_idx + spans: 1 span +explain(gist): +• top-k +│ order +│ +└── • render + │ + └── • group + │ group by: _ + │ + └── • render + │ + └── • lookup join (left outer) + │ table: last_trade@last_trade_pkey + │ equality: (_) = (lt_s_symb) + │ equality cols are key + │ + └── • lookup join (left outer) + │ table: holding_summary@holding_summary_pkey + │ equality: (_) = (hs_ca_id) + │ + └── • index join + │ table: customer_account@customer_account_pkey + │ + └── • scan + table: customer_account@customer_account_ca_c_id_idx + spans: 1 span + +gist-explain-roundtrip +WITH +update_last_trade AS ( + UPDATE last_trade + SET lt_vol = lt_vol + 10, + lt_price = 100.00:::FLOAT8::DECIMAL, + lt_dts = '2020-06-15 22:27:42.148484+00:00'::TIMESTAMP + WHERE lt_s_symb = 'ROACH' + RETURNING NULL +), +request_list AS ( + SELECT tr_t_id, tr_bid_price::FLOAT8, tr_tt_id, tr_qty + FROM trade_request + WHERE tr_s_symb = 'ROACH' + AND ( + (tr_tt_id = 'TMB'::VARCHAR(3) AND tr_bid_price >= 100.00:::FLOAT8::DECIMAL) OR + (tr_tt_id = 'TMS'::VARCHAR(3) AND tr_bid_price <= 100.00:::FLOAT8::DECIMAL) OR + (tr_tt_id = 'TLS'::VARCHAR(3) AND tr_bid_price >= 100.00:::FLOAT8::DECIMAL) + ) +), +delete_trade_request AS ( + DELETE FROM trade_request + WHERE tr_t_id IN (SELECT tr_t_id FROM request_list) + RETURNING NULL +), +insert_trade_history AS ( + INSERT INTO trade_history (th_t_id, th_st_id, th_dts) + (SELECT tr_t_id, 'SBMT', '2020-06-15 22:27:42.148484+00:00'::TIMESTAMP FROM request_list) + RETURNING NULL +), +update_trade_submitted AS ( + UPDATE trade + SET t_st_id = 'SBMT', t_dts = '2020-06-15 22:27:42.148484+00:00'::TIMESTAMP + WHERE t_id IN (SELECT tr_t_id FROM request_list) + RETURNING NULL +) +SELECT * FROM request_list; +---- +hash: 7096273538769246907 +plan-gist: AgGkAQIAHwIAAAcQBRAhpAEAAAcCMAGUAQIAHwAAAAMHCDAxBQIUAJQBAgIBBQgHCAUII5QBAAcCMDEFAgcGBQYwH5IBADEFAhQFkAECAgEqMQUCFAWwAQICASoHAjAxBQIUAJABAgIBBRwHIAUgMCGQAQAAMQUCFAWwAQICASoHAjAxBQgGCA== +explain(shape): +• root +│ +├── • scan buffer +│ label: buffer 3 (request_list) +│ +├── • subquery +│ │ id: @S1 +│ │ original sql: UPDATE last_trade SET lt_vol = lt_vol + 10, lt_price = 100.0::DECIMAL, lt_dts = '2020-06-15 22:27:42.148484+00:00'::TIMESTAMP WHERE lt_s_symb = 'ROACH' RETURNING NULL +│ │ exec mode: all rows +│ │ +│ └── • buffer +│ │ label: buffer 2 (update_last_trade) +│ │ +│ └── • render +│ │ +│ └── • update +│ │ table: last_trade +│ │ set: lt_dts, lt_price, lt_vol +│ │ +│ └── • render +│ │ +│ └── • scan +│ missing stats +│ table: last_trade@last_trade_pkey +│ spans: 1 span +│ +├── • subquery +│ │ id: @S2 +│ │ original sql: SELECT tr_t_id, tr_bid_price::FLOAT8, tr_tt_id, tr_qty FROM trade_request WHERE (tr_s_symb = 'ROACH') AND ((((tr_tt_id = 'TMB'::VARCHAR(3)) AND (tr_bid_price >= 100.0::DECIMAL)) OR ((tr_tt_id = 'TMS'::VARCHAR(3)) AND (tr_bid_price <= 100.0::DECIMAL))) OR ((tr_tt_id = 'TLS'::VARCHAR(3)) AND (tr_bid_price >= 100.0::DECIMAL))) +│ │ exec mode: all rows +│ │ +│ └── • buffer +│ │ label: buffer 3 (request_list) +│ │ +│ └── • render +│ │ +│ └── • filter +│ │ filter: (tr_s_symb = _) AND ((((tr_tt_id = _) AND (tr_bid_price >= _)) OR ((tr_tt_id = _) AND (tr_bid_price <= _))) OR ((tr_tt_id = _) AND (tr_bid_price >= _))) +│ │ +│ └── • scan +│ missing stats +│ table: trade_request@trade_request_pkey +│ spans: FULL SCAN +│ +├── • subquery +│ │ id: @S3 +│ │ original sql: DELETE FROM trade_request WHERE tr_t_id IN (SELECT tr_t_id FROM request_list) RETURNING NULL +│ │ exec mode: all rows +│ │ +│ └── • buffer +│ │ label: buffer 4 (delete_trade_request) +│ │ +│ └── • render +│ │ +│ └── • delete +│ │ from: trade_request +│ │ +│ └── • render +│ │ +│ └── • lookup join +│ │ table: trade_request@trade_request_pkey +│ │ equality: (tr_t_id) = (tr_t_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label: buffer 3 (request_list) +│ +├── • subquery +│ │ id: @S4 +│ │ original sql: INSERT INTO trade_history(th_t_id, th_st_id, th_dts) (SELECT tr_t_id, 'SBMT', '2020-06-15 22:27:42.148484+00:00'::TIMESTAMP FROM request_list) RETURNING NULL +│ │ exec mode: all rows +│ │ +│ └── • buffer +│ │ label: buffer 6 (insert_trade_history) +│ │ +│ └── • render +│ │ +│ └── • insert +│ │ into: trade_history(th_t_id, th_dts, th_st_id) +│ │ +│ └── • buffer +│ │ label: buffer 5 +│ │ +│ └── • render +│ │ +│ └── • scan buffer +│ label: buffer 3 (request_list) +│ +├── • subquery +│ │ id: @S5 +│ │ original sql: UPDATE trade SET t_st_id = 'SBMT', t_dts = '2020-06-15 22:27:42.148484+00:00'::TIMESTAMP WHERE t_id IN (SELECT tr_t_id FROM request_list) RETURNING NULL +│ │ exec mode: all rows +│ │ +│ └── • buffer +│ │ label: buffer 8 (update_trade_submitted) +│ │ +│ └── • render +│ │ +│ └── • update +│ │ table: trade +│ │ set: t_dts, t_st_id +│ │ +│ └── • buffer +│ │ label: buffer 7 +│ │ +│ └── • render +│ │ +│ └── • lookup join +│ │ table: trade@trade_pkey +│ │ equality: (tr_t_id) = (t_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label: buffer 3 (request_list) +│ +├── • constraint-check +│ │ +│ └── • error if rows +│ │ +│ └── • lookup join (anti) +│ │ table: trade@trade_pkey +│ │ equality: (tr_t_id) = (t_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label: buffer 5 +│ +├── • constraint-check +│ │ +│ └── • error if rows +│ │ +│ └── • lookup join (anti) +│ │ table: status_type@status_type_pkey +│ │ equality: (?column?) = (st_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label: buffer 5 +│ +└── • constraint-check + │ + └── • error if rows + │ + └── • lookup join (anti) + │ table: status_type@status_type_pkey + │ equality: (t_st_id_new) = (st_id) + │ equality cols are key + │ + └── • scan buffer + label: buffer 7 +explain(gist): +• root +│ +├── • scan buffer +│ label +│ +├── • subquery +│ │ id: @S1 +│ │ exec mode: exists +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • update +│ │ table: last_trade +│ │ set +│ │ +│ └── • render +│ │ +│ └── • scan +│ table: last_trade@last_trade_pkey +│ spans: 1 span +│ +├── • subquery +│ │ id: @S2 +│ │ exec mode: exists +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • filter +│ │ +│ └── • scan +│ table: trade_request@trade_request_pkey +│ spans: FULL SCAN +│ +├── • subquery +│ │ id: @S3 +│ │ exec mode: exists +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • delete +│ │ from: trade_request +│ │ +│ └── • render +│ │ +│ └── • lookup join +│ │ table: trade_request@trade_request_pkey +│ │ equality: (_) = (tr_t_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label +│ +├── • subquery +│ │ id: @S4 +│ │ exec mode: exists +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • insert +│ │ into: trade_history() +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • scan buffer +│ label +│ +├── • subquery +│ │ id: @S5 +│ │ exec mode: exists +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • update +│ │ table: trade +│ │ set +│ │ +│ └── • buffer +│ │ label +│ │ +│ └── • render +│ │ +│ └── • lookup join +│ │ table: trade@trade_pkey +│ │ equality: (_) = (t_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label +│ +├── • constraint-check +│ │ +│ └── • error if rows +│ │ +│ └── • lookup join (anti) +│ │ table: trade@trade_pkey +│ │ equality: (_) = (t_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label +│ +├── • constraint-check +│ │ +│ └── • error if rows +│ │ +│ └── • lookup join (anti) +│ │ table: status_type@status_type_pkey +│ │ equality: (_) = (st_id) +│ │ equality cols are key +│ │ +│ └── • scan buffer +│ label +│ +└── • constraint-check + │ + └── • error if rows + │ + └── • lookup join (anti) + │ table: status_type@status_type_pkey + │ equality: (_) = (st_id) + │ equality cols are key + │ + └── • scan buffer + label diff --git a/pkg/sql/opt/exec/factory.go b/pkg/sql/opt/exec/factory.go index a92af48a2285..83f3ae70b947 100644 --- a/pkg/sql/opt/exec/factory.go +++ b/pkg/sql/opt/exec/factory.go @@ -340,5 +340,5 @@ type ExecutionStats struct { } // BuildPlanForExplainFn builds an execution plan against the given -// ExplainFactory. -type BuildPlanForExplainFn func(ef ExplainFactory) (Plan, error) +// base factory. +type BuildPlanForExplainFn func(f Factory) (Plan, error) diff --git a/pkg/sql/opt/optbuilder/explain.go b/pkg/sql/opt/optbuilder/explain.go index 114106ec2b3e..284e42517390 100644 --- a/pkg/sql/opt/optbuilder/explain.go +++ b/pkg/sql/opt/optbuilder/explain.go @@ -55,6 +55,9 @@ func (b *Builder) buildExplain(explain *tree.Explain, inScope *scope) (outScope telemetry.Inc(sqltelemetry.ExplainDDLStages) } + case tree.ExplainGist: + telemetry.Inc(sqltelemetry.ExplainGist) + default: panic(errors.Errorf("EXPLAIN mode %s not supported", explain.Mode)) } diff --git a/pkg/sql/opt/optgen/cmd/optgen/BUILD.bazel b/pkg/sql/opt/optgen/cmd/optgen/BUILD.bazel index e4ea0ed5a89c..2529ad980fba 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/BUILD.bazel +++ b/pkg/sql/opt/optgen/cmd/optgen/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "exec_explain_gen.go", "exec_factory_gen.go", + "exec_plan_gist_gen.go", "explorer_gen.go", "exprs_gen.go", "factory_gen.go", diff --git a/pkg/sql/opt/optgen/cmd/optgen/exec_explain_gen.go b/pkg/sql/opt/optgen/cmd/optgen/exec_explain_gen.go index 92a9519d615b..56fc552a5d39 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/exec_explain_gen.go +++ b/pkg/sql/opt/optgen/cmd/optgen/exec_explain_gen.go @@ -87,7 +87,7 @@ func (g *execExplainGen) genExplainFactory() { fmt.Fprintf(&nodesBuf, ", %sNode", n) } } - g.w.writeIndent("_n, err := f.newNode(%sOp, args, %s%s)\n", opName, ordering, nodesBuf.String()) + g.w.writeIndent("_n, err := newNode(%sOp, args, %s%s)\n", opName, ordering, nodesBuf.String()) g.w.nestIndent("if err != nil {\n") g.w.writeIndent("return nil, err\n") g.w.unnest("}\n") @@ -121,6 +121,7 @@ func (g *execExplainGen) genEnum() { for _, define := range g.compiled.Defines { g.w.writeIndent("%sOp\n", unTitle(string(define.Name))) } + g.w.writeIndent("numOperators\n") g.w.unnest(")\n") } diff --git a/pkg/sql/opt/optgen/cmd/optgen/exec_factory_gen.go b/pkg/sql/opt/optgen/cmd/optgen/exec_factory_gen.go index 834c42e19022..407281d9ce40 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/exec_factory_gen.go +++ b/pkg/sql/opt/optgen/cmd/optgen/exec_factory_gen.go @@ -52,6 +52,13 @@ func (g *execFactoryGen) genExecFactory() { g.w.write("// leaf nodes, or they take other nodes previously constructed by this same\n") g.w.write("// factory as children.\n") g.w.write("//\n") + g.w.write("// The PlanGistFactory further requires that the factory methods be called in\n") + g.w.write("// natural tree order, that is, after an operator's children are constructed the\n") + g.w.write("// next factory method to be called must be the parent of those nodes. The gist's\n") + g.w.write("// flattened tree representation relies on this to enable the use of a stack to\n") + g.w.write("// hold children, thus avoiding the complexity of dealing with other tree traversal\n") + g.w.write("// methods.\n") + g.w.write("//\n") g.w.write("// The TypedExprs passed to these functions refer to columns of the input node\n") g.w.write("// via IndexedVars.\n") g.w.nest("type Factory interface {\n") diff --git a/pkg/sql/opt/optgen/cmd/optgen/exec_plan_gist_gen.go b/pkg/sql/opt/optgen/cmd/optgen/exec_plan_gist_gen.go new file mode 100644 index 000000000000..47f607fd6bc7 --- /dev/null +++ b/pkg/sql/opt/optgen/cmd/optgen/exec_plan_gist_gen.go @@ -0,0 +1,199 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package main + +import ( + "fmt" + "io" + "strings" + + "github.com/cockroachdb/cockroach/pkg/sql/opt/optgen/lang" +) + +type execPlanGistGen struct { + compiled *lang.CompiledExpr + w *matchWriter +} + +func (g *execPlanGistGen) generate(compiled *lang.CompiledExpr, w io.Writer) { + g.compiled = compiled + g.w = &matchWriter{writer: w} + + g.w.write("package explain\n\n") + + g.w.nestIndent("import (\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/opt\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/opt/cat\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/opt/constraint\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/opt/exec\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/sem/tree\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/types\"\n") + g.w.writeIndent("\"github.com/cockroachdb/cockroach/pkg/sql/inverted\"\n") + g.w.writeIndent("\"github.com/cockroachdb/errors\"\n") + g.w.unnest(")\n") + + g.genPlanGistFactory() + g.genPlanGistDecoder() +} + +// boolAllowList includes all the "bool" operator properties that affect the +// output of EXPLAIN (SHAPE) and therefore should be included in the gist. +var boolAllowList = map[string]bool{ + "autoCommit": true, + "leftEqColsAreKey": true, + "rightEqColsAreKey": true, + "eqColsAreKey": true, + "all": true, +} + +func (g *execPlanGistGen) genPlanGistFactory() { + for _, define := range g.compiled.Defines { + g.w.write("\n") + g.w.nest("func (f *PlanGistFactory) Construct%s(\n", define.Name) + for _, field := range define.Fields { + generateComments(g.w.writer, field.Comments, string(field.Name), unTitle(string(field.Name))) + g.w.writeIndent("%s %s,\n", unTitle(string(field.Name)), field.Type) + } + g.w.write(") (exec.Node, error) {\n") + + op := fmt.Sprintf("%sOp", unTitle(string(define.Name))) + g.w.write("f.encodeOperator(%s)\n", op) + + if strings.HasPrefix(string(define.Name), "AlterTable") { + g.w.write("f.encodeDataSource(index.Table().ID(), index.Table().Name())\n") + } + for _, field := range define.Fields { + // grab ids + var expr, encoder string + name := unTitle(string(field.Name)) + switch unTitle(string(field.Type)) { + case "cat.Index", "cat.Table": + g.w.writeIndent("f.encodeDataSource(%s.ID(), %s.Name())\n", name, name) + case "cat.Schema": + g.w.writeIndent("f.encodeID(%s.ID())\n", name) + case "[]exec.NodeColumnOrdinal": + expr = name + encoder = "encodeNodeColumnOrdinals" + case "colinfo.ResultColumns": + expr = name + encoder = "encodeResultColumns" + case "bool": + if boolAllowList[name] { + expr = name + encoder = "encodeBool" + } + case "descpb.JoinType": + g.w.writeIndent("f.encodeByte(byte(%s))\n", name) + case "colinfo.ColumnOrdering": + expr = name + encoder = "encodeColumnOrdering" + case "exec.ScanParams": + expr = name + encoder = "encodeScanParams" + case "[][]tree.TypedExpr": + expr = name + encoder = "encodeRows" + } + if len(expr) > 0 { + g.w.writeIndent("f.%s(%s)\n", encoder, expr) + } + } + + g.w.nestIndent("node, err := f.wrappedFactory.Construct%s(\n", define.Name) + for _, field := range define.Fields { + g.w.writeIndent("%s,\n", unTitle(string(field.Name))) + } + g.w.unnest(")\n") + g.w.writeIndent("return node, err\n") + g.w.unnest("}\n") + } +} + +func (g *execPlanGistGen) genPlanGistDecoder() { + g.w.write("\n") + g.w.nest("func (f *PlanGistFactory) decodeOperatorBody(op execOperator) (*Node, error) {\n") + g.w.writeIndent("var _n *Node\n") + g.w.writeIndent("var reqOrdering exec.OutputOrdering\n") + g.w.writeIndent("var err error\n") + g.w.writeIndent("var tbl cat.Table\n") + g.w.nestIndent("switch op {\n") + childrenNames := []string{} + for _, define := range g.compiled.Defines { + g.w.writeIndent("case %sOp:\n", unTitle(string(define.Name))) + g.w.nestIndent("var args %sArgs\n", unTitle(string(define.Name))) + // table is implicit + if strings.HasPrefix(string(define.Name), "AlterTable") { + g.w.writeIndent("tbl := f.decodeTable()\n") + } + for f, field := range define.Fields { + // grab ids + var argName, decoder, decoderArg, store string + name := unTitle(string(field.Name)) + argName = title(name) + switch unTitle(string(field.Type)) { + case "cat.Table": + decoder = "decodeTable" + if f+1 < len(define.Fields) && string(define.Fields[f+1].Type) == "cat.Index" { + store = "tbl" + } + case "cat.Index": + decoder = "decodeIndex" + decoderArg = "tbl" + case "cat.Schema": + decoder = "decodeSchema" + case "[]exec.NodeColumnOrdinal": + decoder = "decodeNodeColumnOrdinals" + case "colinfo.ResultColumns": + decoder = "decodeResultColumns" + case "bool": + if boolAllowList[name] { + decoder = "decodeBool" + } + case "descpb.JoinType": + decoder = "decodeJoinType" + case "colinfo.ColumnOrdering": + decoder = "decodeColumnOrdering" + case "exec.ScanParams": + decoder = "decodeScanParams" + case "[][]tree.TypedExpr": + decoder = "decodeRows" + } + + if len(decoder) > 0 { + g.w.writeIndent("args.%s = f.%s(%s)\n", argName, decoder, decoderArg) + } + if len(store) > 0 { + g.w.writeIndent("%s = args.%s\n", store, argName) + } + // ScanBuffer is an exception here, the node it references is not a child. + if field.Type == "exec.Node" && define.Name != "ScanBuffer" { + childName := string(field.Name) + childrenNames = append(childrenNames, childName) + } + } + + for i := len(childrenNames) - 1; i >= 0; i-- { + childrenNames[i] = "args." + childrenNames[i] + g.w.writeIndent("%s = f.popChild()\n", childrenNames[i]) + } + + g.w.writeIndent("_n, err = newNode(op, &args, reqOrdering, %s)\n", strings.Join(childrenNames, ",")) + childrenNames = []string{} + g.w.unnest("") + } + g.w.writeIndent("default:\n") + g.w.writeIndent("return nil, errors.Newf(\"invalid op: %%d\", op)\n") + g.w.unnest("}\n") + g.w.writeIndent("return _n, err\n") + g.w.unnest("}\n") +} diff --git a/pkg/sql/opt/optgen/cmd/optgen/main.go b/pkg/sql/opt/optgen/cmd/optgen/main.go index e4c7fa935905..cbc19b3e83a9 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/main.go +++ b/pkg/sql/opt/optgen/cmd/optgen/main.go @@ -98,7 +98,7 @@ func (g *optgen) run(args ...string) bool { case "ops": case "rulenames": - case "execfactory", "execexplain": + case "execfactory", "execexplain", "execplangist": runValidate = false default: @@ -189,6 +189,10 @@ func (g *optgen) run(args ...string) bool { case "execexplain": var gen execExplainGen err = g.generate(compiled, gen.generate) + + case "execplangist": + var gen execPlanGistGen + err = g.generate(compiled, gen.generate) } if err != nil { @@ -260,12 +264,16 @@ func (g *optgen) usage() { fmt.Fprintf(g.stdErr, "\toptgen [flags] command sources...\n\n") fmt.Fprintf(g.stdErr, "The commands are:\n\n") - fmt.Fprintf(g.stdErr, "\tcompile generate the optgen compiled format\n") - fmt.Fprintf(g.stdErr, "\texplorer generate expression tree exploration rules\n") - fmt.Fprintf(g.stdErr, "\texprs generate expression definitions and functions\n") - fmt.Fprintf(g.stdErr, "\tfactory generate expression tree creation and normalization functions\n") - fmt.Fprintf(g.stdErr, "\tops generate operator definitions and functions\n") - fmt.Fprintf(g.stdErr, "\trulenames generate enumeration of rule names\n") + fmt.Fprintf(g.stdErr, "\tcompile generate the optgen compiled format\n") + fmt.Fprintf(g.stdErr, "\texplorer generate expression tree exploration rules\n") + fmt.Fprintf(g.stdErr, "\texprs generate expression definitions and functions\n") + fmt.Fprintf(g.stdErr, "\tfactory generate expression tree creation and normalization functions\n") + fmt.Fprintf(g.stdErr, "\tops generate operator definitions and functions\n") + fmt.Fprintf(g.stdErr, "\trulenames generate enumeration of rule names\n") + fmt.Fprintf(g.stdErr, "\texecfactory generate exec.Factory interface\n") + fmt.Fprintf(g.stdErr, "\texecexplain generate explain factory\n") + fmt.Fprintf(g.stdErr, "\texecplangist generate plan gist factory\n") + fmt.Fprintf(g.stdErr, "\n") fmt.Fprintf(g.stdErr, "The sources can be file names and/or filepath.Glob patterns.\n") diff --git a/pkg/sql/opt/optgen/cmd/optgen/testdata/execexplain b/pkg/sql/opt/optgen/cmd/optgen/testdata/execexplain index 4e79e48a2865..4fd07784682c 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/testdata/execexplain +++ b/pkg/sql/opt/optgen/cmd/optgen/testdata/execexplain @@ -54,7 +54,7 @@ func (f *Factory) ConstructScan( Params: params, ReqOrdering: reqOrdering, } - _n, err := f.newNode(scanOp, args, reqOrdering) + _n, err := newNode(scanOp, args, reqOrdering) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (f *Factory) ConstructFilter( Filter: filter, ReqOrdering: reqOrdering, } - _n, err := f.newNode(filterOp, args, reqOrdering, inputNode) + _n, err := newNode(filterOp, args, reqOrdering, inputNode) if err != nil { return nil, err } @@ -122,7 +122,7 @@ func (f *Factory) ConstructHashJoin( RightEqColsAreKey: rightEqColsAreKey, ExtraOnCond: extraOnCond, } - _n, err := f.newNode(hashJoinOp, args, nil /* ordering */, leftNode, rightNode) + _n, err := newNode(hashJoinOp, args, nil /* ordering */, leftNode, rightNode) if err != nil { return nil, err } @@ -151,6 +151,7 @@ const ( scanOp filterOp hashJoinOp + numOperators ) type scanArgs struct { diff --git a/pkg/sql/opt/optgen/cmd/optgen/testdata/execfactory b/pkg/sql/opt/optgen/cmd/optgen/testdata/execfactory index dfded53677e3..8cf3d8a7fad7 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/testdata/execfactory +++ b/pkg/sql/opt/optgen/cmd/optgen/testdata/execfactory @@ -39,6 +39,13 @@ import ( // leaf nodes, or they take other nodes previously constructed by this same // factory as children. // +// The PlanGistFactory further requires that the factory methods be called in +// natural tree order, that is, after an operator's children are constructed the +// next factory method to be called must be the parent of those nodes. The gist's +// flattened tree representation relies on this to enable the use of a stack to +// hold children, thus avoiding the complexity of dealing with other tree traversal +// methods. +// // The TypedExprs passed to these functions refer to columns of the input node // via IndexedVars. type Factory interface { diff --git a/pkg/sql/opt/optgen/cmd/optgen/testdata/execplangist b/pkg/sql/opt/optgen/cmd/optgen/testdata/execplangist new file mode 100644 index 000000000000..063fdc5d1059 --- /dev/null +++ b/pkg/sql/opt/optgen/cmd/optgen/testdata/execplangist @@ -0,0 +1,141 @@ +optgen execplangist test.opt +# Scan returns a node that represents a scan of the given index on +# the given table. +define Scan { + Table cat.Table + Index cat.Index + Params exec.ScanParams + ReqOrdering exec.OutputOrdering +} + +define Filter { + Input exec.Node + Filter tree.TypedExpr + ReqOrdering exec.OutputOrdering +} + +define HashJoin { + JoinType descpb.JoinType + Left exec.Node + Right exec.Node + LeftEqCols []exec.NodeColumnOrdinal + RightEqCols []exec.NodeColumnOrdinal + LeftEqColsAreKey bool + RightEqColsAreKey bool + ExtraOnCond tree.TypedExpr +} +---- +---- +// Code generated by optgen; [omitted] + +package explain + +import ( + "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/inverted" + "github.com/cockroachdb/cockroach/pkg/sql/opt" + "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" + "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/errors" +) + +func (f *PlanGistFactory) ConstructScan( + table cat.Table, + index cat.Index, + params exec.ScanParams, + reqOrdering exec.OutputOrdering, +) (exec.Node, error) { + f.encodeOperator(scanOp) + f.encodeDataSource(table.ID(), table.Name()) + f.encodeDataSource(index.ID(), index.Name()) + f.encodeScanParams(params) + node, err := f.wrappedFactory.ConstructScan( + table, + index, + params, + reqOrdering, + ) + return node, err +} + +func (f *PlanGistFactory) ConstructFilter( + input exec.Node, + filter tree.TypedExpr, + reqOrdering exec.OutputOrdering, +) (exec.Node, error) { + f.encodeOperator(filterOp) + node, err := f.wrappedFactory.ConstructFilter( + input, + filter, + reqOrdering, + ) + return node, err +} + +func (f *PlanGistFactory) ConstructHashJoin( + joinType descpb.JoinType, + left exec.Node, + right exec.Node, + leftEqCols []exec.NodeColumnOrdinal, + rightEqCols []exec.NodeColumnOrdinal, + leftEqColsAreKey bool, + rightEqColsAreKey bool, + extraOnCond tree.TypedExpr, +) (exec.Node, error) { + f.encodeOperator(hashJoinOp) + f.encodeByte(byte(joinType)) + f.encodeNodeColumnOrdinals(leftEqCols) + f.encodeNodeColumnOrdinals(rightEqCols) + f.encodeBool(leftEqColsAreKey) + f.encodeBool(rightEqColsAreKey) + node, err := f.wrappedFactory.ConstructHashJoin( + joinType, + left, + right, + leftEqCols, + rightEqCols, + leftEqColsAreKey, + rightEqColsAreKey, + extraOnCond, + ) + return node, err +} + +func (f *PlanGistFactory) decodeOperatorBody(op execOperator) (*Node, error) { + var _n *Node + var reqOrdering exec.OutputOrdering + var err error + var tbl cat.Table + switch op { + case scanOp: + var args scanArgs + args.Table = f.decodeTable() + tbl = args.Table + args.Index = f.decodeIndex(tbl) + args.Params = f.decodeScanParams() + _n, err = newNode(op, &args, reqOrdering) + case filterOp: + var args filterArgs + args.Input = f.popChild() + _n, err = newNode(op, &args, reqOrdering, args.Input) + case hashJoinOp: + var args hashJoinArgs + args.JoinType = f.decodeJoinType() + args.LeftEqCols = f.decodeNodeColumnOrdinals() + args.RightEqCols = f.decodeNodeColumnOrdinals() + args.LeftEqColsAreKey = f.decodeBool() + args.RightEqColsAreKey = f.decodeBool() + args.Right = f.popChild() + args.Left = f.popChild() + _n, err = newNode(op, &args, reqOrdering, args.Left, args.Right) + default: + return nil, errors.Newf("invalid op: %d", op) + } + return _n, err +} +---- +---- diff --git a/pkg/sql/opt/optgen/cmd/optgen/testdata/usage b/pkg/sql/opt/optgen/cmd/optgen/testdata/usage index 4b5663d0fcd6..a143f4da404d 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/testdata/usage +++ b/pkg/sql/opt/optgen/cmd/optgen/testdata/usage @@ -15,12 +15,15 @@ Usage: The commands are: - compile generate the optgen compiled format - explorer generate expression tree exploration rules - exprs generate expression definitions and functions - factory generate expression tree creation and normalization functions - ops generate operator definitions and functions - rulenames generate enumeration of rule names + compile generate the optgen compiled format + explorer generate expression tree exploration rules + exprs generate expression definitions and functions + factory generate expression tree creation and normalization functions + ops generate operator definitions and functions + rulenames generate enumeration of rule names + execfactory generate exec.Factory interface + execexplain generate explain factory + execplangist generate plan gist factory The sources can be file names and/or filepath.Glob patterns. @@ -47,12 +50,15 @@ Usage: The commands are: - compile generate the optgen compiled format - explorer generate expression tree exploration rules - exprs generate expression definitions and functions - factory generate expression tree creation and normalization functions - ops generate operator definitions and functions - rulenames generate enumeration of rule names + compile generate the optgen compiled format + explorer generate expression tree exploration rules + exprs generate expression definitions and functions + factory generate expression tree creation and normalization functions + ops generate operator definitions and functions + rulenames generate enumeration of rule names + execfactory generate exec.Factory interface + execexplain generate explain factory + execplangist generate plan gist factory The sources can be file names and/or filepath.Glob patterns. @@ -80,12 +86,15 @@ Usage: The commands are: - compile generate the optgen compiled format - explorer generate expression tree exploration rules - exprs generate expression definitions and functions - factory generate expression tree creation and normalization functions - ops generate operator definitions and functions - rulenames generate enumeration of rule names + compile generate the optgen compiled format + explorer generate expression tree exploration rules + exprs generate expression definitions and functions + factory generate expression tree creation and normalization functions + ops generate operator definitions and functions + rulenames generate enumeration of rule names + execfactory generate exec.Factory interface + execexplain generate explain factory + execplangist generate plan gist factory The sources can be file names and/or filepath.Glob patterns. diff --git a/pkg/sql/opt/testutils/opttester/BUILD.bazel b/pkg/sql/opt/testutils/opttester/BUILD.bazel index 337ead14396b..4d942413f9bc 100644 --- a/pkg/sql/opt/testutils/opttester/BUILD.bazel +++ b/pkg/sql/opt/testutils/opttester/BUILD.bazel @@ -16,6 +16,8 @@ go_library( deps = [ "//pkg/build/bazel", "//pkg/jobs/jobspb", + "//pkg/keys", + "//pkg/kv", "//pkg/roachpb:with-mocks", "//pkg/security", "//pkg/settings/cluster", @@ -23,6 +25,7 @@ go_library( "//pkg/sql/catalog/schemaexpr", "//pkg/sql/opt", "//pkg/sql/opt/cat", + "//pkg/sql/opt/exec", "//pkg/sql/opt/exec/execbuilder", "//pkg/sql/opt/memo", "//pkg/sql/opt/norm", @@ -37,8 +40,11 @@ go_library( "//pkg/sql/pgwire/pgerror", "//pkg/sql/sem/tree", "//pkg/sql/stats", + "//pkg/testutils", "//pkg/testutils/sqlutils", "//pkg/util", + "//pkg/util/hlc", + "//pkg/util/stop", "//pkg/util/timeutil", "//pkg/util/treeprinter", "@com_github_cockroachdb_datadriven//:datadriven", diff --git a/pkg/sql/opt/testutils/opttester/opt_tester.go b/pkg/sql/opt/testutils/opttester/opt_tester.go index 0b9073c638da..dc8d5299e4f8 100644 --- a/pkg/sql/opt/testutils/opttester/opt_tester.go +++ b/pkg/sql/opt/testutils/opttester/opt_tester.go @@ -33,6 +33,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/build/bazel" "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/security" "github.com/cockroachdb/cockroach/pkg/settings/cluster" @@ -40,7 +42,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemaexpr" "github.com/cockroachdb/cockroach/pkg/sql/opt" "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" - _ "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/execbuilder" // for ExprFmtHideScalars. + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/execbuilder" "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" "github.com/cockroachdb/cockroach/pkg/sql/opt/norm" "github.com/cockroachdb/cockroach/pkg/sql/opt/optbuilder" @@ -53,7 +56,10 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/stats" + "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/util" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/stop" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/cockroach/pkg/util/treeprinter" "github.com/cockroachdb/datadriven" @@ -2041,3 +2047,18 @@ func ruleFromString(str string) (opt.RuleName, error) { return opt.InvalidRuleName, fmt.Errorf("rule '%s' does not exist", str) } + +// ExecBuild is used for testing to expose makeOptimizer. +func (ot *OptTester) ExecBuild(f exec.Factory, mem *memo.Memo, expr opt.Expr) (exec.Plan, error) { + // For DDL we need a fresh root transaction that isn't system tenant. + if opt.IsDDLOp(expr) { + ot.evalCtx.Codec = keys.MakeSQLCodec(roachpb.MakeTenantID(5)) + factory := kv.MockTxnSenderFactory{} + clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) + stopper := stop.NewStopper() + db := kv.NewDB(testutils.MakeAmbientCtx(), factory, clock, stopper) + ot.evalCtx.Txn = kv.NewTxn(context.Background(), db, 1) + } + bld := execbuilder.New(f, ot.makeOptimizer(), mem, ot.catalog, expr, &ot.evalCtx, true) + return bld.Build() +} diff --git a/pkg/sql/opt_exec_factory.go b/pkg/sql/opt_exec_factory.go index 30f38daf609f..31d939e90220 100644 --- a/pkg/sql/opt_exec_factory.go +++ b/pkg/sql/opt_exec_factory.go @@ -2052,11 +2052,10 @@ func (ef *execFactory) ConstructExplain( return nil, errors.New("ENV only supported with (OPT) option") } - explainFactory := explain.NewFactory(&execFactory{ + plan, err := buildFn(&execFactory{ planner: ef.planner, isExplain: true, }) - plan, err := buildFn(explainFactory) if err != nil { return nil, err } diff --git a/pkg/sql/plan_opt.go b/pkg/sql/plan_opt.go index cd07e84b4de5..7c2aa53c0638 100644 --- a/pkg/sql/plan_opt.go +++ b/pkg/sql/plan_opt.go @@ -564,9 +564,9 @@ func (opc *optPlanningCtx) runExecBuilder( var containsLargeFullTableScan bool var containsLargeFullIndexScan bool var containsMutation bool + gf := explain.NewPlanGistFactory(f) if !planTop.instrumentation.ShouldBuildExplainPlan() { - // No instrumentation. - bld := execbuilder.New(f, &opc.optimizer, mem, &opc.catalog, mem.RootExpr(), evalCtx, allowAutoCommit) + bld := execbuilder.New(gf, &opc.optimizer, mem, &opc.catalog, mem.RootExpr(), evalCtx, allowAutoCommit) plan, err := bld.Build() if err != nil { return err @@ -580,7 +580,7 @@ func (opc *optPlanningCtx) runExecBuilder( containsMutation = bld.ContainsMutation } else { // Create an explain factory and record the explain.Plan. - explainFactory := explain.NewFactory(f) + explainFactory := explain.NewFactory(gf) bld := execbuilder.New( explainFactory, &opc.optimizer, mem, &opc.catalog, mem.RootExpr(), evalCtx, allowAutoCommit, ) @@ -599,6 +599,7 @@ func (opc *optPlanningCtx) runExecBuilder( planTop.instrumentation.RecordExplainPlan(explainPlan) } + planTop.instrumentation.planGist = gf.PlanGist() if stmt.ExpectedTypes != nil { cols := result.main.planColumns() @@ -634,3 +635,9 @@ func (opc *optPlanningCtx) runExecBuilder( } return nil } + +// DecodeGist Avoid an import cycle by keeping the cat out of the tree, RFC: is +// there a better way? +func (p *planner) DecodeGist(gist string) ([]string, error) { + return explain.DecodePlanGistToRows(gist, &p.optPlanningCtx.catalog) +} diff --git a/pkg/sql/sem/builtins/generator_builtins.go b/pkg/sql/sem/builtins/generator_builtins.go index 9788b6573099..45b7c06ac644 100644 --- a/pkg/sql/sem/builtins/generator_builtins.go +++ b/pkg/sql/sem/builtins/generator_builtins.go @@ -423,6 +423,65 @@ The output can be used to recreate a database.' tree.VolatilityVolatile, ), ), + "crdb_internal.decode_plan_gist": makeBuiltin( + tree.FunctionProperties{ + Class: tree.GeneratorClass, + }, + makeGeneratorOverload( + tree.ArgTypes{ + {"gist", types.String}, + }, + decodePlanGistGeneratorType, + makeDecodePlanGistGenerator, + `Returns rows of output similar to EXPLAIN from a gist created by EXPLAIN (GIST) +`, + tree.VolatilityVolatile, + ), + ), +} + +var decodePlanGistGeneratorType = types.String + +type gistPlanGenerator struct { + gist string + index int + rows []string + p tree.EvalPlanner +} + +var _ tree.ValueGenerator = &gistPlanGenerator{} + +func (g *gistPlanGenerator) ResolvedType() *types.T { + return types.String +} + +func (g *gistPlanGenerator) Start(_ context.Context, _ *kv.Txn) error { + rows, err := g.p.DecodeGist(g.gist) + if err != nil { + return err + } + g.rows = rows + g.index = -1 + return nil +} + +func (g *gistPlanGenerator) Next(context.Context) (bool, error) { + g.index++ + return g.index < len(g.rows), nil +} + +func (g *gistPlanGenerator) Close(context.Context) {} + +// Values implements the tree.ValueGenerator interface. +func (g *gistPlanGenerator) Values() (tree.Datums, error) { + return tree.Datums{tree.NewDString(g.rows[g.index])}, nil +} + +func makeDecodePlanGistGenerator( + ctx *tree.EvalContext, args tree.Datums, +) (tree.ValueGenerator, error) { + gist := string(tree.MustBeDString(args[0])) + return &gistPlanGenerator{gist: gist, p: ctx.Planner}, nil } func makeGeneratorOverload( diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index f24fbf8e8058..38c789a3074e 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -3225,6 +3225,9 @@ type EvalPlanner interface { // ExternalWriteFile writes the content to an external file URI. ExternalWriteFile(ctx context.Context, uri string, content []byte) error + + // DecodeGist exposes gist functionality to the builtin functions. + DecodeGist(gist string) ([]string, error) } // CompactEngineSpanFunc is used to compact an engine key span at the given diff --git a/pkg/sql/sem/tree/explain.go b/pkg/sql/sem/tree/explain.go index e5aa364d0c5f..c16b8706c5b9 100644 --- a/pkg/sql/sem/tree/explain.go +++ b/pkg/sql/sem/tree/explain.go @@ -72,6 +72,9 @@ const ( // ExplainDDL + // ExplainGist generates a plan "gist". + ExplainGist + numExplainModes = iota ) @@ -82,6 +85,7 @@ var explainModeStrings = [...]string{ ExplainVec: "VEC", ExplainDebug: "DEBUG", ExplainDDL: "DDL", + ExplainGist: "GIST", } var explainModeStringMap = func() map[string]ExplainMode { diff --git a/pkg/sql/sqltelemetry/planning.go b/pkg/sql/sqltelemetry/planning.go index c16af7ece8b5..ce42efda6824 100644 --- a/pkg/sql/sqltelemetry/planning.go +++ b/pkg/sql/sqltelemetry/planning.go @@ -116,6 +116,10 @@ var ExplainDDLDeps = telemetry.GetCounterOnce("sql.plan.explain-ddl-deps") // EXPLAIN (OPT, VERBOSE) is run. var ExplainOptVerboseUseCounter = telemetry.GetCounterOnce("sql.plan.explain-opt-verbose") +// ExplainGist is to be incremented whenever +// EXPLAIN (GIST) is run. +var ExplainGist = telemetry.GetCounterOnce("sql.plan.explain-gist") + // CreateStatisticsUseCounter is to be incremented whenever a non-automatic // run of CREATE STATISTICS occurs. var CreateStatisticsUseCounter = telemetry.GetCounterOnce("sql.plan.stats.created") diff --git a/pkg/sql/testdata/telemetry/planning b/pkg/sql/testdata/telemetry/planning index 891af9405c8c..c92f8a809cd2 100644 --- a/pkg/sql/testdata/telemetry/planning +++ b/pkg/sql/testdata/telemetry/planning @@ -16,6 +16,7 @@ sql.plan.explain-analyze sql.plan.explain-opt sql.plan.explain-opt-verbose sql.plan.explain-distsql +sql.plan.explain-gist ---- feature-usage @@ -47,6 +48,11 @@ EXPLAIN (OPT, VERBOSE) SELECT * FROM x ---- sql.plan.explain-opt-verbose +feature-usage +EXPLAIN (GIST) SELECT * FROM x +---- +sql.plan.explain-gist + # Tests for hints. feature-allowlist diff --git a/pkg/util/fast_int_set.go b/pkg/util/fast_int_set.go index 6c1db4ac6e16..05672f6b749a 100644 --- a/pkg/util/fast_int_set.go +++ b/pkg/util/fast_int_set.go @@ -14,6 +14,8 @@ package util import ( + "encoding/binary" + "io" "math/bits" "golang.org/x/tools/container/intsets" @@ -347,3 +349,66 @@ func (s *FastIntSet) Shift(delta int) FastIntSet { }) return result } + +// Encode the set to a Writer using binary.varint byte encoding. A zero +// precedes a small int containing s.small. A non-zero first value is a +// length and each bit encoded separately follows. +func (s *FastIntSet) Encode(wr io.Writer) error { + writeUint := func(u uint64) error { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], u) + _, err := wr.Write(buf[:n]) + return err + } + + if s.large == nil { + err := writeUint(0) + if err != nil { + return err + } + err = writeUint(s.small) + if err != nil { + return err + } + } else { + err := writeUint(uint64(s.Len())) + if err != nil { + return err + } + for i, ok := s.Next(0); ok; i, ok = s.Next(i + 1) { + err := writeUint(uint64(i)) + if err != nil { + return err + } + } + } + return nil +} + +// Decode does the opposite of Encode. Upon success the contents of s +// are overwritten. +func (s *FastIntSet) Decode(br io.ByteReader) error { + val, err := binary.ReadUvarint(br) + if err != nil { + return err + } + if val == 0 { + val, err = binary.ReadUvarint(br) + if err != nil { + return err + } + s.small = val + } else { + // Only mutate receiver if we read everything successfully. + temp := FastIntSet{} + for i, l := 0, int(val); i < l; i++ { + val, err = binary.ReadUvarint(br) + if err != nil { + return err + } + temp.Add(int(val)) + } + *s = temp + } + return nil +} diff --git a/pkg/util/hash.go b/pkg/util/hash.go index b767deda5eb1..b216163e38b4 100644 --- a/pkg/util/hash.go +++ b/pkg/util/hash.go @@ -39,6 +39,11 @@ func MakeFNV64() FNV64 { return FNV64{sum: fnvBase} } +// Init initializes FNV64 to starting value. +func (f *FNV64) Init() { + f.sum = fnvBase +} + // IsInitialized returns true if the hash struct was initialized, which happens // automatically when created through MakeFNV64 above. func (f *FNV64) IsInitialized() bool { From ab839c60ba8bead24470990143c713c720dd4c6f Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Fri, 15 Oct 2021 14:23:47 -0400 Subject: [PATCH 044/205] sql: update opt bench tests to inject a gist factory Release note: None --- pkg/sql/opt/bench/bench_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/sql/opt/bench/bench_test.go b/pkg/sql/opt/bench/bench_test.go index f5f6d93d3d8d..37bcd963f518 100644 --- a/pkg/sql/opt/bench/bench_test.go +++ b/pkg/sql/opt/bench/bench_test.go @@ -25,6 +25,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemaexpr" "github.com/cockroachdb/cockroach/pkg/sql/opt/exec" "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/execbuilder" + "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/explain" "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" "github.com/cockroachdb/cockroach/pkg/sql/opt/optbuilder" "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat" @@ -479,7 +480,7 @@ func (h *harness) runSimple(tb testing.TB, query benchQuery, phase Phase) { root := execMemo.RootExpr() eb := execbuilder.New( - exec.StubFactory{}, + explain.NewPlanGistFactory(exec.StubFactory{}), &h.optimizer, execMemo, nil, /* catalog */ @@ -532,7 +533,7 @@ func (h *harness) runPrepared(tb testing.TB, phase Phase) { root := execMemo.RootExpr() eb := execbuilder.New( - exec.StubFactory{}, + explain.NewPlanGistFactory(exec.StubFactory{}), &h.optimizer, execMemo, nil, /* catalog */ From 3c7855b8a74daaf14afc7f36b4aedb4d6044918c Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Fri, 15 Oct 2021 14:24:31 -0400 Subject: [PATCH 045/205] sql: add a session setting to disable gists Release note: None --- pkg/sql/exec_util.go | 4 + .../testdata/logic_test/information_schema | 1 + .../logictest/testdata/logic_test/pg_catalog | 3 + pkg/sql/logictest/testdata/logic_test/set | 6 + .../logictest/testdata/logic_test/show_source | 1 + .../exec/execbuilder/testdata/explain_gist | 25 ++ pkg/sql/plan_opt.go | 14 +- pkg/sql/plan_opt_test.go | 64 ++++ .../local_only_session_data.pb.go | 280 ++++++++++-------- .../local_only_session_data.proto | 2 + pkg/sql/vars.go | 17 ++ 11 files changed, 292 insertions(+), 125 deletions(-) diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index b6050ec6b4a2..adfc5b43e961 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -2720,6 +2720,10 @@ func (m *sessionDataMutator) SetSynchronousCommit(val bool) { m.data.SynchronousCommit = val } +func (m *sessionDataMutator) SetDisablePlanGists(val bool) { + m.data.DisablePlanGists = val +} + func (m *sessionDataMutator) SetDistSQLMode(val sessiondatapb.DistSQLExecMode) { m.data.DistSQLMode = val } diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index bf9e1d805d48..9b34ba712481 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -4625,6 +4625,7 @@ default_transaction_priority normal default_transaction_read_only off default_transaction_use_follower_reads off disable_partially_distributed_plans off +disable_plan_gists off disallow_full_table_scans off distsql_workmem 64 MiB enable_copying_partitioning_when_deinterleaving_table off diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index 879ead30685f..051d7535e765 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -3994,6 +3994,7 @@ default_transaction_priority normal NULL default_transaction_read_only off NULL NULL NULL string default_transaction_use_follower_reads off NULL NULL NULL string disable_partially_distributed_plans off NULL NULL NULL string +disable_plan_gists off NULL NULL NULL string disallow_full_table_scans off NULL NULL NULL string distsql off NULL NULL NULL string distsql_workmem 64 MiB NULL NULL NULL string @@ -4091,6 +4092,7 @@ default_transaction_priority normal NULL default_transaction_read_only off NULL user NULL off off default_transaction_use_follower_reads off NULL user NULL off off disable_partially_distributed_plans off NULL user NULL off off +disable_plan_gists off NULL user NULL off off disallow_full_table_scans off NULL user NULL off off distsql off NULL user NULL off off distsql_workmem 64 MiB NULL user NULL 64 MiB 64 MiB @@ -4184,6 +4186,7 @@ default_transaction_priority NULL NULL NULL default_transaction_read_only NULL NULL NULL NULL NULL default_transaction_use_follower_reads NULL NULL NULL NULL NULL disable_partially_distributed_plans NULL NULL NULL NULL NULL +disable_plan_gists NULL NULL NULL NULL NULL disallow_full_table_scans NULL NULL NULL NULL NULL distsql NULL NULL NULL NULL NULL distsql_workmem NULL NULL NULL NULL NULL diff --git a/pkg/sql/logictest/testdata/logic_test/set b/pkg/sql/logictest/testdata/logic_test/set index 0d24e04d61cb..70717b69af07 100644 --- a/pkg/sql/logictest/testdata/logic_test/set +++ b/pkg/sql/logictest/testdata/logic_test/set @@ -560,3 +560,9 @@ SET cluster setting sql.trace.txn.enable_threshold='1s' statement ok SET cluster setting sql.trace.txn.enable_threshold='0s' + +statement ok +SET disable_plan_gists = 'true' + +statement ok +SET disable_plan_gists = 'false' diff --git a/pkg/sql/logictest/testdata/logic_test/show_source b/pkg/sql/logictest/testdata/logic_test/show_source index 9406da4bcb4e..9a88a187bb0f 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_source +++ b/pkg/sql/logictest/testdata/logic_test/show_source @@ -41,6 +41,7 @@ default_transaction_priority normal default_transaction_read_only off default_transaction_use_follower_reads off disable_partially_distributed_plans off +disable_plan_gists off disallow_full_table_scans off distsql off distsql_workmem 64 MiB diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain_gist b/pkg/sql/opt/exec/execbuilder/testdata/explain_gist index 2791f9f813eb..19fcb6c05566 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain_gist +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain_gist @@ -19,6 +19,31 @@ SELECT crdb_internal.decode_plan_gist('$gist') table: t@t_pkey spans: FULL SCAN +# Test that EXPLAIN (GIST) still works if automatic gists are disabled. +statement ok +SET disable_plan_gists = 'true' + +let $gist +EXPLAIN (GIST) SELECT count(*) FROM t + +query T +SELECT * FROM crdb_internal.decode_plan_gist('$gist') +---- +• group (scalar) +│ +└── • scan + table: t@t_pkey + spans: FULL SCAN + +query T +SELECT crdb_internal.decode_plan_gist('$gist') +---- +• group (scalar) +│ +└── • scan + table: t@t_pkey + spans: FULL SCAN + statement error pq: unknown signature: crdb_internal\.decode_plan_gist\(int\) SELECT * FROM crdb_internal.decode_plan_gist(10) diff --git a/pkg/sql/plan_opt.go b/pkg/sql/plan_opt.go index 7c2aa53c0638..3c385ff189a0 100644 --- a/pkg/sql/plan_opt.go +++ b/pkg/sql/plan_opt.go @@ -564,9 +564,13 @@ func (opc *optPlanningCtx) runExecBuilder( var containsLargeFullTableScan bool var containsLargeFullIndexScan bool var containsMutation bool - gf := explain.NewPlanGistFactory(f) + var gf *explain.PlanGistFactory + if !opc.p.SessionData().DisablePlanGists { + gf = explain.NewPlanGistFactory(f) + f = gf + } if !planTop.instrumentation.ShouldBuildExplainPlan() { - bld := execbuilder.New(gf, &opc.optimizer, mem, &opc.catalog, mem.RootExpr(), evalCtx, allowAutoCommit) + bld := execbuilder.New(f, &opc.optimizer, mem, &opc.catalog, mem.RootExpr(), evalCtx, allowAutoCommit) plan, err := bld.Build() if err != nil { return err @@ -580,7 +584,7 @@ func (opc *optPlanningCtx) runExecBuilder( containsMutation = bld.ContainsMutation } else { // Create an explain factory and record the explain.Plan. - explainFactory := explain.NewFactory(gf) + explainFactory := explain.NewFactory(f) bld := execbuilder.New( explainFactory, &opc.optimizer, mem, &opc.catalog, mem.RootExpr(), evalCtx, allowAutoCommit, ) @@ -599,7 +603,9 @@ func (opc *optPlanningCtx) runExecBuilder( planTop.instrumentation.RecordExplainPlan(explainPlan) } - planTop.instrumentation.planGist = gf.PlanGist() + if gf != nil { + planTop.instrumentation.planGist = gf.PlanGist() + } if stmt.ExpectedTypes != nil { cols := result.main.planColumns() diff --git a/pkg/sql/plan_opt_test.go b/pkg/sql/plan_opt_test.go index 67e75c8e4fd5..ee54195e6495 100644 --- a/pkg/sql/plan_opt_test.go +++ b/pkg/sql/plan_opt_test.go @@ -19,6 +19,10 @@ import ( "time" "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/sql/parser" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" @@ -642,3 +646,63 @@ func BenchmarkQueryCache(b *testing.B) { }) } } + +func TestPlanGistControl(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + ctx := context.Background() + + s, _, db := serverutils.StartServer(t, base.TestServerArgs{}) + defer s.Stopper().Stop(ctx) + execCfg := s.ExecutorConfig().(ExecutorConfig) + internalPlanner, cleanup := NewInternalPlanner( + "test", + kv.NewTxn(ctx, db, s.NodeID()), + security.RootUserName(), + &MemoryMetrics{}, + &execCfg, + sessiondatapb.SessionData{}, + ) + defer cleanup() + + p := internalPlanner.(*planner) + stmt, err := parser.ParseOne("SELECT 1") + if err != nil { + t.Fatal(err) + } + p.stmt = makeStatement(stmt, ClusterWideID{}) + if err := p.makeOptimizerPlan(ctx); err != nil { + t.Fatal(err) + } + + if p.SessionData().DisablePlanGists { + t.Error("expected gists to be enabled by default") + } + + if p.instrumentation.planGist.String() == "" { + t.Error("expected gist by default") + } + + internalPlanner, cleanup = NewInternalPlanner( + "test", + kv.NewTxn(ctx, db, s.NodeID()), + security.RootUserName(), + &MemoryMetrics{}, + &execCfg, + sessiondatapb.SessionData{}, + ) + defer cleanup() + + p = internalPlanner.(*planner) + p.SessionData().DisablePlanGists = true + + p.stmt = makeStatement(stmt, ClusterWideID{}) + if err := p.makeOptimizerPlan(ctx); err != nil { + t.Fatal(err) + } + + if p.instrumentation.planGist.String() != "" { + t.Error("expected no gist") + } +} diff --git a/pkg/sql/sessiondatapb/local_only_session_data.pb.go b/pkg/sql/sessiondatapb/local_only_session_data.pb.go index 40043e7e08ac..e9faf5304c2d 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.pb.go +++ b/pkg/sql/sessiondatapb/local_only_session_data.pb.go @@ -217,6 +217,8 @@ type LocalOnlySessionData struct { // NULLS FIRST for ascending order by default, whereas postgres defaults // to NULLS LAST. NullOrderedLast bool `protobuf:"varint,55,opt,name=null_ordered_last,json=nullOrderedLast,proto3" json:"null_ordered_last,omitempty"` + // DisablePlanGists indicates whether we should disable automatic gists. + DisablePlanGists bool `protobuf:"varint,56,opt,name=disable_plan_gists,json=disablePlanGists,proto3" json:"disable_plan_gists,omitempty"` } func (m *LocalOnlySessionData) Reset() { *m = LocalOnlySessionData{} } @@ -304,127 +306,128 @@ func init() { } var fileDescriptor_21ead158cf36da28 = []byte{ - // 1911 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x57, 0x5b, 0x73, 0x1b, 0xb7, - 0x15, 0x36, 0xe3, 0x24, 0xb5, 0x21, 0xcb, 0x96, 0x36, 0x72, 0xb4, 0xb2, 0x6c, 0x51, 0xf2, 0x25, - 0x96, 0xe3, 0x84, 0x4c, 0x9c, 0x34, 0x75, 0xd3, 0xe9, 0x25, 0xba, 0xd5, 0x17, 0x25, 0x92, 0x97, - 0x72, 0x3c, 0x4d, 0x3b, 0x83, 0x81, 0x76, 0x0f, 0x49, 0x44, 0x20, 0xb0, 0x04, 0xb0, 0x92, 0xa8, - 0x87, 0xfe, 0x86, 0xce, 0xf4, 0xad, 0x33, 0xfd, 0x3f, 0x79, 0xcc, 0x63, 0x9e, 0x34, 0xad, 0xfc, - 0x2f, 0xfc, 0xd4, 0x39, 0x07, 0xbb, 0x24, 0x65, 0x2b, 0xe9, 0x1b, 0x79, 0xbe, 0xcb, 0x02, 0x67, - 0xcf, 0x39, 0xc0, 0xb2, 0xa6, 0xeb, 0xab, 0xa6, 0x03, 0xe7, 0xa4, 0xd1, 0x99, 0xf0, 0x22, 0xdf, - 0x6d, 0x2a, 0x93, 0x0a, 0xc5, 0x8d, 0x56, 0x03, 0x5e, 0x02, 0x1c, 0x91, 0x46, 0x6e, 0x8d, 0x37, - 0xd1, 0x7c, 0x6a, 0xd2, 0x3d, 0x6b, 0x44, 0xda, 0x6d, 0xb8, 0xbe, 0x6a, 0x9c, 0x92, 0x5e, 0x9b, - 0xe9, 0x98, 0x8e, 0x21, 0x5e, 0x13, 0x7f, 0x05, 0xc9, 0xcd, 0x7f, 0x5d, 0x67, 0x33, 0x9b, 0x68, - 0xba, 0xa5, 0xd5, 0xa0, 0x15, 0x04, 0x6b, 0xc2, 0x8b, 0xe8, 0x23, 0x16, 0x39, 0xb1, 0x0f, 0xdc, - 0x8b, 0x5d, 0x05, 0x8e, 0xe7, 0x16, 0xda, 0xf2, 0x30, 0xae, 0x2d, 0xd6, 0x96, 0x2f, 0x26, 0x53, - 0x88, 0xec, 0x10, 0xb0, 0x4d, 0xf1, 0xe8, 0xaf, 0x6c, 0xde, 0xe4, 0x5e, 0xf6, 0xe4, 0x11, 0x58, - 0xde, 0xde, 0xe3, 0xa9, 0x70, 0xa9, 0xc8, 0xc0, 0x71, 0x25, 0x7b, 0xd2, 0xc7, 0x6f, 0x2d, 0xd6, - 0x96, 0xcf, 0xaf, 0x5c, 0x3f, 0x39, 0xae, 0xc7, 0x5b, 0x15, 0x6d, 0xe3, 0xe9, 0x6a, 0x49, 0xda, - 0x44, 0x4e, 0x12, 0x0f, 0x0d, 0x36, 0xf6, 0x4e, 0x21, 0xd1, 0xe7, 0xec, 0x92, 0xf3, 0x3d, 0xcf, - 0xbd, 0xec, 0x81, 0x29, 0x7c, 0x7c, 0x9e, 0xdc, 0xa6, 0x5f, 0x1d, 0xd7, 0x27, 0x31, 0xd4, 0x58, - 0x2b, 0xac, 0xf0, 0xd2, 0xe8, 0x64, 0x02, 0x69, 0x3b, 0x81, 0x15, 0x3d, 0x62, 0xb3, 0x32, 0x53, - 0xc0, 0xa5, 0x1e, 0xa6, 0xaa, 0x32, 0x78, 0xfb, 0xe7, 0x0c, 0x66, 0x50, 0xf1, 0x58, 0x97, 0x79, - 0xa8, 0x9c, 0x38, 0xbb, 0x55, 0x39, 0x79, 0x2b, 0xb4, 0x13, 0x29, 0x92, 0xdf, 0x70, 0x7d, 0xe7, - 0xe7, 0x5c, 0xeb, 0xc1, 0x75, 0x67, 0xa4, 0x7d, 0xed, 0x01, 0x5f, 0xb0, 0x59, 0x6d, 0xbc, 0x4c, - 0x81, 0x67, 0xd2, 0xe5, 0x4a, 0xe0, 0xcb, 0xdd, 0x07, 0x2b, 0xfd, 0x20, 0x7e, 0x77, 0xb1, 0xb6, - 0x3c, 0x99, 0x5c, 0x0d, 0xf0, 0x5a, 0x40, 0x5b, 0x25, 0x18, 0x35, 0xd8, 0x7b, 0x16, 0x8c, 0xcd, - 0xc0, 0xf2, 0xef, 0x8d, 0xd4, 0x55, 0xb6, 0x7f, 0x85, 0x0b, 0x49, 0xa6, 0x4b, 0xe8, 0x09, 0x22, - 0x21, 0x91, 0x9f, 0xb0, 0x99, 0x0c, 0xda, 0xa2, 0x50, 0x9e, 0xfb, 0x43, 0xcd, 0x73, 0x2b, 0x0d, - 0x3d, 0xe4, 0x02, 0x09, 0xa2, 0x12, 0xdb, 0x39, 0xd4, 0xdb, 0x25, 0x12, 0x7d, 0xca, 0xae, 0x8e, - 0x2b, 0x2c, 0x88, 0x8c, 0xaa, 0x2f, 0xbe, 0xb8, 0x58, 0x5b, 0xbe, 0x30, 0x2e, 0x49, 0x40, 0x64, - 0x58, 0x43, 0xd1, 0x0a, 0x5b, 0x18, 0x97, 0x14, 0x0e, 0x78, 0xdb, 0x28, 0x65, 0x0e, 0xc0, 0x92, - 0xde, 0xc5, 0x8c, 0xb4, 0xd7, 0x46, 0xda, 0xe7, 0x0e, 0x36, 0x4a, 0x0a, 0xda, 0xb8, 0x68, 0x8b, - 0xdd, 0xce, 0x85, 0xf5, 0x52, 0x28, 0x35, 0xc0, 0x9c, 0x78, 0x2b, 0x77, 0x0b, 0x0f, 0x19, 0xcf, - 0x95, 0xd0, 0x0e, 0x23, 0x58, 0x7c, 0x59, 0x3c, 0x41, 0x4e, 0x4b, 0x43, 0xee, 0xda, 0x88, 0xba, - 0x8d, 0xcc, 0xb5, 0x92, 0x18, 0x3d, 0x64, 0xa3, 0xf2, 0xa2, 0x25, 0x75, 0xa5, 0xf3, 0xa6, 0x63, - 0x45, 0xcf, 0xc5, 0x97, 0xc8, 0xe4, 0xfd, 0x21, 0xfe, 0xdc, 0xc1, 0xa3, 0x21, 0x1a, 0xfd, 0x89, - 0xdd, 0x38, 0xad, 0xec, 0x15, 0xca, 0x4b, 0x9e, 0x1a, 0xc5, 0x9d, 0x17, 0xde, 0xc5, 0x93, 0x24, - 0x9f, 0x1b, 0x97, 0x7f, 0x8d, 0x94, 0x55, 0xa3, 0x5a, 0x48, 0x88, 0xbe, 0x64, 0x73, 0xd4, 0xb6, - 0xd2, 0x0f, 0x78, 0xc5, 0xca, 0xb8, 0x03, 0x61, 0xd3, 0x6e, 0x7c, 0x99, 0xd4, 0xb3, 0x15, 0xa1, - 0xea, 0x8e, 0xac, 0x45, 0x70, 0xb4, 0xc4, 0x2e, 0x39, 0xd1, 0x06, 0x5e, 0xe4, 0x99, 0xf0, 0xe0, - 0xe2, 0x2b, 0x44, 0x9f, 0xc0, 0xd8, 0xf3, 0x10, 0x8a, 0xfe, 0xc2, 0xe6, 0xb1, 0x39, 0xc1, 0x72, - 0x65, 0xcc, 0x5e, 0x91, 0x97, 0xa5, 0xd0, 0x36, 0xd8, 0x88, 0x2e, 0x9e, 0x42, 0xc5, 0xca, 0xfc, - 0xc9, 0x71, 0x7d, 0x76, 0x9b, 0x68, 0x9b, 0xc4, 0xa2, 0xaa, 0xd8, 0x30, 0x76, 0xe3, 0xa9, 0x4b, - 0x66, 0xf3, 0xb3, 0x80, 0x3d, 0x87, 0xf5, 0x75, 0x24, 0x3b, 0x47, 0xa2, 0x43, 0x9e, 0x1c, 0x74, - 0xc8, 0xfa, 0x34, 0x2d, 0x62, 0x3a, 0x40, 0xc8, 0x5f, 0x0f, 0x40, 0xf4, 0x15, 0xbb, 0x61, 0xa1, - 0x5f, 0x48, 0x0b, 0x1c, 0x0e, 0x73, 0x25, 0x53, 0xe9, 0xb1, 0xc8, 0x7a, 0xc2, 0x0e, 0xf8, 0x1e, - 0x0c, 0x5c, 0x1c, 0x85, 0x37, 0x5f, 0x92, 0xd6, 0x4b, 0xce, 0x76, 0xa0, 0x3c, 0x85, 0x81, 0xc3, - 0x56, 0x68, 0x1b, 0x9b, 0x02, 0xc7, 0x11, 0x93, 0x1b, 0xa9, 0x3d, 0xb7, 0xe0, 0xbc, 0xb0, 0x3e, - 0x7e, 0x8f, 0xc4, 0x57, 0x09, 0x6e, 0x55, 0x68, 0x12, 0xc0, 0xe8, 0x21, 0x9b, 0x13, 0x58, 0x41, - 0x38, 0xa8, 0x72, 0x61, 0x81, 0x0b, 0x87, 0xc9, 0xa6, 0x82, 0x89, 0x67, 0x82, 0x92, 0x08, 0xdb, - 0x01, 0xff, 0xca, 0x6d, 0xe5, 0x1e, 0x6b, 0x04, 0x37, 0xe9, 0xa1, 0x97, 0x57, 0x83, 0xae, 0xda, - 0xe4, 0xd5, 0xb0, 0x49, 0x84, 0xc2, 0xa4, 0xab, 0x36, 0xb9, 0xc5, 0x6e, 0xcb, 0x5e, 0xb9, 0xb9, - 0xd4, 0xa8, 0xa2, 0xa7, 0x39, 0xd5, 0x1f, 0xf6, 0xb5, 0xd4, 0x9d, 0xa1, 0xc1, 0xfb, 0xa1, 0x36, - 0x2b, 0xee, 0x2a, 0x51, 0xb7, 0xc7, 0x98, 0x95, 0xe1, 0x0b, 0x76, 0xcf, 0xec, 0x83, 0xb5, 0x32, - 0xab, 0x8a, 0xcb, 0x42, 0x07, 0x07, 0xcb, 0x91, 0xd1, 0xc0, 0x53, 0xa3, 0xdb, 0x72, 0xe4, 0x1a, - 0x93, 0xeb, 0xed, 0x4a, 0x40, 0x95, 0x96, 0x10, 0xfd, 0x3b, 0xa3, 0x61, 0x95, 0xc8, 0x95, 0xf1, - 0x1f, 0xd9, 0xf5, 0xae, 0x70, 0x5d, 0xee, 0xba, 0xc2, 0x66, 0x90, 0x71, 0xa9, 0x33, 0x38, 0x1c, - 0xdb, 0xe2, 0x5c, 0xa8, 0x5c, 0xe4, 0xb4, 0x02, 0xe5, 0x71, 0x60, 0x54, 0x06, 0xbf, 0x65, 0x73, - 0xd8, 0x6a, 0x94, 0xd7, 0x76, 0xa1, 0x54, 0xc8, 0x11, 0x77, 0xa9, 0xd0, 0x2e, 0xbe, 0x16, 0xda, - 0xa6, 0x22, 0x6c, 0x14, 0x4a, 0x51, 0xa2, 0x5a, 0x88, 0x46, 0xbf, 0x63, 0xd7, 0x86, 0x59, 0x72, - 0xa0, 0x20, 0xf5, 0x54, 0x91, 0xa1, 0x8e, 0xe3, 0xf9, 0x50, 0xf5, 0x15, 0xa3, 0x45, 0x84, 0x0d, - 0x63, 0x43, 0x4d, 0x47, 0xcb, 0x6c, 0x4a, 0x6a, 0x07, 0xd6, 0xf3, 0xb6, 0x70, 0x9e, 0xe7, 0xc2, - 0x77, 0xe3, 0xeb, 0x24, 0xb9, 0x1c, 0xe2, 0x1b, 0xc2, 0xf9, 0x6d, 0xe1, 0xbb, 0xd1, 0x23, 0xb6, - 0x24, 0x94, 0x07, 0x5b, 0xbd, 0x09, 0x3f, 0xc8, 0x81, 0x77, 0x40, 0x83, 0x15, 0x6a, 0xb8, 0xcf, - 0x1b, 0x24, 0xbd, 0x41, 0xc4, 0xf0, 0x1a, 0x76, 0x06, 0x39, 0xfc, 0x39, 0xb0, 0xaa, 0xbd, 0x7e, - 0xcc, 0x22, 0x37, 0xd0, 0x69, 0xd7, 0x1a, 0x6d, 0x0a, 0xc7, 0x53, 0xd3, 0xc3, 0x51, 0xba, 0x10, - 0xaa, 0x60, 0x0c, 0x59, 0x25, 0x20, 0xfa, 0x80, 0x5d, 0x09, 0xf6, 0xdc, 0x41, 0x9f, 0x32, 0x12, - 0xd7, 0x89, 0x3b, 0x19, 0xc2, 0x2d, 0xe8, 0x63, 0x22, 0xa2, 0x1d, 0x76, 0xb7, 0xe4, 0x15, 0x5a, - 0xf6, 0x0b, 0xe0, 0x07, 0xd2, 0x77, 0x4d, 0xe1, 0xc3, 0xcb, 0xc0, 0xb7, 0xeb, 0xbc, 0x15, 0x52, - 0x7b, 0x17, 0x2f, 0x91, 0xfe, 0x56, 0xa0, 0x3f, 0x27, 0xf6, 0x8b, 0x40, 0xa6, 0xd7, 0xb2, 0x3a, - 0xa2, 0x46, 0xbf, 0x67, 0xf3, 0xce, 0x17, 0xbb, 0x3c, 0x15, 0x5e, 0x28, 0xd3, 0x79, 0xbd, 0x76, - 0x6f, 0x92, 0x53, 0x8c, 0x94, 0xd5, 0xc0, 0x38, 0x5d, 0xc2, 0xcf, 0xd8, 0x1d, 0x38, 0xcc, 0xc1, - 0xca, 0x1e, 0x68, 0x2f, 0x14, 0x6e, 0x36, 0xa7, 0xf1, 0x5a, 0x66, 0xd1, 0xc2, 0x81, 0x95, 0x38, - 0x6e, 0x6e, 0xd1, 0x71, 0x7f, 0x73, 0x9c, 0xbc, 0x5a, 0x72, 0x43, 0x22, 0x93, 0x92, 0x19, 0xfd, - 0x8d, 0xdd, 0x4f, 0x4d, 0x3e, 0x38, 0xdd, 0x0a, 0x07, 0x5d, 0xd0, 0x3c, 0x03, 0xa9, 0x3d, 0x58, - 0x05, 0x62, 0x1f, 0x63, 0xb4, 0xd4, 0xf8, 0x36, 0xad, 0xf0, 0x2e, 0x4a, 0xc6, 0x5b, 0xe2, 0x45, - 0x17, 0xf4, 0xda, 0x29, 0x3e, 0x2d, 0x1c, 0x47, 0x68, 0x95, 0x6d, 0x6f, 0x41, 0xf4, 0xb8, 0x05, - 0xac, 0x1c, 0x3a, 0x5e, 0xe3, 0x3b, 0xa1, 0x98, 0xca, 0xbc, 0x13, 0x9e, 0x8c, 0xe0, 0x70, 0x48, - 0xba, 0x42, 0x79, 0xc7, 0x77, 0x8b, 0x36, 0xce, 0x49, 0x27, 0x8f, 0x20, 0xfe, 0xa0, 0x3a, 0x24, - 0x09, 0x5a, 0x21, 0xa4, 0x25, 0x8f, 0x00, 0x8f, 0x8a, 0xdc, 0x9a, 0x5c, 0x74, 0x84, 0xc7, 0x23, - 0x3f, 0x2f, 0x3c, 0xa7, 0x73, 0x54, 0xea, 0x4e, 0x7c, 0x37, 0xd4, 0xfc, 0x10, 0x7f, 0x8c, 0xf0, - 0x56, 0x89, 0x46, 0xff, 0xac, 0xb1, 0x53, 0xa9, 0xa2, 0x93, 0xcb, 0xf5, 0x15, 0x0d, 0x21, 0x4a, - 0x48, 0xcf, 0x64, 0x10, 0x2f, 0xd3, 0x3d, 0x61, 0xe3, 0xe4, 0xb8, 0x5e, 0x5f, 0x1f, 0x63, 0xe3, - 0xd9, 0xd5, 0x7a, 0xb6, 0xb9, 0x5d, 0x72, 0xbf, 0x36, 0x19, 0xbc, 0xfa, 0xff, 0x94, 0xa4, 0x0e, - 0xaf, 0x11, 0x5c, 0x5f, 0x8d, 0x13, 0xa2, 0x0d, 0x36, 0x89, 0xeb, 0xe0, 0xb8, 0x10, 0x7a, 0xfe, - 0x3d, 0x7a, 0xfe, 0xcd, 0x93, 0xe3, 0xfa, 0x44, 0x69, 0x58, 0x3e, 0xeb, 0x4a, 0xf9, 0x77, 0xfd, - 0x10, 0x52, 0xf2, 0x9e, 0x40, 0x61, 0xab, 0xaf, 0xc8, 0xe7, 0x05, 0x9b, 0x73, 0x60, 0xa5, 0x50, - 0x5c, 0x1b, 0xdb, 0x13, 0x4a, 0x1e, 0x51, 0x7e, 0x83, 0xe7, 0x87, 0xe4, 0x39, 0xff, 0xea, 0xb8, - 0x3e, 0xdb, 0x22, 0xd2, 0x37, 0xe3, 0x1c, 0x32, 0x9b, 0x75, 0x67, 0x03, 0xd1, 0x16, 0x9b, 0xd5, - 0x70, 0xc0, 0x5d, 0xda, 0x85, 0x9e, 0xe0, 0x69, 0x57, 0xe8, 0x0e, 0xd8, 0x60, 0x7b, 0x9f, 0x6c, - 0xe3, 0x57, 0xc7, 0xf5, 0x99, 0x6f, 0xe0, 0xa0, 0x45, 0x8c, 0xd5, 0x40, 0x20, 0xcf, 0x19, 0x7d, - 0x46, 0x34, 0xfa, 0x3b, 0xbb, 0xec, 0xa0, 0x5f, 0x80, 0x4e, 0x81, 0xa7, 0x22, 0xed, 0x42, 0xfc, - 0xd1, 0xe2, 0xf9, 0xe5, 0x89, 0x07, 0x6b, 0x8d, 0x5f, 0xb8, 0x1f, 0x37, 0xce, 0xba, 0x05, 0x37, - 0x5a, 0xa5, 0xcf, 0x2a, 0xda, 0xac, 0x6b, 0x6f, 0x07, 0xe1, 0x82, 0x77, 0x2a, 0x9e, 0x4c, 0xba, - 0xf1, 0xbf, 0xd1, 0x7d, 0x36, 0x9d, 0x2b, 0x91, 0x02, 0xbe, 0x93, 0x61, 0x4f, 0x7e, 0x4c, 0xa5, - 0x33, 0x35, 0x04, 0xaa, 0x5e, 0xcc, 0x59, 0x54, 0x5d, 0x24, 0x0b, 0x07, 0x96, 0xd3, 0xb5, 0x3c, - 0x6e, 0x60, 0xe3, 0xad, 0xac, 0xbc, 0x3a, 0xae, 0xff, 0xa1, 0x23, 0x7d, 0xb7, 0xd8, 0x6d, 0xa4, - 0xa6, 0xd7, 0x1c, 0x2e, 0x3f, 0xdb, 0x1d, 0xfd, 0x6e, 0xe6, 0x7b, 0x9d, 0xa6, 0x83, 0xb4, 0xc0, - 0x0b, 0x5b, 0xa3, 0xf5, 0x6c, 0xf3, 0xb9, 0x03, 0xab, 0x45, 0x0f, 0xb6, 0xd1, 0x29, 0x99, 0x2a, - 0xdd, 0x31, 0x4a, 0x91, 0xa8, 0xc9, 0x66, 0xe8, 0x2e, 0x67, 0x0e, 0x1c, 0xc7, 0xee, 0xf5, 0xa0, - 0xb9, 0x32, 0x9d, 0xb8, 0x19, 0x3a, 0xc2, 0x1f, 0xea, 0xc4, 0x1c, 0xb8, 0x17, 0x01, 0xd9, 0x34, - 0x9d, 0x33, 0x05, 0x60, 0x6d, 0xfc, 0xc9, 0x59, 0x82, 0x75, 0x6b, 0xa3, 0x7b, 0x6c, 0x7a, 0x28, - 0xa0, 0x2b, 0x23, 0xda, 0x7f, 0x4a, 0xec, 0xcb, 0x25, 0x1b, 0xef, 0x79, 0xe8, 0xfd, 0x06, 0x15, - 0x8d, 0x1f, 0xbc, 0x41, 0x45, 0xd7, 0x07, 0xec, 0xaa, 0x28, 0xbc, 0xe1, 0x16, 0xba, 0xa6, 0x37, - 0x7e, 0xd2, 0x7e, 0x46, 0xa9, 0x7d, 0x0f, 0xc1, 0xa4, 0xc4, 0xaa, 0xec, 0x2e, 0xb1, 0x4b, 0xd2, - 0x71, 0x57, 0xe4, 0x60, 0x31, 0xbb, 0xf1, 0xe7, 0xe1, 0xfe, 0x24, 0x5d, 0xab, 0x0a, 0xe1, 0xee, - 0x94, 0xb0, 0x1d, 0x08, 0x27, 0x1c, 0x4e, 0x72, 0x5a, 0x4d, 0xfc, 0xeb, 0xc5, 0xda, 0x72, 0x2d, - 0x99, 0x26, 0x0c, 0x0f, 0x37, 0x1c, 0xe7, 0xb8, 0x1c, 0x1c, 0xbe, 0x52, 0x7f, 0x8f, 0x27, 0x9a, - 0x05, 0x6f, 0x07, 0xb8, 0x62, 0x63, 0x47, 0xc3, 0xf7, 0x8b, 0x30, 0x7c, 0x03, 0x25, 0x41, 0xc6, - 0x3a, 0x11, 0xaa, 0x25, 0x7d, 0xc8, 0xa6, 0x35, 0x3e, 0x89, 0x86, 0x0a, 0x64, 0x5c, 0x09, 0xe7, - 0xe3, 0xdf, 0x90, 0xe8, 0x0a, 0x02, 0x5b, 0x21, 0xbe, 0x29, 0x9c, 0xbf, 0xd6, 0x67, 0xd1, 0x9b, - 0x15, 0x18, 0x4d, 0xb1, 0xf3, 0x7b, 0x30, 0xa0, 0x6f, 0xb1, 0xc9, 0x04, 0x7f, 0x46, 0xeb, 0xec, - 0x9d, 0x7d, 0xa1, 0x0a, 0xa0, 0x0f, 0xad, 0x89, 0x07, 0xcd, 0x5f, 0x2c, 0xf4, 0x37, 0x1d, 0x93, - 0xa0, 0xfe, 0xf2, 0xad, 0x87, 0xb5, 0x27, 0x6f, 0x5f, 0x98, 0x9d, 0x8a, 0x9f, 0xbc, 0x7d, 0x61, - 0x71, 0x6a, 0xe9, 0xe6, 0xbf, 0x6b, 0x67, 0x3e, 0xff, 0x0e, 0xbb, 0x4c, 0x6d, 0x95, 0xf1, 0x7d, - 0xb0, 0x68, 0x5c, 0x2e, 0x65, 0x32, 0x44, 0xbf, 0x0d, 0xc1, 0xe8, 0x16, 0x9b, 0x4c, 0x0b, 0x6b, - 0xb1, 0x09, 0x46, 0x8b, 0x3b, 0x9f, 0x5c, 0x2a, 0x83, 0xdf, 0x62, 0x2c, 0xba, 0xce, 0x2e, 0x4a, - 0x9d, 0x5a, 0x6a, 0x89, 0xf0, 0x61, 0x97, 0x8c, 0x02, 0xd1, 0x0d, 0xc6, 0x74, 0xd1, 0x0b, 0x72, - 0x17, 0x3e, 0xdb, 0x92, 0x8b, 0xba, 0xe8, 0x91, 0xd6, 0xad, 0x34, 0x7f, 0xf8, 0xef, 0xc2, 0xb9, - 0x1f, 0x4e, 0x16, 0x6a, 0x3f, 0x9e, 0x2c, 0xd4, 0x7e, 0x3a, 0x59, 0xa8, 0xfd, 0xe7, 0x64, 0xa1, - 0xf6, 0x8f, 0x97, 0x0b, 0xe7, 0x7e, 0x7c, 0xb9, 0x70, 0xee, 0xa7, 0x97, 0x0b, 0xe7, 0xbe, 0x9b, - 0x3c, 0xb5, 0xf5, 0xdd, 0x77, 0xa9, 0xbb, 0x3e, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3, - 0x3e, 0xa9, 0x54, 0x5a, 0x0f, 0x00, 0x00, + // 1930 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x57, 0xdd, 0x73, 0x1b, 0xb7, + 0x11, 0x37, 0xe3, 0x24, 0xb5, 0x21, 0xcb, 0x96, 0x2e, 0x72, 0x74, 0xb2, 0x6c, 0x51, 0xb2, 0x9d, + 0x58, 0x8e, 0x13, 0x32, 0x71, 0xd2, 0xd4, 0x4d, 0xa7, 0x1f, 0xd1, 0x57, 0xfc, 0xa1, 0x44, 0xf2, + 0x51, 0x8e, 0xa7, 0x69, 0x67, 0x30, 0xd0, 0xdd, 0x92, 0x44, 0x04, 0x02, 0x47, 0x00, 0x27, 0x89, + 0x7a, 0xe8, 0xdf, 0xd0, 0x99, 0x3e, 0xf7, 0xff, 0xf1, 0x63, 0x1e, 0xf3, 0xa4, 0x69, 0xe5, 0xff, + 0xc2, 0x4f, 0x9d, 0x5d, 0xdc, 0x91, 0x94, 0xad, 0xa4, 0x6f, 0xe4, 0xfe, 0x3e, 0x0e, 0xd8, 0xdb, + 0x5d, 0xe0, 0x58, 0xd3, 0xf5, 0x55, 0xd3, 0x81, 0x73, 0xd2, 0xe8, 0x4c, 0x78, 0x91, 0xef, 0x36, + 0x95, 0x49, 0x85, 0xe2, 0x46, 0xab, 0x01, 0x2f, 0x01, 0x8e, 0x48, 0x23, 0xb7, 0xc6, 0x9b, 0x68, + 0x3e, 0x35, 0xe9, 0x9e, 0x35, 0x22, 0xed, 0x36, 0x5c, 0x5f, 0x35, 0x4e, 0x49, 0xaf, 0xcd, 0x74, + 0x4c, 0xc7, 0x10, 0xaf, 0x89, 0xbf, 0x82, 0xe4, 0xe6, 0x8b, 0xeb, 0x6c, 0x66, 0x13, 0x4d, 0xb7, + 0xb4, 0x1a, 0xb4, 0x82, 0x60, 0x4d, 0x78, 0x11, 0x7d, 0xcc, 0x22, 0x27, 0xf6, 0x81, 0x7b, 0xb1, + 0xab, 0xc0, 0xf1, 0xdc, 0x42, 0x5b, 0x1e, 0xc6, 0xb5, 0xc5, 0xda, 0xf2, 0xc5, 0x64, 0x0a, 0x91, + 0x1d, 0x02, 0xb6, 0x29, 0x1e, 0xfd, 0x8d, 0xcd, 0x9b, 0xdc, 0xcb, 0x9e, 0x3c, 0x02, 0xcb, 0xdb, + 0x7b, 0x3c, 0x15, 0x2e, 0x15, 0x19, 0x38, 0xae, 0x64, 0x4f, 0xfa, 0xf8, 0xad, 0xc5, 0xda, 0xf2, + 0xf9, 0x95, 0xeb, 0x27, 0xc7, 0xf5, 0x78, 0xab, 0xa2, 0x6d, 0x3c, 0x59, 0x2d, 0x49, 0x9b, 0xc8, + 0x49, 0xe2, 0xa1, 0xc1, 0xc6, 0xde, 0x29, 0x24, 0xfa, 0x82, 0x5d, 0x72, 0xbe, 0xe7, 0xb9, 0x97, + 0x3d, 0x30, 0x85, 0x8f, 0xcf, 0x93, 0xdb, 0xf4, 0xab, 0xe3, 0xfa, 0x24, 0x86, 0x1a, 0x6b, 0x85, + 0x15, 0x5e, 0x1a, 0x9d, 0x4c, 0x20, 0x6d, 0x27, 0xb0, 0xa2, 0x87, 0x6c, 0x56, 0x66, 0x0a, 0xb8, + 0xd4, 0xc3, 0x54, 0x55, 0x06, 0x6f, 0xff, 0x92, 0xc1, 0x0c, 0x2a, 0x1e, 0xe9, 0x32, 0x0f, 0x95, + 0x13, 0x67, 0xb7, 0x2a, 0x27, 0x6f, 0x85, 0x76, 0x22, 0x45, 0xf2, 0x1b, 0xae, 0xef, 0xfc, 0x92, + 0x6b, 0x3d, 0xb8, 0xee, 0x8c, 0xb4, 0xaf, 0x3d, 0xe0, 0x4b, 0x36, 0xab, 0x8d, 0x97, 0x29, 0xf0, + 0x4c, 0xba, 0x5c, 0x09, 0x7c, 0xb9, 0xfb, 0x60, 0xa5, 0x1f, 0xc4, 0xef, 0x2e, 0xd6, 0x96, 0x27, + 0x93, 0xab, 0x01, 0x5e, 0x0b, 0x68, 0xab, 0x04, 0xa3, 0x06, 0x7b, 0xcf, 0x82, 0xb1, 0x19, 0x58, + 0xfe, 0xa3, 0x91, 0xba, 0xca, 0xf6, 0x6f, 0x70, 0x21, 0xc9, 0x74, 0x09, 0x3d, 0x46, 0x24, 0x24, + 0xf2, 0x53, 0x36, 0x93, 0x41, 0x5b, 0x14, 0xca, 0x73, 0x7f, 0xa8, 0x79, 0x6e, 0xa5, 0xa1, 0x87, + 0x5c, 0x20, 0x41, 0x54, 0x62, 0x3b, 0x87, 0x7a, 0xbb, 0x44, 0xa2, 0xcf, 0xd8, 0xd5, 0x71, 0x85, + 0x05, 0x91, 0x51, 0xf5, 0xc5, 0x17, 0x17, 0x6b, 0xcb, 0x17, 0xc6, 0x25, 0x09, 0x88, 0x0c, 0x6b, + 0x28, 0x5a, 0x61, 0x0b, 0xe3, 0x92, 0xc2, 0x01, 0x6f, 0x1b, 0xa5, 0xcc, 0x01, 0x58, 0xd2, 0xbb, + 0x98, 0x91, 0xf6, 0xda, 0x48, 0xfb, 0xcc, 0xc1, 0x46, 0x49, 0x41, 0x1b, 0x17, 0x6d, 0xb1, 0xdb, + 0xb9, 0xb0, 0x5e, 0x0a, 0xa5, 0x06, 0x98, 0x13, 0x6f, 0xe5, 0x6e, 0xe1, 0x21, 0xe3, 0xb9, 0x12, + 0xda, 0x61, 0x04, 0x8b, 0x2f, 0x8b, 0x27, 0xc8, 0x69, 0x69, 0xc8, 0x5d, 0x1b, 0x51, 0xb7, 0x91, + 0xb9, 0x56, 0x12, 0xa3, 0x07, 0x6c, 0x54, 0x5e, 0xb4, 0xa4, 0xae, 0x74, 0xde, 0x74, 0xac, 0xe8, + 0xb9, 0xf8, 0x12, 0x99, 0xbc, 0x3f, 0xc4, 0x9f, 0x39, 0x78, 0x38, 0x44, 0xa3, 0xbf, 0xb0, 0x1b, + 0xa7, 0x95, 0xbd, 0x42, 0x79, 0xc9, 0x53, 0xa3, 0xb8, 0xf3, 0xc2, 0xbb, 0x78, 0x92, 0xe4, 0x73, + 0xe3, 0xf2, 0x6f, 0x91, 0xb2, 0x6a, 0x54, 0x0b, 0x09, 0xd1, 0x57, 0x6c, 0x8e, 0xda, 0x56, 0xfa, + 0x01, 0xaf, 0x58, 0x19, 0x77, 0x20, 0x6c, 0xda, 0x8d, 0x2f, 0x93, 0x7a, 0xb6, 0x22, 0x54, 0xdd, + 0x91, 0xb5, 0x08, 0x8e, 0x96, 0xd8, 0x25, 0x27, 0xda, 0xc0, 0x8b, 0x3c, 0x13, 0x1e, 0x5c, 0x7c, + 0x85, 0xe8, 0x13, 0x18, 0x7b, 0x16, 0x42, 0xd1, 0x5f, 0xd9, 0x3c, 0x36, 0x27, 0x58, 0xae, 0x8c, + 0xd9, 0x2b, 0xf2, 0xb2, 0x14, 0xda, 0x06, 0x1b, 0xd1, 0xc5, 0x53, 0xa8, 0x58, 0x99, 0x3f, 0x39, + 0xae, 0xcf, 0x6e, 0x13, 0x6d, 0x93, 0x58, 0x54, 0x15, 0x1b, 0xc6, 0x6e, 0x3c, 0x71, 0xc9, 0x6c, + 0x7e, 0x16, 0xb0, 0xe7, 0xb0, 0xbe, 0x8e, 0x64, 0xe7, 0x48, 0x74, 0xc8, 0x93, 0x83, 0x0e, 0x59, + 0x9f, 0xa6, 0x45, 0x4c, 0x07, 0x08, 0xf9, 0xeb, 0x01, 0x88, 0xbe, 0x66, 0x37, 0x2c, 0xf4, 0x0b, + 0x69, 0x81, 0xc3, 0x61, 0xae, 0x64, 0x2a, 0x3d, 0x16, 0x59, 0x4f, 0xd8, 0x01, 0xdf, 0x83, 0x81, + 0x8b, 0xa3, 0xf0, 0xe6, 0x4b, 0xd2, 0x7a, 0xc9, 0xd9, 0x0e, 0x94, 0x27, 0x30, 0x70, 0xd8, 0x0a, + 0x6d, 0x63, 0x53, 0xe0, 0x38, 0x62, 0x72, 0x23, 0xb5, 0xe7, 0x16, 0x9c, 0x17, 0xd6, 0xc7, 0xef, + 0x91, 0xf8, 0x2a, 0xc1, 0xad, 0x0a, 0x4d, 0x02, 0x18, 0x3d, 0x60, 0x73, 0x02, 0x2b, 0x08, 0x07, + 0x55, 0x2e, 0x2c, 0x70, 0xe1, 0x30, 0xd9, 0x54, 0x30, 0xf1, 0x4c, 0x50, 0x12, 0x61, 0x3b, 0xe0, + 0x5f, 0xbb, 0xad, 0xdc, 0x63, 0x8d, 0xe0, 0x26, 0x3d, 0xf4, 0xf2, 0x6a, 0xd0, 0x55, 0x9b, 0xbc, + 0x1a, 0x36, 0x89, 0x50, 0x98, 0x74, 0xd5, 0x26, 0xb7, 0xd8, 0x6d, 0xd9, 0x2b, 0x37, 0x97, 0x1a, + 0x55, 0xf4, 0x34, 0xa7, 0xfa, 0xc3, 0xbe, 0x96, 0xba, 0x33, 0x34, 0x78, 0x3f, 0xd4, 0x66, 0xc5, + 0x5d, 0x25, 0xea, 0xf6, 0x18, 0xb3, 0x32, 0x7c, 0xce, 0xee, 0x9a, 0x7d, 0xb0, 0x56, 0x66, 0x55, + 0x71, 0x59, 0xe8, 0xe0, 0x60, 0x39, 0x32, 0x1a, 0x78, 0x6a, 0x74, 0x5b, 0x8e, 0x5c, 0x63, 0x72, + 0xbd, 0x5d, 0x09, 0xa8, 0xd2, 0x12, 0xa2, 0xff, 0x60, 0x34, 0xac, 0x12, 0xb9, 0x32, 0xfe, 0x33, + 0xbb, 0xde, 0x15, 0xae, 0xcb, 0x5d, 0x57, 0xd8, 0x0c, 0x32, 0x2e, 0x75, 0x06, 0x87, 0x63, 0x5b, + 0x9c, 0x0b, 0x95, 0x8b, 0x9c, 0x56, 0xa0, 0x3c, 0x0a, 0x8c, 0xca, 0xe0, 0xf7, 0x6c, 0x0e, 0x5b, + 0x8d, 0xf2, 0xda, 0x2e, 0x94, 0x0a, 0x39, 0xe2, 0x2e, 0x15, 0xda, 0xc5, 0xd7, 0x42, 0xdb, 0x54, + 0x84, 0x8d, 0x42, 0x29, 0x4a, 0x54, 0x0b, 0xd1, 0xe8, 0x0f, 0xec, 0xda, 0x30, 0x4b, 0x0e, 0x14, + 0xa4, 0x9e, 0x2a, 0x32, 0xd4, 0x71, 0x3c, 0x1f, 0xaa, 0xbe, 0x62, 0xb4, 0x88, 0xb0, 0x61, 0x6c, + 0xa8, 0xe9, 0x68, 0x99, 0x4d, 0x49, 0xed, 0xc0, 0x7a, 0xde, 0x16, 0xce, 0xf3, 0x5c, 0xf8, 0x6e, + 0x7c, 0x9d, 0x24, 0x97, 0x43, 0x7c, 0x43, 0x38, 0xbf, 0x2d, 0x7c, 0x37, 0x7a, 0xc8, 0x96, 0x84, + 0xf2, 0x60, 0xab, 0x37, 0xe1, 0x07, 0x39, 0xf0, 0x0e, 0x68, 0xb0, 0x42, 0x0d, 0xf7, 0x79, 0x83, + 0xa4, 0x37, 0x88, 0x18, 0x5e, 0xc3, 0xce, 0x20, 0x87, 0x6f, 0x02, 0xab, 0xda, 0xeb, 0x27, 0x2c, + 0x72, 0x03, 0x9d, 0x76, 0xad, 0xd1, 0xa6, 0x70, 0x3c, 0x35, 0x3d, 0x1c, 0xa5, 0x0b, 0xa1, 0x0a, + 0xc6, 0x90, 0x55, 0x02, 0xa2, 0x0f, 0xd9, 0x95, 0x60, 0xcf, 0x1d, 0xf4, 0x29, 0x23, 0x71, 0x9d, + 0xb8, 0x93, 0x21, 0xdc, 0x82, 0x3e, 0x26, 0x22, 0xda, 0x61, 0x77, 0x4a, 0x5e, 0xa1, 0x65, 0xbf, + 0x00, 0x7e, 0x20, 0x7d, 0xd7, 0x14, 0x3e, 0xbc, 0x0c, 0x7c, 0xbb, 0xce, 0x5b, 0x21, 0xb5, 0x77, + 0xf1, 0x12, 0xe9, 0x6f, 0x05, 0xfa, 0x33, 0x62, 0x3f, 0x0f, 0x64, 0x7a, 0x2d, 0xab, 0x23, 0x6a, + 0xf4, 0x47, 0x36, 0xef, 0x7c, 0xb1, 0xcb, 0x53, 0xe1, 0x85, 0x32, 0x9d, 0xd7, 0x6b, 0xf7, 0x26, + 0x39, 0xc5, 0x48, 0x59, 0x0d, 0x8c, 0xd3, 0x25, 0xfc, 0x94, 0x7d, 0x00, 0x87, 0x39, 0x58, 0xd9, + 0x03, 0xed, 0x85, 0xc2, 0xcd, 0xe6, 0x34, 0x5e, 0xcb, 0x2c, 0x5a, 0x38, 0xb0, 0x12, 0xc7, 0xcd, + 0x2d, 0x3a, 0xee, 0x6f, 0x8e, 0x93, 0x57, 0x4b, 0x6e, 0x48, 0x64, 0x52, 0x32, 0xa3, 0xbf, 0xb3, + 0x7b, 0xa9, 0xc9, 0x07, 0xa7, 0x5b, 0xe1, 0xa0, 0x0b, 0x9a, 0x67, 0x20, 0xb5, 0x07, 0xab, 0x40, + 0xec, 0x63, 0x8c, 0x96, 0x1a, 0xdf, 0xa6, 0x15, 0xde, 0x41, 0xc9, 0x78, 0x4b, 0x3c, 0xef, 0x82, + 0x5e, 0x3b, 0xc5, 0xa7, 0x85, 0xe3, 0x08, 0xad, 0xb2, 0xed, 0x2d, 0x88, 0x1e, 0xb7, 0x80, 0x95, + 0x43, 0xc7, 0x6b, 0xfc, 0x41, 0x28, 0xa6, 0x32, 0xef, 0x84, 0x27, 0x23, 0x38, 0x1c, 0x92, 0xae, + 0x50, 0xde, 0xf1, 0xdd, 0xa2, 0x8d, 0x73, 0xd2, 0xc9, 0x23, 0x88, 0x3f, 0xac, 0x0e, 0x49, 0x82, + 0x56, 0x08, 0x69, 0xc9, 0x23, 0xc0, 0xa3, 0x22, 0xb7, 0x26, 0x17, 0x1d, 0xe1, 0xf1, 0xc8, 0xcf, + 0x0b, 0xcf, 0xe9, 0x1c, 0x95, 0xba, 0x13, 0xdf, 0x09, 0x35, 0x3f, 0xc4, 0x1f, 0x21, 0xbc, 0x55, + 0xa2, 0xd1, 0xbf, 0x6a, 0xec, 0x54, 0xaa, 0xe8, 0xe4, 0x72, 0x7d, 0x45, 0x43, 0x88, 0x12, 0xd2, + 0x33, 0x19, 0xc4, 0xcb, 0x74, 0x4f, 0xd8, 0x38, 0x39, 0xae, 0xd7, 0xd7, 0xc7, 0xd8, 0x78, 0x76, + 0xb5, 0x9e, 0x6e, 0x6e, 0x97, 0xdc, 0x6f, 0x4d, 0x06, 0xaf, 0xfe, 0x3f, 0x25, 0xa9, 0xc3, 0x6b, + 0x04, 0xd7, 0x57, 0xe3, 0x84, 0x68, 0x83, 0x4d, 0xe2, 0x3a, 0x38, 0x2e, 0x84, 0x9e, 0x7f, 0x97, + 0x9e, 0x7f, 0xf3, 0xe4, 0xb8, 0x3e, 0x51, 0x1a, 0x96, 0xcf, 0xba, 0x52, 0xfe, 0x5d, 0x3f, 0x84, + 0x94, 0xbc, 0x27, 0x50, 0xd8, 0xea, 0x2b, 0xf2, 0x79, 0xce, 0xe6, 0x1c, 0x58, 0x29, 0x14, 0xd7, + 0xc6, 0xf6, 0x84, 0x92, 0x47, 0x94, 0xdf, 0xe0, 0xf9, 0x11, 0x79, 0xce, 0xbf, 0x3a, 0xae, 0xcf, + 0xb6, 0x88, 0xf4, 0xdd, 0x38, 0x87, 0xcc, 0x66, 0xdd, 0xd9, 0x40, 0xb4, 0xc5, 0x66, 0x35, 0x1c, + 0x70, 0x97, 0x76, 0xa1, 0x27, 0x78, 0xda, 0x15, 0xba, 0x03, 0x36, 0xd8, 0xde, 0x23, 0xdb, 0xf8, + 0xd5, 0x71, 0x7d, 0xe6, 0x3b, 0x38, 0x68, 0x11, 0x63, 0x35, 0x10, 0xc8, 0x73, 0x46, 0x9f, 0x11, + 0x8d, 0xfe, 0xc1, 0x2e, 0x3b, 0xe8, 0x17, 0xa0, 0x53, 0xe0, 0xa9, 0x48, 0xbb, 0x10, 0x7f, 0xbc, + 0x78, 0x7e, 0x79, 0xe2, 0xfe, 0x5a, 0xe3, 0x57, 0xee, 0xc7, 0x8d, 0xb3, 0x6e, 0xc1, 0x8d, 0x56, + 0xe9, 0xb3, 0x8a, 0x36, 0xeb, 0xda, 0xdb, 0x41, 0xb8, 0xe0, 0x9d, 0x8a, 0x27, 0x93, 0x6e, 0xfc, + 0x6f, 0x74, 0x8f, 0x4d, 0xe7, 0x4a, 0xa4, 0x80, 0xef, 0x64, 0xd8, 0x93, 0x9f, 0x50, 0xe9, 0x4c, + 0x0d, 0x81, 0xaa, 0x17, 0x73, 0x16, 0x55, 0x17, 0xc9, 0xc2, 0x81, 0xe5, 0x74, 0x2d, 0x8f, 0x1b, + 0xd8, 0x78, 0x2b, 0x2b, 0xaf, 0x8e, 0xeb, 0x7f, 0xea, 0x48, 0xdf, 0x2d, 0x76, 0x1b, 0xa9, 0xe9, + 0x35, 0x87, 0xcb, 0xcf, 0x76, 0x47, 0xbf, 0x9b, 0xf9, 0x5e, 0xa7, 0xe9, 0x20, 0x2d, 0xf0, 0xc2, + 0xd6, 0x68, 0x3d, 0xdd, 0x7c, 0xe6, 0xc0, 0x6a, 0xd1, 0x83, 0x6d, 0x74, 0x4a, 0xa6, 0x4a, 0x77, + 0x8c, 0x52, 0x24, 0x6a, 0xb2, 0x19, 0xba, 0xcb, 0x99, 0x03, 0xc7, 0xb1, 0x7b, 0x3d, 0x68, 0xae, + 0x4c, 0x27, 0x6e, 0x86, 0x8e, 0xf0, 0x87, 0x3a, 0x31, 0x07, 0xee, 0x79, 0x40, 0x36, 0x4d, 0xe7, + 0x4c, 0x01, 0x58, 0x1b, 0x7f, 0x7a, 0x96, 0x60, 0xdd, 0xda, 0xe8, 0x2e, 0x9b, 0x1e, 0x0a, 0xe8, + 0xca, 0x88, 0xf6, 0x9f, 0x11, 0xfb, 0x72, 0xc9, 0xc6, 0x7b, 0x1e, 0x7a, 0xbf, 0x41, 0x45, 0xe3, + 0xfb, 0x6f, 0x50, 0xd1, 0xf5, 0x3e, 0xbb, 0x2a, 0x0a, 0x6f, 0xb8, 0x85, 0xae, 0xe9, 0x8d, 0x9f, + 0xb4, 0x9f, 0x53, 0x6a, 0xdf, 0x43, 0x30, 0x29, 0xb1, 0x2a, 0xbb, 0x4b, 0xec, 0x92, 0x74, 0xdc, + 0x15, 0x39, 0x58, 0xcc, 0x6e, 0xfc, 0x45, 0xb8, 0x3f, 0x49, 0xd7, 0xaa, 0x42, 0xb8, 0x3b, 0x25, + 0x6c, 0x07, 0xc2, 0x09, 0x87, 0x93, 0x9c, 0x56, 0x13, 0xff, 0x76, 0xb1, 0xb6, 0x5c, 0x4b, 0xa6, + 0x09, 0xc3, 0xc3, 0x0d, 0xc7, 0x39, 0x2e, 0x07, 0x87, 0xaf, 0xd4, 0x3f, 0xe2, 0x89, 0x66, 0xc1, + 0xdb, 0x01, 0xae, 0xd8, 0xd8, 0xd1, 0xf0, 0xfd, 0x32, 0x0c, 0xdf, 0x40, 0x49, 0x90, 0xb1, 0x4e, + 0x84, 0x6a, 0x49, 0x1f, 0xb1, 0x69, 0x8d, 0x4f, 0xa2, 0xa1, 0x02, 0x19, 0x57, 0xc2, 0xf9, 0xf8, + 0x77, 0x24, 0xba, 0x82, 0xc0, 0x56, 0x88, 0x6f, 0x0a, 0xe7, 0xf1, 0x23, 0xac, 0xbc, 0xeb, 0xd2, + 0x0c, 0xe1, 0x1d, 0xec, 0xef, 0xf8, 0x41, 0x28, 0xa5, 0x12, 0xc1, 0x66, 0xff, 0x06, 0xe3, 0xd7, + 0xfa, 0x2c, 0x7a, 0xb3, 0x5e, 0xa3, 0x29, 0x76, 0x7e, 0x0f, 0x06, 0xf4, 0xe5, 0x36, 0x99, 0xe0, + 0xcf, 0x68, 0x9d, 0xbd, 0xb3, 0x2f, 0x54, 0x01, 0xf4, 0x59, 0x36, 0x71, 0xbf, 0xf9, 0xab, 0x6d, + 0xf1, 0xa6, 0x63, 0x12, 0xd4, 0x5f, 0xbd, 0xf5, 0xa0, 0xf6, 0xf8, 0xed, 0x0b, 0xb3, 0x53, 0xf1, + 0xe3, 0xb7, 0x2f, 0x2c, 0x4e, 0x2d, 0xdd, 0xfc, 0x77, 0xed, 0xcc, 0xe7, 0x7f, 0xc0, 0x2e, 0x53, + 0x13, 0x66, 0x7c, 0x1f, 0x2c, 0x1a, 0x97, 0x4b, 0x99, 0x0c, 0xd1, 0xef, 0x43, 0x30, 0xba, 0xc5, + 0x26, 0xd3, 0xc2, 0x5a, 0x6c, 0x99, 0xd1, 0xe2, 0xce, 0x27, 0x97, 0xca, 0xe0, 0xf7, 0x18, 0x8b, + 0xae, 0xb3, 0x8b, 0x52, 0xa7, 0x96, 0x1a, 0x28, 0x7c, 0x06, 0x26, 0xa3, 0x40, 0x74, 0x83, 0x31, + 0x5d, 0xf4, 0x82, 0xdc, 0x85, 0x8f, 0xbc, 0xe4, 0xa2, 0x2e, 0x7a, 0xa4, 0x75, 0x2b, 0xcd, 0x17, + 0xff, 0x5d, 0x38, 0xf7, 0xe2, 0x64, 0xa1, 0xf6, 0xd3, 0xc9, 0x42, 0xed, 0xe7, 0x93, 0x85, 0xda, + 0x7f, 0x4e, 0x16, 0x6a, 0xff, 0x7c, 0xb9, 0x70, 0xee, 0xa7, 0x97, 0x0b, 0xe7, 0x7e, 0x7e, 0xb9, + 0x70, 0xee, 0x87, 0xc9, 0x53, 0x5b, 0xdf, 0x7d, 0x97, 0x7a, 0xf1, 0xf3, 0xff, 0x05, 0x00, 0x00, + 0xff, 0xff, 0x9f, 0x8b, 0xd4, 0xd9, 0x88, 0x0f, 0x00, 0x00, } func (m *LocalOnlySessionData) Marshal() (dAtA []byte, err error) { @@ -447,6 +450,18 @@ func (m *LocalOnlySessionData) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.DisablePlanGists { + i-- + if m.DisablePlanGists { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x3 + i-- + dAtA[i] = 0xc0 + } if m.NullOrderedLast { i-- if m.NullOrderedLast { @@ -1214,6 +1229,9 @@ func (m *LocalOnlySessionData) Size() (n int) { if m.NullOrderedLast { n += 3 } + if m.DisablePlanGists { + n += 3 + } return n } @@ -2439,6 +2457,26 @@ func (m *LocalOnlySessionData) Unmarshal(dAtA []byte) error { } } m.NullOrderedLast = bool(v != 0) + case 56: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DisablePlanGists", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalOnlySessionData + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DisablePlanGists = bool(v != 0) default: iNdEx = preIndex skippy, err := skipLocalOnlySessionData(dAtA[iNdEx:]) diff --git a/pkg/sql/sessiondatapb/local_only_session_data.proto b/pkg/sql/sessiondatapb/local_only_session_data.proto index c163caae433b..e82aee942c33 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.proto +++ b/pkg/sql/sessiondatapb/local_only_session_data.proto @@ -211,6 +211,8 @@ message LocalOnlySessionData { // NULLS FIRST for ascending order by default, whereas postgres defaults // to NULLS LAST. bool null_ordered_last = 55; + // DisablePlanGists indicates whether we should disable automatic gists. + bool disable_plan_gists = 56; /////////////////////////////////////////////////////////////////////////// // WARNING: consider whether a session parameter you're adding needs to // diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index ff55cc815494..7d797b45e52a 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -454,6 +454,23 @@ var varGen = map[string]sessionVar{ GlobalDefault: globalFalse, }, + // CockroachDB extension. + `disable_plan_gists`: { + GetStringVal: makePostgresBoolGetStringValFn(`disable_plan_gists`), + Set: func(_ context.Context, m sessionDataMutator, s string) error { + b, err := paramparse.ParseBoolVar("disable_plan_gists", s) + if err != nil { + return err + } + m.SetDisablePlanGists(b) + return nil + }, + Get: func(evalCtx *extendedEvalContext) string { + return formatBoolAsPostgresSetting(evalCtx.SessionData().DisablePlanGists) + }, + GlobalDefault: globalFalse, + }, + // CockroachDB extension. `distsql`: { Set: func(_ context.Context, m sessionDataMutator, s string) error { From 8f804f879bca7bd054f542ada532612b5282c896 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Mon, 25 Oct 2021 18:36:01 -0400 Subject: [PATCH 046/205] ui: default filter on Transaction and Statement page exclude internals Previously, the default value when no App filter was selected on Transactions and Statements page, we were showing all data, now we're excluding internal transaction/statements. Fixes #70544 Release note (ui change): The default value when no App is selected on Transactions and Statements filter is now excluding internal Transactions and Statements. --- .../statementsPage.selectors.ts | 5 +++ .../src/transactionsPage/utils.spec.ts | 14 ++++---- .../cluster-ui/src/transactionsPage/utils.ts | 34 +++++++++++-------- .../src/views/statements/statements.spec.tsx | 4 +-- .../src/views/statements/statementsPage.tsx | 5 +++ 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts index 86744b98aa4a..76cdb5a663bf 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.selectors.ts @@ -169,6 +169,11 @@ export const selectStatements = createSelector( (showInternal && isInternal(statement)) || criteria.includes(statement.app), ); + } else { + // We don't want to show internal statements by default. + statements = statements.filter( + (statement: ExecutionStatistics) => !isInternal(statement), + ); } const statsByStatementKey: { diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.spec.ts b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.spec.ts index 3d04aa7491e5..edc7240d3b5d 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.spec.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.spec.ts @@ -44,7 +44,7 @@ describe("getStatementsByFingerprintIdAndTime", () => { const txData = (data.transactions as any) as Transaction[]; describe("Filter transactions", () => { - it("show all if no filters applied", () => { + it("show non internal if no filters applied", () => { const filter: Filters = { app: "", timeNumber: "0", @@ -61,7 +61,7 @@ describe("Filter transactions", () => { nodeRegions, false, ).transactions.length, - 11, + 4, ); }); @@ -151,7 +151,7 @@ describe("Filter transactions", () => { it("filters by time", () => { const filter: Filters = { - app: "", + app: "$ internal,$ TEST", timeNumber: "40", timeUnit: "miliseconds", nodes: "", @@ -172,7 +172,7 @@ describe("Filter transactions", () => { it("filters by one node", () => { const filter: Filters = { - app: "", + app: "$ internal,$ TEST", timeNumber: "0", timeUnit: "seconds", nodes: "n1", @@ -193,7 +193,7 @@ describe("Filter transactions", () => { it("filters by multiple nodes", () => { const filter: Filters = { - app: "", + app: "$ internal,$ TEST,$ TEST EXACT", timeNumber: "0", timeUnit: "seconds", nodes: "n2,n4", @@ -214,7 +214,7 @@ describe("Filter transactions", () => { it("filters by one region", () => { const filter: Filters = { - app: "", + app: "$ internal,$ TEST", timeNumber: "0", timeUnit: "seconds", nodes: "", @@ -235,7 +235,7 @@ describe("Filter transactions", () => { it("filters by multiple regions", () => { const filter: Filters = { - app: "", + app: "$ internal,$ TEST,$ TEST EXACT", timeNumber: "0", timeUnit: "seconds", nodes: "", diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts index 9acec70ccad6..38f710486e94 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts @@ -157,26 +157,32 @@ export const filterTransactions = ( // Return transactions filtered by the values selected on the filter. A // transaction must match all selected filters. + // We don't want to show statements that are internal or with unset App names by default. // Current filters: app, service latency, nodes and regions. const filteredTransactions = data .filter((t: Transaction) => { const isInternal = (t: Transaction) => t.stats_data.app.startsWith(internalAppNamePrefix); - const apps = filters.app.split(","); - let showInternal = false; - if (apps.includes(internalAppNamePrefix)) { - showInternal = true; - } - if (apps.includes("(unset)")) { - apps.push(""); - } - return ( - filters.app === "" || - (showInternal && isInternal(t)) || - t.stats_data.app === filters.app || - apps.includes(t.stats_data.app) - ); + if (filters.app && filters.app != "All") { + const apps = filters.app.split(","); + let showInternal = false; + if (apps.includes(internalAppNamePrefix)) { + showInternal = true; + } + if (apps.includes("(unset)")) { + apps.push(""); + } + + return ( + (showInternal && isInternal(t)) || + t.stats_data.app === filters.app || + apps.includes(t.stats_data.app) + ); + } else { + // We don't want to show internal transactions by default. + return !isInternal(t); + } }) .filter( (t: Transaction) => diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx index 56b242e4cfce..adcec10c0b6e 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx @@ -65,7 +65,7 @@ describe("selectStatements", () => { assert.deepEqual(actualFingerprints, expectedFingerprints); }); - it("returns the statements with Internal for default ALL filter", () => { + it("returns the statements without Internal for default ALL filter", () => { const stmtA = makeFingerprint(1); const stmtB = makeFingerprint(2, INTERNAL_STATEMENT_PREFIX); const stmtC = makeFingerprint(3, INTERNAL_STATEMENT_PREFIX); @@ -75,7 +75,7 @@ describe("selectStatements", () => { const result = selectStatements(state, props); - assert.equal(result.length, 3); + assert.equal(result.length, 2); }); it("coalesces statements from different apps", () => { diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx index 9d05834fdc1e..5b678ca6bdfb 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statementsPage.tsx @@ -100,6 +100,11 @@ export const selectStatements = createSelector( (showInternal && isInternal(statement)) || criteria.includes(statement.app), ); + } else { + // We don't want to show internal statements by default. + statements = statements.filter( + (statement: ExecutionStatistics) => !isInternal(statement), + ); } const statsByStatementKey: { From 9b223d4d0a668b5b80c5ca92fdb1718e10533e2a Mon Sep 17 00:00:00 2001 From: Darin Peshev Date: Sun, 24 Oct 2021 19:54:39 -0700 Subject: [PATCH 047/205] server: fix missing gateway node for tenant batches Currently, when Node.Batch is called for a tenant, the BatchRequest's GatewayNodeID is not set (and defaults to zero). This is to be expected as the tenant processes don't have node ids assigned. The GatewayNodeID however is used for one single purpose - as a proxy for the locality of the originator of the request and as a basis for computing QPS per locality for a range. When the gateway node id is not set - the range's QPS doesn't get updated and as a result the QPS based rebalancing doesn't work. This is bad as it prevents the distribution of the load in the host cluster and severelly decreases the performance under load. This PR sets the GatewayNodeID to be the node id of the host node that gets the request from the tenant. This isn't ideal but works for the current serverless setup. Ideally - the tenant processes need to support and provide locality information that isn't tied to node ids. Release note: None --- pkg/ccl/kvccl/kvtenantccl/BUILD.bazel | 1 + pkg/ccl/kvccl/kvtenantccl/tenant_kv_test.go | 82 +++++++++++++++++++++ pkg/kv/kvserver/batcheval/cmd_export.go | 2 +- pkg/kv/kvserver/replica_metrics.go | 9 ++- pkg/kv/kvserver/split_queue.go | 2 +- pkg/roachpb/api.pb.go | 2 + pkg/roachpb/api.proto | 2 + pkg/server/node.go | 10 +++ pkg/server/status.go | 3 +- 9 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 pkg/ccl/kvccl/kvtenantccl/tenant_kv_test.go diff --git a/pkg/ccl/kvccl/kvtenantccl/BUILD.bazel b/pkg/ccl/kvccl/kvtenantccl/BUILD.bazel index d8e777a1046f..038c4de90a44 100644 --- a/pkg/ccl/kvccl/kvtenantccl/BUILD.bazel +++ b/pkg/ccl/kvccl/kvtenantccl/BUILD.bazel @@ -37,6 +37,7 @@ go_test( srcs = [ "connector_test.go", "main_test.go", + "tenant_kv_test.go", "tenant_trace_test.go", "tenant_upgrade_test.go", ], diff --git a/pkg/ccl/kvccl/kvtenantccl/tenant_kv_test.go b/pkg/ccl/kvccl/kvtenantccl/tenant_kv_test.go new file mode 100644 index 000000000000..ced390c5c1e8 --- /dev/null +++ b/pkg/ccl/kvccl/kvtenantccl/tenant_kv_test.go @@ -0,0 +1,82 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package kvtenantccl + +import ( + "context" + "fmt" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/kv/kvserver" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/stretchr/testify/require" +) + +// TestTenantRangeQPSStat verifies that queries on a tenant range are +// reflected in the range's QPS. +func TestTenantRangeQPSStat(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + ctx := context.Background() + + tc := serverutils.StartNewTestCluster(t, 1, base.TestClusterArgs{ + ServerArgs: base.TestServerArgs{ + DisableWebSessionAuthentication: true, + }, + }) + defer tc.Stopper().Stop(ctx) + ts := tc.Server(0) + + _, db := serverutils.StartTenant( + t, ts, base.TestTenantArgs{TenantID: serverutils.TestTenantID()}, + ) + defer db.Close() + + // Tenant connection. + r := sqlutils.MakeSQLRunner(db) + r.Exec(t, `CREATE DATABASE foo`) + r.Exec(t, `CREATE TABLE foo.qps_test (k STRING PRIMARY KEY)`) + r.Exec(t, `INSERT INTO foo.qps_test VALUES('abc')`) + + // Host connection. + conn := tc.ServerConn(0) + sqlDB := sqlutils.MakeSQLRunner(conn) + + var rangeID int + stmt := fmt.Sprintf( + "SELECT range_id FROM crdb_internal.ranges WHERE start_pretty='/Tenant/%s'", + serverutils.TestTenantID(), + ) + sqlDB.QueryRow(t, stmt).Scan(&rangeID) + require.NotEqualf(t, 0, rangeID, "Unable to determine test table range id") + + store, err := ts.GetStores().(*kvserver.Stores).GetStore(ts.GetFirstStoreID()) + require.NoError(t, err) + repl, err := store.GetReplica(roachpb.RangeID(rangeID)) + require.NoError(t, err) + + qpsBefore, durationBefore := repl.QueriesPerSecond() + queriesBefore := qpsBefore * durationBefore.Seconds() + for i := 0; i < 110; i++ { + r.Exec(t, `SELECT k FROM foo.qps_test`) + } + qpsAfter, durationAfter := repl.QueriesPerSecond() + queriesAfter := qpsAfter * durationAfter.Seconds() + queriesIncrease := int(queriesAfter - queriesBefore) + // If queries are correctly recorded, we should see increase in query count by + // 110. As it is possible due to rounding and conversion from QPS to query count + // to get a slightly higher or lower number - we expect the increase to be at + // least 100. + require.Greater(t, queriesIncrease, 100) +} diff --git a/pkg/kv/kvserver/batcheval/cmd_export.go b/pkg/kv/kvserver/batcheval/cmd_export.go index b2a2160cd22b..17d838fce449 100644 --- a/pkg/kv/kvserver/batcheval/cmd_export.go +++ b/pkg/kv/kvserver/batcheval/cmd_export.go @@ -101,7 +101,7 @@ func evalExport( var evalExportTrace types.StringValue if cArgs.EvalCtx.NodeID() == h.GatewayNodeID { - evalExportTrace.Value = fmt.Sprintf("evaluating Export on local node %d", cArgs.EvalCtx.NodeID()) + evalExportTrace.Value = fmt.Sprintf("evaluating Export on gateway node %d", cArgs.EvalCtx.NodeID()) } else { evalExportTrace.Value = fmt.Sprintf("evaluating Export on remote node %d", cArgs.EvalCtx.NodeID()) } diff --git a/pkg/kv/kvserver/replica_metrics.go b/pkg/kv/kvserver/replica_metrics.go index 70ee3143dbde..efbb986a7b18 100644 --- a/pkg/kv/kvserver/replica_metrics.go +++ b/pkg/kv/kvserver/replica_metrics.go @@ -12,6 +12,7 @@ package kvserver import ( "context" + "time" "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency" @@ -248,10 +249,10 @@ func calcBehindCount( // A "Query" is a BatchRequest (regardless of its contents) arriving at the // leaseholder with a gateway node set in the header (i.e. excluding requests // that weren't sent through a DistSender, which in practice should be -// practically none). -func (r *Replica) QueriesPerSecond() float64 { - qps, _ := r.leaseholderStats.avgQPS() - return qps +// practically none). Also return the amount of time over which the stat was +// accumulated. +func (r *Replica) QueriesPerSecond() (float64, time.Duration) { + return r.leaseholderStats.avgQPS() } // WritesPerSecond returns the range's average keys written per second. A diff --git a/pkg/kv/kvserver/split_queue.go b/pkg/kv/kvserver/split_queue.go index 63af8a63bef3..a611a335ac73 100644 --- a/pkg/kv/kvserver/split_queue.go +++ b/pkg/kv/kvserver/split_queue.go @@ -218,7 +218,7 @@ func (sq *splitQueue) processAttempt( now := timeutil.Now() if splitByLoadKey := r.loadBasedSplitter.MaybeSplitKey(now); splitByLoadKey != nil { - batchHandledQPS := r.QueriesPerSecond() + batchHandledQPS, _ := r.QueriesPerSecond() raftAppliedQPS := r.WritesPerSecond() splitQPS := r.loadBasedSplitter.LastQPS(now) reason := fmt.Sprintf( diff --git a/pkg/roachpb/api.pb.go b/pkg/roachpb/api.pb.go index 2b66f560fb66..82fb1c48886c 100644 --- a/pkg/roachpb/api.pb.go +++ b/pkg/roachpb/api.pb.go @@ -6681,6 +6681,8 @@ type Header struct { // RangeInfo with up-to-date information. ClientRangeInfo ClientRangeInfo `protobuf:"bytes,17,opt,name=client_range_info,json=clientRangeInfo,proto3" json:"client_range_info"` // gateway_node_id is the ID of the gateway node where the request originated. + // For requests from tenants, this is set to the NodeID of the KV node handling + // the BatchRequest. GatewayNodeID NodeID `protobuf:"varint,11,opt,name=gateway_node_id,json=gatewayNodeId,proto3,casttype=NodeID" json:"gateway_node_id,omitempty"` // If set, the request will return to the client before proposing the // request into Raft. All consensus processing will be performed diff --git a/pkg/roachpb/api.proto b/pkg/roachpb/api.proto index 2219dfafc7a9..dc65313ce03c 100644 --- a/pkg/roachpb/api.proto +++ b/pkg/roachpb/api.proto @@ -2162,6 +2162,8 @@ message Header { // RangeInfo with up-to-date information. ClientRangeInfo client_range_info = 17 [(gogoproto.nullable) = false]; // gateway_node_id is the ID of the gateway node where the request originated. + // For requests from tenants, this is set to the NodeID of the KV node handling + // the BatchRequest. int32 gateway_node_id = 11 [(gogoproto.customname) = "GatewayNodeID", (gogoproto.casttype) = "NodeID"]; // If set, the request will return to the client before proposing the // request into Raft. All consensus processing will be performed diff --git a/pkg/server/node.go b/pkg/server/node.go index 8a8714eec131..fcc05e71e5f8 100644 --- a/pkg/server/node.go +++ b/pkg/server/node.go @@ -955,6 +955,16 @@ func (n *Node) Batch( if !ok { tenantID = roachpb.SystemTenantID } + + // Requests from tenants don't have gateway node id set but are required for + // the QPS based rebalancing to work. The GatewayNodeID is used as a proxy + // for the locality of the origin of the request. The replica stats aggregate + // all incoming BatchRequests and which localities they come from in order to + // compute per second stats used for the rebalancing decisions. + if args.GatewayNodeID == 0 && tenantID != roachpb.SystemTenantID { + args.GatewayNodeID = n.Descriptor.NodeID + } + handle, err := n.admissionController.AdmitKVWork(ctx, tenantID, args) if err != nil { return nil, err diff --git a/pkg/server/status.go b/pkg/server/status.go index e9462809ae0f..94293f181250 100644 --- a/pkg/server/status.go +++ b/pkg/server/status.go @@ -1905,6 +1905,7 @@ func (s *statusServer) rangesHelper( WaitingWriters: lm.WaitingWriters, }) } + qps, _ := rep.QueriesPerSecond() return serverpb.RangeInfo{ Span: span, RaftState: raftState, @@ -1913,7 +1914,7 @@ func (s *statusServer) rangesHelper( SourceStoreID: storeID, LeaseHistory: leaseHistory, Stats: serverpb.RangeStatistics{ - QueriesPerSecond: rep.QueriesPerSecond(), + QueriesPerSecond: qps, WritesPerSecond: rep.WritesPerSecond(), }, Problems: serverpb.RangeProblems{ From 88df2b966a894ad1b047b44167c388fbd65e154e Mon Sep 17 00:00:00 2001 From: Andrew Werner Date: Mon, 25 Oct 2021 10:33:37 -0400 Subject: [PATCH 048/205] sql/catalog/nstree: optimize with lazy initialization, pooling btrees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change comes in response to https://github.com/cockroachdb/cockroach/pull/66112#issuecomment-950196996. The thrust of the change is to lazily initialize the data structures so that when they are not used, they do not incur cost. Additionally, pool the underlying tree so that when they are used, the allocations are effectively free. This was originally deemed unimportant because the connExecutor descs.Collection is long-lived. Unfortunately, others, as used for the type resolve in distsql, are not. ``` name old time/op new time/op delta FlowSetup/vectorize=true/distribute=true-16 145µs ± 2% 139µs ± 3% -3.94% (p=0.000 n=19+20) FlowSetup/vectorize=true/distribute=false-16 141µs ± 3% 134µs ± 2% -4.66% (p=0.000 n=18+19) FlowSetup/vectorize=false/distribute=true-16 138µs ± 4% 132µs ± 4% -4.23% (p=0.000 n=20+20) FlowSetup/vectorize=false/distribute=false-16 133µs ± 3% 127µs ± 3% -4.41% (p=0.000 n=20+19) name old alloc/op new alloc/op delta FlowSetup/vectorize=true/distribute=true-16 38.1kB ± 3% 36.7kB ± 2% -3.58% (p=0.000 n=18+18) FlowSetup/vectorize=true/distribute=false-16 36.2kB ± 0% 34.9kB ± 0% -3.66% (p=0.000 n=18+16) FlowSetup/vectorize=false/distribute=true-16 42.6kB ± 0% 41.3kB ± 0% -3.11% (p=0.000 n=17+17) FlowSetup/vectorize=false/distribute=false-16 41.0kB ± 0% 39.7kB ± 0% -3.22% (p=0.000 n=17+17) name old allocs/op new allocs/op delta FlowSetup/vectorize=true/distribute=true-16 368 ± 0% 314 ± 0% -14.67% (p=0.000 n=16+17) FlowSetup/vectorize=true/distribute=false-16 354 ± 0% 300 ± 0% -15.25% (p=0.000 n=17+17) FlowSetup/vectorize=false/distribute=true-16 338 ± 1% 283 ± 1% -16.13% (p=0.000 n=19+19) FlowSetup/vectorize=false/distribute=false-16 325 ± 0% 271 ± 0% -16.62% (p=0.000 n=18+18) ``` Omitting a release note because I'm doubtful this meaningfully shows up on its own. Release note: None --- pkg/sql/catalog/descs/collection.go | 2 - pkg/sql/catalog/descs/leased_descriptors.go | 3 +- .../catalog/descs/synthetic_descriptors.go | 4 -- .../catalog/descs/uncommitted_descriptors.go | 9 ---- pkg/sql/catalog/lease/name_cache.go | 2 +- pkg/sql/catalog/nstree/map.go | 52 +++++++++++++------ pkg/sql/catalog/nstree/map_test.go | 6 +-- pkg/sql/catalog/nstree/set.go | 39 +++++++++----- pkg/sql/catalog/nstree/set_test.go | 6 +-- pkg/sql/catalog/nstree/tree.go | 11 ++++ 10 files changed, 81 insertions(+), 53 deletions(-) diff --git a/pkg/sql/catalog/descs/collection.go b/pkg/sql/catalog/descs/collection.go index 49e63c1b17a8..8f60a335de87 100644 --- a/pkg/sql/catalog/descs/collection.go +++ b/pkg/sql/catalog/descs/collection.go @@ -53,8 +53,6 @@ func makeCollection( hydratedTables: hydratedTables, virtual: makeVirtualDescriptors(virtualSchemas), leased: makeLeasedDescriptors(leaseMgr), - synthetic: makeSyntheticDescriptors(), - uncommitted: makeUncommittedDescriptors(), kv: makeKVDescriptors(codec), temporary: makeTemporaryDescriptors(codec, temporarySchemaProvider), } diff --git a/pkg/sql/catalog/descs/leased_descriptors.go b/pkg/sql/catalog/descs/leased_descriptors.go index 8958e0b39dc0..d5759fb45d23 100644 --- a/pkg/sql/catalog/descs/leased_descriptors.go +++ b/pkg/sql/catalog/descs/leased_descriptors.go @@ -64,8 +64,7 @@ func (m maxTimestampBoundDeadlineHolder) UpdateDeadline( func makeLeasedDescriptors(lm leaseManager) leasedDescriptors { return leasedDescriptors{ - lm: lm, - cache: nstree.MakeMap(), + lm: lm, } } diff --git a/pkg/sql/catalog/descs/synthetic_descriptors.go b/pkg/sql/catalog/descs/synthetic_descriptors.go index 918bf80d8c71..9d17f8b9100b 100644 --- a/pkg/sql/catalog/descs/synthetic_descriptors.go +++ b/pkg/sql/catalog/descs/synthetic_descriptors.go @@ -20,10 +20,6 @@ type syntheticDescriptors struct { descs nstree.Map } -func makeSyntheticDescriptors() syntheticDescriptors { - return syntheticDescriptors{descs: nstree.MakeMap()} -} - func (sd *syntheticDescriptors) set(descs []catalog.Descriptor) { sd.descs.Clear() for _, desc := range descs { diff --git a/pkg/sql/catalog/descs/uncommitted_descriptors.go b/pkg/sql/catalog/descs/uncommitted_descriptors.go index f3712dbe5c97..ce954b10bbdc 100644 --- a/pkg/sql/catalog/descs/uncommitted_descriptors.go +++ b/pkg/sql/catalog/descs/uncommitted_descriptors.go @@ -82,15 +82,6 @@ type uncommittedDescriptors struct { descNames nstree.Set } -func makeUncommittedDescriptors() uncommittedDescriptors { - ud := uncommittedDescriptors{ - descs: nstree.MakeMap(), - descNames: nstree.MakeSet(), - } - ud.reset() - return ud -} - func (ud *uncommittedDescriptors) reset() { ud.descs.Clear() ud.descNames.Clear() diff --git a/pkg/sql/catalog/lease/name_cache.go b/pkg/sql/catalog/lease/name_cache.go index 9373782808eb..a65a26960d2f 100644 --- a/pkg/sql/catalog/lease/name_cache.go +++ b/pkg/sql/catalog/lease/name_cache.go @@ -21,7 +21,7 @@ import ( ) func makeNameCache() nameCache { - return nameCache{descriptors: nstree.MakeMap()} + return nameCache{} } // nameCache is a cache of descriptor name -> latest version mappings. diff --git a/pkg/sql/catalog/nstree/map.go b/pkg/sql/catalog/nstree/map.go index 738df21747d2..2d487202aa09 100644 --- a/pkg/sql/catalog/nstree/map.go +++ b/pkg/sql/catalog/nstree/map.go @@ -19,7 +19,8 @@ import ( // Map is a lookup structure for descriptors. It is used to provide // indexed access to a set of entries either by name or by ID. The // entries' properties are indexed; they must not change or else the -// index will be corrupted. +// index will be corrupted. Safe for use without initialization. Calling +// Clear will return memory to a sync.Pool. type Map struct { byID byIDMap byName byNameMap @@ -31,22 +32,10 @@ type Map struct { // stop but no error will be returned. type EntryIterator func(entry catalog.NameEntry) error -// MakeMap makes a new Map. -func MakeMap() Map { - const ( - degree = 8 // arbitrary - initialNodes = 2 // one per tree - ) - freeList := btree.NewFreeList(initialNodes) - return Map{ - byName: byNameMap{t: btree.NewWithFreeList(degree, freeList)}, - byID: byIDMap{t: btree.NewWithFreeList(degree, freeList)}, - } -} - // Upsert adds the descriptor to the tree. If any descriptor exists in the // tree with the same name or id, it will be removed. func (dt *Map) Upsert(d catalog.NameEntry) { + dt.maybeInitialize() if replaced := dt.byName.upsert(d); replaced != nil { dt.byID.delete(replaced.GetID()) } @@ -58,6 +47,7 @@ func (dt *Map) Upsert(d catalog.NameEntry) { // Remove removes the descriptor with the given ID from the tree and // returns it if it exists. func (dt *Map) Remove(id descpb.ID) catalog.NameEntry { + dt.maybeInitialize() if d := dt.byID.delete(id); d != nil { dt.byName.delete(d) return d @@ -67,26 +57,58 @@ func (dt *Map) Remove(id descpb.ID) catalog.NameEntry { // GetByID gets a descriptor from the tree by id. func (dt *Map) GetByID(id descpb.ID) catalog.NameEntry { + if !dt.initialized() { + return nil + } return dt.byID.get(id) } // GetByName gets a descriptor from the tree by name. func (dt *Map) GetByName(parentID, parentSchemaID descpb.ID, name string) catalog.NameEntry { + if !dt.initialized() { + return nil + } return dt.byName.getByName(parentID, parentSchemaID, name) } -// Clear removes all entries. +// Clear removes all entries, returning any held memory to the sync.Pool. func (dt *Map) Clear() { + if !dt.initialized() { + return + } dt.byID.clear() dt.byName.clear() + btreeSyncPool.Put(dt.byName.t) + btreeSyncPool.Put(dt.byID.t) + *dt = Map{} } // IterateByID iterates the descriptors by ID, ascending. func (dt *Map) IterateByID(f EntryIterator) error { + if !dt.initialized() { + return nil + } return dt.byID.ascend(f) } // Len returns the number of descriptors in the tree. func (dt *Map) Len() int { + if !dt.initialized() { + return 0 + } return dt.byID.len() } + +func (dt Map) initialized() bool { + return dt != (Map{}) +} + +func (dt *Map) maybeInitialize() { + if dt.initialized() { + return + } + *dt = Map{ + byName: byNameMap{t: btreeSyncPool.Get().(*btree.BTree)}, + byID: byIDMap{t: btreeSyncPool.Get().(*btree.BTree)}, + } +} diff --git a/pkg/sql/catalog/nstree/map_test.go b/pkg/sql/catalog/nstree/map_test.go index e96268209ff4..296d1d259026 100644 --- a/pkg/sql/catalog/nstree/map_test.go +++ b/pkg/sql/catalog/nstree/map_test.go @@ -53,14 +53,14 @@ import ( // func TestMapDataDriven(t *testing.T) { datadriven.Walk(t, "testdata/map", func(t *testing.T, path string) { - tr := MakeMap() + var tr Map datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { - return testMapDataDriven(t, d, tr) + return testMapDataDriven(t, d, &tr) }) }) } -func testMapDataDriven(t *testing.T, d *datadriven.TestData, tr Map) string { +func testMapDataDriven(t *testing.T, d *datadriven.TestData, tr *Map) string { switch d.Cmd { case "add": a := parseArgs(t, d, argID|argName, argParentID|argParentSchemaID) diff --git a/pkg/sql/catalog/nstree/set.go b/pkg/sql/catalog/nstree/set.go index 4668cfe8825b..06592e8e87b2 100644 --- a/pkg/sql/catalog/nstree/set.go +++ b/pkg/sql/catalog/nstree/set.go @@ -15,25 +15,15 @@ import ( "github.com/google/btree" ) -// Set is a set of namespace keys. +// Set is a set of namespace keys. Safe for use without initialization. +// Calling Clear will return memory to a sync.Pool. type Set struct { t *btree.BTree } -// MakeSet makes a Set of namespace keys. -func MakeSet() Set { - const ( - degree = 8 // arbitrary - initialNodes = 1 // one per tree - ) - freeList := btree.NewFreeList(initialNodes) - return Set{ - t: btree.NewWithFreeList(degree, freeList), - } -} - // Add will add the relevant namespace key to the set. func (s *Set) Add(components catalog.NameKey) { + s.maybeInitialize() item := makeByNameItem(components).get() item.v = item // the value needs to be non-nil upsert(s.t, item) @@ -41,10 +31,31 @@ func (s *Set) Add(components catalog.NameKey) { // Contains will test whether the relevant namespace key was added. func (s *Set) Contains(components catalog.NameKey) bool { + if !s.initialized() { + return false + } return get(s.t, makeByNameItem(components).get()) != nil } -// Clear will clear the set. +// Clear will clear the set, returning any held memory to the sync.Pool. func (s *Set) Clear() { + if !s.initialized() { + return + } clear(s.t) + btreeSyncPool.Put(s.t) + *s = Set{} +} + +func (s *Set) maybeInitialize() { + if s.initialized() { + return + } + *s = Set{ + t: btreeSyncPool.Get().(*btree.BTree), + } +} + +func (s Set) initialized() bool { + return s != (Set{}) } diff --git a/pkg/sql/catalog/nstree/set_test.go b/pkg/sql/catalog/nstree/set_test.go index 0db4f6a620d6..be20a1e52d26 100644 --- a/pkg/sql/catalog/nstree/set_test.go +++ b/pkg/sql/catalog/nstree/set_test.go @@ -36,14 +36,14 @@ import ( // func TestSetDataDriven(t *testing.T) { datadriven.Walk(t, "testdata/set", func(t *testing.T, path string) { - tr := MakeSet() + var tr Set datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { - return testSetDataDriven(t, d, tr) + return testSetDataDriven(t, d, &tr) }) }) } -func testSetDataDriven(t *testing.T, d *datadriven.TestData, tr Set) string { +func testSetDataDriven(t *testing.T, d *datadriven.TestData, tr *Set) string { switch d.Cmd { case "add": a := parseArgs(t, d, argName, argParentID|argParentSchemaID) diff --git a/pkg/sql/catalog/nstree/tree.go b/pkg/sql/catalog/nstree/tree.go index 309c3fff6284..38de7d312632 100644 --- a/pkg/sql/catalog/nstree/tree.go +++ b/pkg/sql/catalog/nstree/tree.go @@ -13,6 +13,8 @@ package nstree import ( + "sync" + "github.com/cockroachdb/cockroach/pkg/util/iterutil" "github.com/google/btree" ) @@ -23,6 +25,15 @@ type item interface { value() interface{} } +// degree is totally arbitrary, used for the btree. +const degree = 8 + +var btreeSyncPool = sync.Pool{ + New: func() interface{} { + return btree.New(degree) + }, +} + func upsert(t *btree.BTree, toUpsert item) interface{} { if overwritten := t.ReplaceOrInsert(toUpsert); overwritten != nil { overwrittenItem := overwritten.(item) From a8290b33a38074ea6a2975ecc86ec74c4cd533d7 Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Thu, 14 Oct 2021 14:43:29 -0400 Subject: [PATCH 049/205] sql: fix skipped pre-txn-commit validation bug Previously, querying a descriptor surgery function with the 'force' flag set would correctly disable pre-txn-commit descriptor validation for this transaction, but the validation could incorrectly remain disabled afterwards. This commit fixes this bug. In practice, this bug directly impacts the usefulness of importing a cluster's descriptor state via a 'debug doctor recreate' dump. This is typically done to practice repairing a corrupted descriptor, a use case for which this validation is particularly useful. Release justification: Low risk bug fix in debug tooling Release note: None --- pkg/sql/catalog/descs/collection.go | 1 + pkg/sql/catalog/descs/validate.go | 1 + pkg/sql/tests/repair_test.go | 66 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/pkg/sql/catalog/descs/collection.go b/pkg/sql/catalog/descs/collection.go index 49e63c1b17a8..763dc7229858 100644 --- a/pkg/sql/catalog/descs/collection.go +++ b/pkg/sql/catalog/descs/collection.go @@ -171,6 +171,7 @@ func (tc *Collection) ReleaseAll(ctx context.Context) { tc.kv.reset() tc.synthetic.reset() tc.deletedDescs = nil + tc.skipValidationOnWrite = false } // HasUncommittedTables returns true if the Collection contains uncommitted diff --git a/pkg/sql/catalog/descs/validate.go b/pkg/sql/catalog/descs/validate.go index 4dadf004d6a0..a42260232803 100644 --- a/pkg/sql/catalog/descs/validate.go +++ b/pkg/sql/catalog/descs/validate.go @@ -33,6 +33,7 @@ func (tc *Collection) ValidateUncommittedDescriptors(ctx context.Context, txn *k if tc.skipValidationOnWrite || !ValidateOnWriteEnabled.Get(&tc.settings.SV) { return nil } + descs := tc.uncommitted.getUncommittedDescriptorsForValidation() if len(descs) == 0 { return nil diff --git a/pkg/sql/tests/repair_test.go b/pkg/sql/tests/repair_test.go index 67bceba00a46..4e684ffa9a22 100644 --- a/pkg/sql/tests/repair_test.go +++ b/pkg/sql/tests/repair_test.go @@ -414,6 +414,19 @@ SELECT crdb_internal.unsafe_delete_namespace_entry("parentID", 0, 'foo', id) }, }, }, + { // 12 + // Upsert a descriptor with the force flag set to skip descriptor + // validation at txn commit time, then check that subsequently upserting + // an invalid descriptor with the force flag unset correctly triggers a + // validation error. + before: []string{ + `CREATE DATABASE test`, + `CREATE TABLE test.foo ()`, + `SELECT crdb_internal.unsafe_upsert_descriptor(id, descriptor, true) FROM system.descriptor WHERE id = 'test.foo'::REGCLASS::OID`, + }, + op: upsertInvalidNameInTestFooNoForce, + expErrRE: `pq: crdb_internal.unsafe_upsert_descriptor\(\): relation \"\" \(53\): empty table name`, + }, } { t.Run(fmt.Sprintf("case #%d: %s", caseIdx+1, tc.op), func(t *testing.T) { s, db, cleanup := setup(t) @@ -643,6 +656,59 @@ SELECT crdb_internal.unsafe_upsert_descriptor(53, crdb_internal.json_to_pb('cockroach.sql.sqlbase.Descriptor', ` + repairedDescriptor + `), true)` + upsertInvalidNameInTestFooNoForce = ` +WITH + as_json + AS ( + SELECT + id, + crdb_internal.pb_to_json( + 'cockroach.sql.sqlbase.Descriptor', + descriptor, + false + ) + AS d + FROM + system.descriptor + WHERE + id = 'test.foo'::REGCLASS::OID + ), + updated + AS ( + SELECT + id, + crdb_internal.json_to_pb( + 'cockroach.sql.sqlbase.Descriptor', + json_set( + json_set( + json_set( + d, + ARRAY['table', 'name'], + '""'::JSONB + ), + ARRAY['table', 'version'], + ((d->'table'->>'version')::INT8 + 1)::STRING::JSONB + ), + ARRAY['table', 'modificationTime'], + json_build_object( + 'wallTime', + ( + (extract('epoch', now()) * 1000000)::INT8 + * 1000 + )::STRING + ) + ) + ) + AS descriptor + FROM + as_json + ) +SELECT + crdb_internal.unsafe_upsert_descriptor(id, descriptor, false) +FROM + updated +` + // This is a statement to update the above descriptor's privileges. // It will change the table owner, add privileges for a new user, // alter the privilege of an existing user, and revoke all privileges for an old user. From d79800219e690181588cc6cdd70788ebdc158b56 Mon Sep 17 00:00:00 2001 From: Azhng Date: Mon, 25 Oct 2021 11:20:48 -0400 Subject: [PATCH 050/205] server,ui: the transactionDetail page now shows statement stats scoped by transaction fingerprint ID Previously, the transactionDetail page showed statement statistics aggregated across **all** executions of that statement fingerprint. This led to confusing UX and the statement statistics were subsequently removed from transactionDetail page. This commit reintroduces statement statistics on the transactionDetail page. The statistics now are only aggregated across the statement fingerprints that are part of the selected transaction fingerprint. Resolves #59205 #65106 Release note (ui change): transactionDetail page now shows statement statistics scoped by the transaction fingeprint. --- pkg/server/combined_statement_stats.go | 22 +++++++++---- .../transactionDetails/transactionDetails.tsx | 32 +++++++++++++------ .../src/transactionsPage/transactionsPage.tsx | 6 ++++ .../cluster-ui/src/transactionsPage/utils.ts | 4 +-- .../cluster-ui/src/util/appStats/appStats.ts | 10 ++++++ 5 files changed, 56 insertions(+), 18 deletions(-) diff --git a/pkg/server/combined_statement_stats.go b/pkg/server/combined_statement_stats.go index 5d7ff2cf7d17..e6425b043a71 100644 --- a/pkg/server/combined_statement_stats.go +++ b/pkg/server/combined_statement_stats.go @@ -113,6 +113,7 @@ func collectCombinedStatements( query := fmt.Sprintf( `SELECT fingerprint_id, + transaction_fingerprint_id, app_name, aggregated_ts, metadata, @@ -122,7 +123,7 @@ func collectCombinedStatements( FROM crdb_internal.statement_statistics %s`, whereClause) - const expectedNumDatums = 7 + const expectedNumDatums = 8 it, err := ie.QueryIteratorEx(ctx, "combined-stmts-by-interval", nil, sessiondata.InternalExecutorOverride{ @@ -157,30 +158,37 @@ func collectCombinedStatements( return nil, err } - app := string(tree.MustBeDString(row[1])) - aggregatedTs := tree.MustBeDTimestampTZ(row[2]).Time + var transactionFingerprintID uint64 + if transactionFingerprintID, err = sqlstatsutil.DatumToUint64(row[1]); err != nil { + return nil, err + } + + app := string(tree.MustBeDString(row[2])) + aggregatedTs := tree.MustBeDTimestampTZ(row[3]).Time var metadata roachpb.CollectedStatementStatistics - metadataJSON := tree.MustBeDJSON(row[3]).JSON + metadataJSON := tree.MustBeDJSON(row[4]).JSON if err = sqlstatsutil.DecodeStmtStatsMetadataJSON(metadataJSON, &metadata); err != nil { return nil, err } metadata.Key.App = app + metadata.Key.TransactionFingerprintID = + roachpb.TransactionFingerprintID(transactionFingerprintID) - statsJSON := tree.MustBeDJSON(row[4]).JSON + statsJSON := tree.MustBeDJSON(row[5]).JSON if err = sqlstatsutil.DecodeStmtStatsStatisticsJSON(statsJSON, &metadata.Stats); err != nil { return nil, err } - planJSON := tree.MustBeDJSON(row[5]).JSON + planJSON := tree.MustBeDJSON(row[6]).JSON plan, err := sqlstatsutil.JSONToExplainTreePlanNode(planJSON) if err != nil { return nil, err } metadata.Stats.SensitiveInfo.MostRecentPlanDescription = *plan - aggInterval := tree.MustBeDInterval(row[6]).Duration + aggInterval := tree.MustBeDInterval(row[7]).Duration stmt := serverpb.StatementsResponse_CollectedStatementStatistics{ Key: serverpb.StatementsResponse_ExtendedStatementStatisticsKey{ diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx index 1ed9827c3dda..1712caa47477 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.tsx @@ -29,7 +29,12 @@ import { SqlBox } from "../sql"; import { aggregateStatements } from "../transactionsPage/utils"; import { Loading } from "../loading"; import { SummaryCard } from "../summaryCard"; -import { Bytes, Duration, formatNumberForDisplay, summarize } from "src/util"; +import { + Bytes, + calculateTotalWorkload, + Duration, + formatNumberForDisplay, +} from "src/util"; import { UIConfigState } from "../store"; import SQLActivityError from "../sqlActivity/errorComponent"; @@ -41,7 +46,7 @@ import { formatTwoPlaces } from "../barCharts"; import { ArrowLeft } from "@cockroachlabs/icons"; import { populateRegionNodeForStatements, - makeStatementFingerprintColumn, + makeStatementsColumns, } from "src/statementsTable/statementsTable"; import { TransactionInfo } from "src/transactionsTable"; import Long from "long"; @@ -65,6 +70,7 @@ interface TransactionDetailsProps { error?: Error | null; resetSQLStats: () => void; isTenant: UIConfigState["isTenant"]; + transactionFingerprintId: Long; } interface TState { @@ -111,6 +117,7 @@ export class TransactionDetails extends React.Component< handleDetails, error, nodeRegions, + transactionFingerprintId, } = this.props; return (
@@ -133,7 +140,12 @@ export class TransactionDetails extends React.Component< render={() => { const { statements, transactionStats, isTenant } = this.props; const { sortSetting, pagination } = this.state; - const aggregatedStatements = aggregateStatements(statements); + const txnScopedStmts = statements.filter(s => + s.key.key_data.transaction_fingerprint_id.equals( + transactionFingerprintId, + ), + ); + const aggregatedStatements = aggregateStatements(txnScopedStmts); populateRegionNodeForStatements( aggregatedStatements, nodeRegions, @@ -286,12 +298,14 @@ export class TransactionDetails extends React.Component<
{ @@ -245,6 +247,8 @@ export class TransactionsPage extends React.Component< transaction?.stats_data?.statement_fingerprint_ids, transactionStats: transaction?.stats_data?.stats, aggregatedTs: transaction?.stats_data?.aggregated_ts, + transactionFingerprintId: + transaction?.stats_data?.transaction_fingerprint_id, }); }; @@ -466,6 +470,7 @@ export class TransactionsPage extends React.Component< aggregatedTs, statementFingerprintIds, transactionStats, + transactionFingerprintId, } = this.state; const transactionDetails = statementFingerprintIds && @@ -489,6 +494,7 @@ export class TransactionsPage extends React.Component< error={this.props.error} resetSQLStats={this.props.resetSQLStats} isTenant={this.props.isTenant} + transactionFingerprintId={transactionFingerprintId} /> ); } diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts index 9acec70ccad6..347e74bf0c6b 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/utils.ts @@ -22,12 +22,12 @@ import { aggregateNumericStats, FixLong, longToInt, - statementKey, TimestampToNumber, addStatementStats, flattenStatementStats, DurationToNumber, computeOrUseStmtSummary, + transactionScopedStatementKey, } from "../util"; type Statement = protos.cockroach.server.serverpb.StatementsResponse.ICollectedStatementStatistics; @@ -97,7 +97,7 @@ export const aggregateStatements = ( const statsKey: { [key: string]: AggregateStatistics } = {}; flattenStatementStats(statements).forEach(s => { - const key = statementKey(s); + const key = transactionScopedStatementKey(s); if (!(key in statsKey)) { statsKey[key] = { label: s.statement, diff --git a/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts b/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts index b5307bdff137..d1940becd825 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts @@ -214,6 +214,7 @@ export interface ExecutionStatistics { full_scan: boolean; failed: boolean; node_id: number; + transaction_fingerprint_id: Long; stats: StatementStatistics; } @@ -233,6 +234,7 @@ export function flattenStatementStats( full_scan: stmt.key.key_data.full_scan, failed: stmt.key.key_data.failed, node_id: stmt.key.node_id, + transaction_fingerprint_id: stmt.key.key_data.transaction_fingerprint_id, stats: stmt.stats, })); } @@ -262,3 +264,11 @@ export function statementKey(stmt: ExecutionStatistics): string { stmt.aggregation_interval ); } + +// transactionScopedStatementKey is similar to statementKey, except that +// it appends the transactionFingerprintID to the string key it generated. +export function transactionScopedStatementKey( + stmt: ExecutionStatistics, +): string { + return statementKey(stmt) + stmt.transaction_fingerprint_id.toString(); +} From a8d5fc2126fb46b405b875d5b10dd9059339bccc Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Mon, 4 Oct 2021 11:30:58 -0400 Subject: [PATCH 051/205] storage/metamorphic: Add savepoints, txn aborts Adds three new types of operations to the mvcc metamorphic tests, and their related operand generators: * Transaction abort * Transaction savepoint creation * Transaction savepoint rollback Part of #70935. Release note: None. --- pkg/storage/metamorphic/generator.go | 42 ++++++-- pkg/storage/metamorphic/meta_test.go | 6 +- pkg/storage/metamorphic/operands.go | 118 ++++++++++++++------- pkg/storage/metamorphic/operations.go | 145 ++++++++++++++++++++++++-- 4 files changed, 251 insertions(+), 60 deletions(-) diff --git a/pkg/storage/metamorphic/generator.go b/pkg/storage/metamorphic/generator.go index 3d4fe2be3f1e..7989978af44a 100644 --- a/pkg/storage/metamorphic/generator.go +++ b/pkg/storage/metamorphic/generator.go @@ -23,6 +23,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/storage" + "github.com/cockroachdb/cockroach/pkg/storage/enginepb" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/pebble" ) @@ -139,6 +140,7 @@ type metaTestRunner struct { tsGenerator tsGenerator opGenerators map[operandType]operandGenerator txnGenerator *txnGenerator + spGenerator *savepointGenerator rwGenerator *readWriterGenerator iterGenerator *iteratorGenerator keyGenerator *keyGenerator @@ -150,6 +152,7 @@ type metaTestRunner struct { openIters map[iteratorID]iteratorInfo openBatches map[readWriterID]storage.ReadWriter openTxns map[txnID]*roachpb.Transaction + openSavepoints map[txnID][]enginepb.TxnSeq nameToGenerator map[string]*opGenerator ops []opRun weights []int @@ -172,11 +175,16 @@ func (m *metaTestRunner) init() { // Initialize opGenerator structs. These retain all generation time // state of open objects. m.txnGenerator = &txnGenerator{ - rng: m.rng, - tsGenerator: &m.tsGenerator, - txnIDMap: make(map[txnID]*roachpb.Transaction), - openBatches: make(map[txnID]map[readWriterID]struct{}), - testRunner: m, + rng: m.rng, + tsGenerator: &m.tsGenerator, + txnIDMap: make(map[txnID]*roachpb.Transaction), + openBatches: make(map[txnID]map[readWriterID]struct{}), + openSavepoints: make(map[txnID]int), + testRunner: m, + } + m.spGenerator = &savepointGenerator{ + rng: m.rng, + txnGenerator: m.txnGenerator, } m.rwGenerator = &readWriterGenerator{ rng: m.rng, @@ -216,6 +224,7 @@ func (m *metaTestRunner) init() { operandIterator: m.iterGenerator, operandFloat: m.floatGenerator, operandBool: m.boolGenerator, + operandSavepoint: m.spGenerator, } m.nameToGenerator = make(map[string]*opGenerator) @@ -228,6 +237,7 @@ func (m *metaTestRunner) init() { m.openIters = make(map[iteratorID]iteratorInfo) m.openBatches = make(map[readWriterID]storage.ReadWriter) m.openTxns = make(map[txnID]*roachpb.Transaction) + m.openSavepoints = make(map[txnID][]enginepb.TxnSeq) } func (m *metaTestRunner) closeGenerators() { @@ -259,6 +269,7 @@ func (m *metaTestRunner) closeAll() { m.openIters = make(map[iteratorID]iteratorInfo) m.openBatches = make(map[readWriterID]storage.ReadWriter) m.openTxns = make(map[txnID]*roachpb.Transaction) + m.openSavepoints = make(map[txnID][]enginepb.TxnSeq) if m.engine != nil { m.engine.Close() m.engine = nil @@ -461,27 +472,38 @@ func (m *metaTestRunner) generateAndAddOp(run opReference) mvccOp { // Resolve all operands (including recursively queueing openers for operands as // necessary) and add the specified operation to the operations list. -func (m *metaTestRunner) resolveAndAddOp(op *opGenerator) { +func (m *metaTestRunner) resolveAndAddOp(op *opGenerator, fixedArgs ...string) { argStrings := make([]string, len(op.operands)) + copy(argStrings, fixedArgs) // Operation op depends on some operands to exist in an open state. // If those operands' opGenerators report a zero count for that object's open // instances, recursively call generateAndAddOp with that operand type's // opener. for i, operand := range op.operands { + if i < len(fixedArgs) { + continue + } opGenerator := m.opGenerators[operand] // Special case: if this is an opener operation, and the operand is the // last one in the list of operands, call getNew() to get a new ID instead. if i == len(op.operands)-1 && op.isOpener { - argStrings[i] = opGenerator.getNew() + argStrings[i] = opGenerator.getNew(argStrings[:i]) continue } - if opGenerator.count() == 0 { + if opGenerator.count(argStrings[:i]) == 0 { + var args []string + // Special case: Savepoints need to be created on transactions, so affix + // the txn when recursing. + switch opGenerator.opener() { + case "txn_create_savepoint": + args = append(args, argStrings[0]) + } // Add this operation to the list first, so that it creates the // dependency. - m.resolveAndAddOp(m.nameToGenerator[opGenerator.opener()]) + m.resolveAndAddOp(m.nameToGenerator[opGenerator.opener()], args...) } - argStrings[i] = opGenerator.get() + argStrings[i] = opGenerator.get(argStrings[:i]) } m.generateAndAddOp(opReference{ diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index 763b15458216..76f612e09827 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -57,7 +57,7 @@ type testRunForEngines struct { func runMetaTestForEngines(run testRunForEngines) { tempDir, cleanup := testutils.TempDir(run.t) defer func() { - if !*keep { + if !*keep && !run.t.Failed() { cleanup() } }() @@ -86,7 +86,7 @@ func runMetaTest(run testRun) { t := run.t outerTempDir, cleanup := testutils.TempDir(run.t) defer func() { - if !*keep { + if !*keep && !t.Failed() { cleanup() } }() @@ -106,7 +106,7 @@ func runMetaTest(run testRun) { t.Run(strings.Join(engineNames, ","), func(t *testing.T) { innerTempDir, cleanup := testutils.TempDir(t) defer func() { - if !*keep { + if !*keep && !t.Failed() { cleanup() } }() diff --git a/pkg/storage/metamorphic/operands.go b/pkg/storage/metamorphic/operands.go index 0c6d74a73788..912c3a875fef 100644 --- a/pkg/storage/metamorphic/operands.go +++ b/pkg/storage/metamorphic/operands.go @@ -32,6 +32,7 @@ const ( operandIterator operandFloat operandBool + operandSavepoint ) const ( @@ -52,19 +53,23 @@ type operandGenerator interface { // keys), it could also generate and return a new type of an instance. An // operand is represented as a serializable string, that can be converted into // a concrete instance type during execution by calling a get() - // or parse() method on the concrete operand generator. - get() string + // or parse() method on the concrete operand generator. Operands generated + // so far are passed in, in case this generator relies on them to generate + // new ones. + get(args []string) string // getNew retrieves a new instance of this type of operand. Called when an // opener operation (with isOpener = true) needs an ID to store its output. - getNew() string + getNew(args []string) string // opener returns the name of an operation generator (defined in // operations.go) that always creates a new instance of this object. Called by // the test runner when an operation requires one instance of this // operand to exist, and count() == 0. opener() string // count returns the number of live objects being managed by this generator. - // If 0, the opener() operation can be called when necessary. - count() int + // If this generator depends on some other previously-generated args, those + // can be obtained from the args list. If 0, the opener() operation can be + // called when necessary. + count(args []string) int // closeAll closes all managed operands. Used when the test exits, or when a // restart operation executes. closeAll() @@ -93,7 +98,7 @@ func (k *keyGenerator) opener() string { return "" } -func (k *keyGenerator) count() int { +func (k *keyGenerator) count(args []string) int { // Always return a nonzero value so opener() is never called directly. return len(k.liveKeys) + 1 } @@ -111,7 +116,7 @@ func (k *keyGenerator) toString(key storage.MVCCKey) string { return fmt.Sprintf("%s/%d", key.Key, key.Timestamp.WallTime) } -func (k *keyGenerator) get() string { +func (k *keyGenerator) get(args []string) string { // 15% chance of returning a new key even if some exist. if len(k.liveKeys) == 0 || k.rng.Float64() < 0.30 { return k.toString(k.open()) @@ -120,8 +125,8 @@ func (k *keyGenerator) get() string { return k.toString(k.liveKeys[k.rng.Intn(len(k.liveKeys))]) } -func (k *keyGenerator) getNew() string { - return k.get() +func (k *keyGenerator) getNew(args []string) string { + return k.get(args) } func (k *keyGenerator) closeAll() { @@ -148,16 +153,16 @@ func (v *valueGenerator) opener() string { return "" } -func (v *valueGenerator) count() int { +func (v *valueGenerator) count(args []string) int { return 1 } -func (v *valueGenerator) get() string { +func (v *valueGenerator) get(args []string) string { return v.toString(generateBytes(v.rng, 4, maxValueSize)) } -func (v *valueGenerator) getNew() string { - return v.get() +func (v *valueGenerator) getNew(args []string) string { + return v.get(args) } func (v *valueGenerator) closeAll() { @@ -175,12 +180,13 @@ func (v *valueGenerator) parse(input string) []byte { type txnID string type txnGenerator struct { - rng *rand.Rand - testRunner *metaTestRunner - tsGenerator *tsGenerator - liveTxns []txnID - txnIDMap map[txnID]*roachpb.Transaction - openBatches map[txnID]map[readWriterID]struct{} + rng *rand.Rand + testRunner *metaTestRunner + tsGenerator *tsGenerator + liveTxns []txnID + txnIDMap map[txnID]*roachpb.Transaction + openBatches map[txnID]map[readWriterID]struct{} + openSavepoints map[txnID]int // Counts "generated" transactions - i.e. how many txn_open()s have been // inserted so far. Could stay 0 in check mode. txnGenCounter uint64 @@ -192,11 +198,11 @@ func (t *txnGenerator) opener() string { return "txn_open" } -func (t *txnGenerator) count() int { +func (t *txnGenerator) count(args []string) int { return len(t.txnIDMap) } -func (t *txnGenerator) get() string { +func (t *txnGenerator) get(args []string) string { if len(t.liveTxns) == 0 { panic("no open txns") } @@ -206,7 +212,7 @@ func (t *txnGenerator) get() string { // getNew returns a transaction ID, and saves this transaction as a "live" // transaction for generation purposes. Called only during generation, and // must be matched with a generateClose call. -func (t *txnGenerator) getNew() string { +func (t *txnGenerator) getNew(args []string) string { t.txnGenCounter++ id := txnID(fmt.Sprintf("t%d", t.txnGenCounter)) // Increment the timestamp. @@ -221,6 +227,7 @@ func (t *txnGenerator) getNew() string { func (t *txnGenerator) generateClose(id txnID) { delete(t.openBatches, id) delete(t.txnIDMap, id) + delete(t.openSavepoints, id) for i := range t.liveTxns { if t.liveTxns[i] == id { @@ -253,6 +260,39 @@ func (t *txnGenerator) closeAll() { t.liveTxns = nil t.txnIDMap = make(map[txnID]*roachpb.Transaction) t.openBatches = make(map[txnID]map[readWriterID]struct{}) + t.openSavepoints = make(map[txnID]int) +} + +type savepointGenerator struct { + rng *rand.Rand + txnGenerator *txnGenerator +} + +var _ operandGenerator = &savepointGenerator{} + +func (s *savepointGenerator) get(args []string) string { + id := txnID(args[len(args)-1]) + n := s.rng.Intn(s.txnGenerator.openSavepoints[id]) + return strconv.Itoa(n) +} + +func (s *savepointGenerator) getNew(args []string) string { + id := txnID(args[len(args)-1]) + s.txnGenerator.openSavepoints[id]++ + return strconv.Itoa(s.txnGenerator.openSavepoints[id] - 1) +} + +func (s *savepointGenerator) opener() string { + return "txn_create_savepoint" +} + +func (s *savepointGenerator) count(args []string) int { + id := txnID(args[len(args)-1]) + return s.txnGenerator.openSavepoints[id] +} + +func (s *savepointGenerator) closeAll() { + // No-op. } type pastTSGenerator struct { @@ -266,7 +306,7 @@ func (t *pastTSGenerator) opener() string { return "" } -func (t *pastTSGenerator) count() int { +func (t *pastTSGenerator) count(args []string) int { // Always return a non-zero count so opener() is never called. return int(t.tsGenerator.lastTS.WallTime) + 1 } @@ -289,12 +329,12 @@ func (t *pastTSGenerator) parse(input string) hlc.Timestamp { return ts } -func (t *pastTSGenerator) get() string { +func (t *pastTSGenerator) get(args []string) string { return t.toString(t.tsGenerator.randomPastTimestamp(t.rng)) } -func (t *pastTSGenerator) getNew() string { - return t.get() +func (t *pastTSGenerator) getNew(args []string) string { + return t.get(args) } // Similar to pastTSGenerator, except it always increments the "current" timestamp @@ -303,12 +343,12 @@ type nextTSGenerator struct { pastTSGenerator } -func (t *nextTSGenerator) get() string { +func (t *nextTSGenerator) get(args []string) string { return t.toString(t.tsGenerator.generate()) } -func (t *nextTSGenerator) getNew() string { - return t.get() +func (t *nextTSGenerator) getNew(args []string) string { + return t.get(args) } type readWriterID string @@ -323,7 +363,7 @@ type readWriterGenerator struct { var _ operandGenerator = &readWriterGenerator{} -func (w *readWriterGenerator) get() string { +func (w *readWriterGenerator) get(args []string) string { // 25% chance of returning the engine, even if there are live batches. if len(w.liveBatches) == 0 || w.rng.Float64() < 0.25 { return "engine" @@ -333,7 +373,7 @@ func (w *readWriterGenerator) get() string { } // getNew is called during generation to generate a batch ID. -func (w *readWriterGenerator) getNew() string { +func (w *readWriterGenerator) getNew(args []string) string { w.batchGenCounter++ id := readWriterID(fmt.Sprintf("batch%d", w.batchGenCounter)) w.batchIDMap[id] = nil @@ -363,7 +403,7 @@ func (w *readWriterGenerator) generateClose(id readWriterID) { w.m.txnGenerator.clearBatch(id) } -func (w *readWriterGenerator) count() int { +func (w *readWriterGenerator) count(args []string) int { return len(w.batchIDMap) + 1 } @@ -395,7 +435,7 @@ type iteratorGenerator struct { var _ operandGenerator = &iteratorGenerator{} -func (i *iteratorGenerator) get() string { +func (i *iteratorGenerator) get(args []string) string { if len(i.liveIters) == 0 { panic("no open iterators") } @@ -403,7 +443,7 @@ func (i *iteratorGenerator) get() string { return string(i.liveIters[i.rng.Intn(len(i.liveIters))]) } -func (i *iteratorGenerator) getNew() string { +func (i *iteratorGenerator) getNew(args []string) string { i.iterGenCounter++ id := fmt.Sprintf("iter%d", i.iterGenCounter) return id @@ -451,7 +491,7 @@ func (i *iteratorGenerator) opener() string { return "iterator_open" } -func (i *iteratorGenerator) count() int { +func (i *iteratorGenerator) count(args []string) int { return len(i.iterInfo) } @@ -465,12 +505,12 @@ type floatGenerator struct { rng *rand.Rand } -func (f *floatGenerator) get() string { +func (f *floatGenerator) get(args []string) string { return fmt.Sprintf("%.4f", f.rng.Float32()) } -func (f *floatGenerator) getNew() string { - return f.get() +func (f *floatGenerator) getNew(args []string) string { + return f.get(args) } func (f *floatGenerator) parse(input string) float32 { @@ -486,7 +526,7 @@ func (f *floatGenerator) opener() string { return "" } -func (f *floatGenerator) count() int { +func (f *floatGenerator) count(args []string) int { return 1 } diff --git a/pkg/storage/metamorphic/operations.go b/pkg/storage/metamorphic/operations.go index fcee2f6f0910..c7f521b77d89 100644 --- a/pkg/storage/metamorphic/operations.go +++ b/pkg/storage/metamorphic/operations.go @@ -17,6 +17,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -420,6 +421,7 @@ type txnCommitOp struct { func (t txnCommitOp) run(ctx context.Context) string { txn := t.m.getTxn(t.id) txn.Status = roachpb.COMMITTED + txn.Sequence++ for _, span := range txn.LockSpans { intent := roachpb.MakeLockUpdate(txn, span) @@ -430,10 +432,77 @@ func (t txnCommitOp) run(ctx context.Context) string { } } delete(t.m.openTxns, t.id) + delete(t.m.openSavepoints, t.id) return "ok" } +type txnAbortOp struct { + m *metaTestRunner + id txnID +} + +func (t txnAbortOp) run(ctx context.Context) string { + txn := t.m.getTxn(t.id) + txn.Status = roachpb.ABORTED + + for _, span := range txn.LockSpans { + intent := roachpb.MakeLockUpdate(txn, span) + intent.Status = roachpb.ABORTED + _, err := storage.MVCCResolveWriteIntent(context.TODO(), t.m.engine, nil, intent) + if err != nil { + panic(err) + } + } + delete(t.m.openTxns, t.id) + delete(t.m.openSavepoints, t.id) + + return "ok" +} + +type txnCreateSavepointOp struct { + m *metaTestRunner + id txnID + savepoint int +} + +func (t txnCreateSavepointOp) run(ctx context.Context) string { + txn := t.m.getTxn(t.id) + txn.Sequence++ + + // Append txn.Sequence. + if len(t.m.openSavepoints[t.id]) == t.savepoint { + t.m.openSavepoints[t.id] = append(t.m.openSavepoints[t.id], txn.Sequence) + } else { + panic(fmt.Sprintf("mismatching savepoint index: %d != %d", len(t.m.openSavepoints[t.id]), t.savepoint)) + } + + return fmt.Sprintf("savepoint %d", t.savepoint) +} + +type txnRollbackSavepointOp struct { + m *metaTestRunner + id txnID + savepoint int +} + +func (t txnRollbackSavepointOp) run(ctx context.Context) string { + txn := t.m.getTxn(t.id) + txn.Sequence++ + + savepoints := t.m.openSavepoints[t.id] + if len(savepoints) == 0 || t.savepoint > len(savepoints) { + panic(fmt.Sprintf("got a higher savepoint idx %d than allowed for txn %s", t.savepoint, t.id)) + } + + ignoredSeqNumRange := enginepb.IgnoredSeqNumRange{ + Start: savepoints[t.savepoint], + End: txn.Sequence, + } + txn.AddIgnoredSeqNumRange(ignoredSeqNumRange) + return "ok" +} + type batchOpenOp struct { m *metaTestRunner id readWriterID @@ -523,7 +592,7 @@ func (i iterSeekOp) run(ctx context.Context) string { if i.seekLT { return "noop due to missing seekLT support in rocksdb batch iterators" } - // RocksDB batch iterators do not account for lower bounds consistently: + // RocksDB batch iterators do not account åfor lower bounds consistently: // https://github.com/cockroachdb/cockroach/issues/44512 // In the meantime, ensure the SeekGE key >= lower bound. lowerBound := iterInfo.lowerBound @@ -820,9 +889,6 @@ var opGenerators = []opGenerator{ txn: txn, } }, - dependentOps: func(m *metaTestRunner, args ...string) (results []opReference) { - return closeItersOnBatch(m, readWriterID(args[0])) - }, operands: []operandType{ operandReadWriter, operandMVCCKey, @@ -855,9 +921,6 @@ var opGenerators = []opGenerator{ endTime: endTime, } }, - dependentOps: func(m *metaTestRunner, args ...string) (results []opReference) { - return closeItersOnBatch(m, readWriterID(args[0])) - }, operands: []operandType{ operandReadWriter, operandMVCCKey, @@ -1000,7 +1063,73 @@ var opGenerators = []opGenerator{ operands: []operandType{ operandTransaction, }, - weight: 100, + weight: 50, + }, + { + name: "txn_abort", + generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { + m.txnGenerator.generateClose(txnID(args[0])) + return &txnAbortOp{ + m: m, + id: txnID(args[0]), + } + }, + dependentOps: func(m *metaTestRunner, args ...string) (result []opReference) { + txn := txnID(args[0]) + + // A transaction could have in-flight writes in some batches. Get a list + // of all those batches, and dispatch batch_commit operations for them. + for batch := range m.txnGenerator.openBatches[txn] { + result = append(result, opReference{ + generator: m.nameToGenerator["batch_commit"], + args: []string{string(batch)}, + }) + } + return + }, + operands: []operandType{ + operandTransaction, + }, + weight: 50, + }, + { + name: "txn_create_savepoint", + generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { + savepoint, err := strconv.ParseInt(args[1], 10, 32) + if err != nil { + panic(err.Error()) + } + return &txnCreateSavepointOp{ + m: m, + id: txnID(args[0]), + savepoint: int(savepoint), + } + }, + operands: []operandType{ + operandTransaction, + operandSavepoint, + }, + isOpener: true, + weight: 10, + }, + { + name: "txn_rollback_savepoint", + generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { + savepoint, err := strconv.ParseInt(args[1], 10, 32) + if err != nil { + panic(err.Error()) + } + return &txnRollbackSavepointOp{ + m: m, + id: txnID(args[0]), + savepoint: int(savepoint), + } + }, + operands: []operandType{ + operandTransaction, + operandSavepoint, + }, + weight: 10, }, { name: "batch_open", From d94e7b28d5b82a798005dd688006395105551afb Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Mon, 4 Oct 2021 17:58:17 -0400 Subject: [PATCH 052/205] metamorphic: Move mvcc metamorphic tests to in-mem storage engine Currently, the mvcc metamorphic tests run for a very small number of operations, as they're required to use the disk due to historic constraints around there being no in-mem virtual FS that would be able to serve both rocksdb and pebble. As this constraint no longer exists, this change updates those tests to move to a much faster in-mem FS. As a result, we're able to bump the number of operations by a 100x factor. Also updates seed generation to be more random. And some other minor updates to add dependent operations that were previously missing. Release note: None. --- pkg/storage/metamorphic/generator.go | 36 ++++++++---- pkg/storage/metamorphic/meta_test.go | 79 +++++++++++++-------------- pkg/storage/metamorphic/operands.go | 9 +-- pkg/storage/metamorphic/operations.go | 11 +++- 4 files changed, 77 insertions(+), 58 deletions(-) diff --git a/pkg/storage/metamorphic/generator.go b/pkg/storage/metamorphic/generator.go index 7989978af44a..bf6f88c3c0fd 100644 --- a/pkg/storage/metamorphic/generator.go +++ b/pkg/storage/metamorphic/generator.go @@ -26,6 +26,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/storage/enginepb" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/pebble" + "github.com/cockroachdb/pebble/vfs" ) const zipfMax uint64 = 100000 @@ -37,19 +38,28 @@ func makeStorageConfig(path string) base.StorageConfig { } } -func createTestPebbleEngine(path string, seed int64) (storage.Engine, error) { - return storage.Open( - context.Background(), - storage.Filesystem(path), - storage.CacheSize(1<<20 /* 1 MiB */), - storage.Settings(cluster.MakeTestingClusterSettings())) +func createTestPebbleEngine(path string, seed int64, fs vfs.FS) (storage.Engine, error) { + pebbleConfig := storage.PebbleConfig{ + StorageConfig: makeStorageConfig(path), + Opts: storage.DefaultPebbleOptions(), + } + if fs != nil { + pebbleConfig.Opts.FS = fs + } + pebbleConfig.Opts.Cache = pebble.NewCache(1 << 20) + defer pebbleConfig.Opts.Cache.Unref() + + return storage.NewPebble(context.Background(), pebbleConfig) } -func createTestPebbleManySSTs(path string, seed int64) (storage.Engine, error) { +func createTestPebbleManySSTs(path string, seed int64, fs vfs.FS) (storage.Engine, error) { pebbleConfig := storage.PebbleConfig{ StorageConfig: makeStorageConfig(path), Opts: storage.DefaultPebbleOptions(), } + if fs != nil { + pebbleConfig.Opts.FS = fs + } levels := pebbleConfig.Opts.Levels for i := range levels { if i == 0 { @@ -68,9 +78,12 @@ func rngIntRange(rng *rand.Rand, min int64, max int64) int64 { return min + rng.Int63n(max-min) } -func createTestPebbleVarOpts(path string, seed int64) (storage.Engine, error) { +func createTestPebbleVarOpts(path string, seed int64, fs vfs.FS) (storage.Engine, error) { opts := storage.DefaultPebbleOptions() + if fs != nil { + opts.FS = fs + } rng := rand.New(rand.NewSource(seed)) opts.BytesPerSync = 1 << rngIntRange(rng, 8, 30) opts.FlushSplitBytes = 1 << rng.Intn(20) @@ -111,7 +124,7 @@ func createTestPebbleVarOpts(path string, seed int64) (storage.Engine, error) { type engineImpl struct { name string - create func(path string, seed int64) (storage.Engine, error) + create func(path string, seed int64, fs vfs.FS) (storage.Engine, error) } var _ fmt.Stringer = &engineImpl{} @@ -133,6 +146,7 @@ type metaTestRunner struct { rng *rand.Rand seed int64 path string + engineFS vfs.FS engineImpls []engineImpl curEngine int restarts bool @@ -166,7 +180,7 @@ func (m *metaTestRunner) init() { m.curEngine = 0 var err error - m.engine, err = m.engineImpls[0].create(m.path, m.seed) + m.engine, err = m.engineImpls[0].create(m.path, m.seed, m.engineFS) if err != nil { m.engine = nil m.t.Fatal(err) @@ -352,7 +366,7 @@ func (m *metaTestRunner) restart() (string, string) { } var err error - m.engine, err = m.engineImpls[m.curEngine].create(m.path, m.seed) + m.engine, err = m.engineImpls[m.curEngine].create(m.path, m.seed, m.engineFS) if err != nil { m.engine = nil m.t.Fatal(err) diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index 76f612e09827..93380cf6bd58 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -15,7 +15,6 @@ import ( "flag" "fmt" "io" - "math/rand" "os" "path/filepath" "strings" @@ -25,20 +24,23 @@ import ( "github.com/cockroachdb/cockroach/pkg/testutils/skip" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/randutil" "github.com/cockroachdb/errors/oserror" + "github.com/cockroachdb/pebble/vfs" ) var ( keep = flag.Bool("keep", false, "keep temp directories after test") check = flag.String("check", "", "run operations in specified file and check output for equality") - seed = flag.Int64("seed", 456, "specify seed to use for random number generator") - opCount = flag.Int("operations", 1000, "number of MVCC operations to generate and run") + seed = flag.Int64("seed", randutil.NewPseudoSeed(), "specify seed to use for random number generator") + opCount = flag.Int("operations", 100000, "number of MVCC operations to generate and run") ) type testRun struct { ctx context.Context t *testing.T seed int64 + inMem bool checkFile string restarts bool engineSequences [][]engineImpl @@ -47,6 +49,7 @@ type testRun struct { type testRunForEngines struct { ctx context.Context t *testing.T + inMem bool seed int64 restarts bool checkFile io.Reader @@ -61,11 +64,16 @@ func runMetaTestForEngines(run testRunForEngines) { cleanup() } }() + var fs vfs.FS + if run.inMem && !*keep { + fs = vfs.NewMem() + } testRunner := metaTestRunner{ ctx: run.ctx, t: run.t, w: run.outputFile, + engineFS: fs, seed: run.seed, restarts: run.restarts, engineImpls: run.engineSequence, @@ -141,6 +149,7 @@ func runMetaTest(run testRun) { engineRun := testRunForEngines{ ctx: run.ctx, t: t, + inMem: run.inMem, seed: run.seed, restarts: run.restarts, checkFile: checkFileReader, @@ -163,25 +172,19 @@ func TestPebbleEquivalence(t *testing.T) { // This test times out with the race detector enabled. skip.UnderRace(t) - // Have one fixed seed, one user-specified seed, and one random seed. - seeds := []int64{123, *seed, rand.Int63()} - - for _, seed := range seeds { - t.Run(fmt.Sprintf("seed=%d", seed), func(t *testing.T) { - run := testRun{ - ctx: ctx, - t: t, - seed: seed, - restarts: false, - engineSequences: [][]engineImpl{ - {engineImplPebble}, - {engineImplPebbleManySSTs}, - {engineImplPebbleVarOpts}, - }, - } - runMetaTest(run) - }) + run := testRun{ + ctx: ctx, + t: t, + seed: *seed, + restarts: false, + inMem: true, + engineSequences: [][]engineImpl{ + {engineImplPebble}, + {engineImplPebbleManySSTs}, + {engineImplPebbleVarOpts}, + }, } + runMetaTest(run) } // TestPebbleRestarts runs the MVCC Metamorphic test suite with restarts @@ -194,30 +197,23 @@ func TestPebbleRestarts(t *testing.T) { skip.UnderRace(t) ctx := context.Background() - - // Have one fixed seed, one user-specified seed, and one random seed. - seeds := []int64{123, *seed, rand.Int63()} - - for _, seed := range seeds { - t.Run(fmt.Sprintf("seed=%d", seed), func(t *testing.T) { - run := testRun{ - ctx: ctx, - t: t, - seed: seed, - restarts: true, - engineSequences: [][]engineImpl{ - {engineImplPebble}, - {engineImplPebble, engineImplPebble}, - {engineImplPebble, engineImplPebbleManySSTs, engineImplPebbleVarOpts}, - }, - } - runMetaTest(run) - }) + run := testRun{ + ctx: ctx, + t: t, + seed: *seed, + inMem: true, + restarts: true, + engineSequences: [][]engineImpl{ + {engineImplPebble}, + {engineImplPebble, engineImplPebble}, + {engineImplPebble, engineImplPebbleManySSTs, engineImplPebbleVarOpts}, + }, } + runMetaTest(run) } // TestPebbleCheck checks whether the output file specified with --check has -// matching behavior across rocks/pebble. +// matching behavior with a standard run of pebble. func TestPebbleCheck(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -234,6 +230,7 @@ func TestPebbleCheck(t *testing.T) { t: t, checkFile: *check, restarts: true, + inMem: true, engineSequences: [][]engineImpl{ {engineImplPebble}, }, diff --git a/pkg/storage/metamorphic/operands.go b/pkg/storage/metamorphic/operands.go index 912c3a875fef..26d67f3941d0 100644 --- a/pkg/storage/metamorphic/operands.go +++ b/pkg/storage/metamorphic/operands.go @@ -419,10 +419,11 @@ func (w *readWriterGenerator) closeAll() { type iteratorID string type iteratorInfo struct { - id iteratorID - iter storage.MVCCIterator - lowerBound roachpb.Key - isBatchIter bool + id iteratorID + iter storage.MVCCIterator + lowerBound roachpb.Key + isBatchIter bool + isEngineIter bool } type iteratorGenerator struct { diff --git a/pkg/storage/metamorphic/operations.go b/pkg/storage/metamorphic/operations.go index c7f521b77d89..2d00511ccc93 100644 --- a/pkg/storage/metamorphic/operations.go +++ b/pkg/storage/metamorphic/operations.go @@ -297,8 +297,9 @@ type mvccClearTimeRangeOp struct { func (m mvccClearTimeRangeOp) run(ctx context.Context) string { writer := m.m.getReadWriter(m.writer) + useTBI := m.writer == "engine" span, err := storage.MVCCClearTimeRange(ctx, writer, &enginepb.MVCCStats{}, m.key, m.endKey, - m.startTime, m.endTime, math.MaxInt64, math.MaxInt64, true /* useTBI */) + m.startTime, m.endTime, math.MaxInt64, math.MaxInt64, useTBI) if err != nil { return fmt.Sprintf("error: %s", err) } @@ -542,7 +543,7 @@ type iterOpenOp struct { func (i iterOpenOp) run(ctx context.Context) string { rw := i.m.getReadWriter(i.rw) - iter := rw.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + iter := rw.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{ Prefix: false, LowerBound: i.key, UpperBound: i.endKey.Next(), @@ -889,6 +890,9 @@ var opGenerators = []opGenerator{ txn: txn, } }, + dependentOps: func(m *metaTestRunner, args ...string) []opReference { + return closeItersOnBatch(m, readWriterID(args[0])) + }, operands: []operandType{ operandReadWriter, operandMVCCKey, @@ -921,6 +925,9 @@ var opGenerators = []opGenerator{ endTime: endTime, } }, + dependentOps: func(m *metaTestRunner, args ...string) []opReference { + return closeItersOnBatch(m, readWriterID(args[0])) + }, operands: []operandType{ operandReadWriter, operandMVCCKey, From 4dded53261d7a755646e298d5db1871ab85332e5 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Thu, 7 Oct 2021 14:33:29 -0400 Subject: [PATCH 053/205] metamorphic: Create pebble with different standard / random options Previously, we only ran metamorphic test operations across 2 standard and 1 random pebble option configs. This change makes a refactor to enable running many more, and moves us to 18 standard and 10 random options. This change is also enabled by the fact that we can run more tests in the same amount of time, thanks to the move to memFS. Release note: None. --- pkg/storage/metamorphic/generator.go | 87 +++++++--------- pkg/storage/metamorphic/meta_test.go | 97 ++++++++++++------ pkg/storage/metamorphic/operations.go | 5 +- pkg/storage/metamorphic/options.go | 141 ++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 84 deletions(-) create mode 100644 pkg/storage/metamorphic/options.go diff --git a/pkg/storage/metamorphic/generator.go b/pkg/storage/metamorphic/generator.go index bf6f88c3c0fd..61d4758e546a 100644 --- a/pkg/storage/metamorphic/generator.go +++ b/pkg/storage/metamorphic/generator.go @@ -38,41 +38,6 @@ func makeStorageConfig(path string) base.StorageConfig { } } -func createTestPebbleEngine(path string, seed int64, fs vfs.FS) (storage.Engine, error) { - pebbleConfig := storage.PebbleConfig{ - StorageConfig: makeStorageConfig(path), - Opts: storage.DefaultPebbleOptions(), - } - if fs != nil { - pebbleConfig.Opts.FS = fs - } - pebbleConfig.Opts.Cache = pebble.NewCache(1 << 20) - defer pebbleConfig.Opts.Cache.Unref() - - return storage.NewPebble(context.Background(), pebbleConfig) -} - -func createTestPebbleManySSTs(path string, seed int64, fs vfs.FS) (storage.Engine, error) { - pebbleConfig := storage.PebbleConfig{ - StorageConfig: makeStorageConfig(path), - Opts: storage.DefaultPebbleOptions(), - } - if fs != nil { - pebbleConfig.Opts.FS = fs - } - levels := pebbleConfig.Opts.Levels - for i := range levels { - if i == 0 { - levels[i].TargetFileSize = 1 << 8 // 256 bytes - } else { - levels[i].TargetFileSize = levels[i-1].TargetFileSize * 2 - } - } - pebbleConfig.Opts.Cache = pebble.NewCache(1 << 20) - defer pebbleConfig.Opts.Cache.Unref() - - return storage.NewPebble(context.Background(), pebbleConfig) -} func rngIntRange(rng *rand.Rand, min int64, max int64) int64 { return min + rng.Int63n(max-min) @@ -122,20 +87,40 @@ func createTestPebbleVarOpts(path string, seed int64, fs vfs.FS) (storage.Engine return storage.NewPebble(context.Background(), pebbleConfig) } -type engineImpl struct { +type engineConfig struct { name string - create func(path string, seed int64, fs vfs.FS) (storage.Engine, error) + opts *pebble.Options } -var _ fmt.Stringer = &engineImpl{} +func (e *engineConfig) create(path string, fs vfs.FS) (storage.Engine, error) { + pebbleConfig := storage.PebbleConfig{ + StorageConfig: makeStorageConfig(path), + Opts: e.opts, + } + if pebbleConfig.Opts == nil { + pebbleConfig.Opts = storage.DefaultPebbleOptions() + } + if fs != nil { + pebbleConfig.Opts.FS = fs + } + pebbleConfig.Opts.Cache = pebble.NewCache(1 << 20) + defer pebbleConfig.Opts.Cache.Unref() + + return storage.NewPebble(context.Background(), pebbleConfig) +} + +var _ fmt.Stringer = &engineConfig{} -func (e *engineImpl) String() string { +func (e *engineConfig) String() string { return e.name } -var engineImplPebble = engineImpl{"pebble", createTestPebbleEngine} -var engineImplPebbleManySSTs = engineImpl{"pebble_many_ssts", createTestPebbleManySSTs} -var engineImplPebbleVarOpts = engineImpl{"pebble_var_opts", createTestPebbleVarOpts} +var engineConfigStandard = engineConfig{"standard=0", storage.DefaultPebbleOptions()} + +type engineSequence struct { + name string + configs []engineConfig +} // Object to store info corresponding to one metamorphic test run. Responsible // for generating and executing operations. @@ -147,7 +132,7 @@ type metaTestRunner struct { seed int64 path string engineFS vfs.FS - engineImpls []engineImpl + engineSeq engineSequence curEngine int restarts bool engine storage.Engine @@ -180,7 +165,8 @@ func (m *metaTestRunner) init() { m.curEngine = 0 var err error - m.engine, err = m.engineImpls[0].create(m.path, m.seed, m.engineFS) + m.engine, err = m.engineSeq.configs[0].create(m.path, m.engineFS) + m.printComment(fmt.Sprintf("engine options: %s", m.engineSeq.configs[0].opts.String())) if err != nil { m.engine = nil m.t.Fatal(err) @@ -353,25 +339,25 @@ func (m *metaTestRunner) generateAndRun(n int) { } // Closes the current engine and starts another one up, with the same path. -// Returns the engine transition that -func (m *metaTestRunner) restart() (string, string) { +// Returns the old and new engine configs. +func (m *metaTestRunner) restart() (engineConfig, engineConfig) { m.closeAll() - oldEngineName := m.engineImpls[m.curEngine].name + oldEngine := m.engineSeq.configs[m.curEngine] // TODO(itsbilal): Select engines at random instead of cycling through them. m.curEngine++ - if m.curEngine >= len(m.engineImpls) { + if m.curEngine >= len(m.engineSeq.configs) { // If we're restarting more times than the number of engine implementations // specified, loop back around to the first engine type specified. m.curEngine = 0 } var err error - m.engine, err = m.engineImpls[m.curEngine].create(m.path, m.seed, m.engineFS) + m.engine, err = m.engineSeq.configs[m.curEngine].create(m.path, m.engineFS) if err != nil { m.engine = nil m.t.Fatal(err) } - return oldEngineName, m.engineImpls[m.curEngine].name + return oldEngine, m.engineSeq.configs[m.curEngine] } func (m *metaTestRunner) parseFileAndRun(f io.Reader) { @@ -541,6 +527,7 @@ func (m *metaTestRunner) printOp(opName string, argStrings []string, output stri // printComment prints a comment line into the output file. Supports single-line // comments only. func (m *metaTestRunner) printComment(comment string) { + comment = strings.ReplaceAll(comment, "\n", "\n# ") fmt.Fprintf(m.w, "# %s\n", comment) } diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index 93380cf6bd58..5a502eac8a5e 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -17,7 +17,6 @@ import ( "io" "os" "path/filepath" - "strings" "testing" "github.com/cockroachdb/cockroach/pkg/testutils" @@ -43,7 +42,7 @@ type testRun struct { inMem bool checkFile string restarts bool - engineSequences [][]engineImpl + engineSequences []engineSequence } type testRunForEngines struct { @@ -54,7 +53,7 @@ type testRunForEngines struct { restarts bool checkFile io.Reader outputFile io.Writer - engineSequence []engineImpl + engineSequence engineSequence } func runMetaTestForEngines(run testRunForEngines) { @@ -70,14 +69,14 @@ func runMetaTestForEngines(run testRunForEngines) { } testRunner := metaTestRunner{ - ctx: run.ctx, - t: run.t, - w: run.outputFile, - engineFS: fs, - seed: run.seed, - restarts: run.restarts, - engineImpls: run.engineSequence, - path: filepath.Join(tempDir, "store"), + ctx: run.ctx, + t: run.t, + w: run.outputFile, + engineFS: fs, + seed: run.seed, + restarts: run.restarts, + engineSeq: run.engineSequence, + path: filepath.Join(tempDir, "store"), } fmt.Printf("store path = %s\n", testRunner.path) @@ -106,12 +105,8 @@ func runMetaTest(run testRun) { fmt.Printf("first run output file: %s\n", firstRunOutput) for _, engineSequence := range run.engineSequences { - var engineNames []string - for _, engineImpl := range engineSequence { - engineNames = append(engineNames, engineImpl.name) - } - t.Run(strings.Join(engineNames, ","), func(t *testing.T) { + t.Run(fmt.Sprintf("engine/%s", engineSequence.name), func(t *testing.T) { innerTempDir, cleanup := testutils.TempDir(t) defer func() { if !*keep && !t.Failed() { @@ -172,17 +167,37 @@ func TestPebbleEquivalence(t *testing.T) { // This test times out with the race detector enabled. skip.UnderRace(t) + engineSeqs := make([]engineSequence, 0, numStandardOptions + numRandomOptions) + + for i := 0; i < numStandardOptions; i++ { + engineSeq := engineSequence{ + configs: []engineConfig{{ + name: fmt.Sprintf("standard=%d", i), + opts: standardOptions(i), + }}, + } + engineSeq.name = engineSeq.configs[0].name + engineSeqs = append(engineSeqs, engineSeq) + } + + for i := 0; i < numRandomOptions; i++ { + engineSeq := engineSequence{ + configs: []engineConfig{{ + name: fmt.Sprintf("random=%d", i), + opts: randomOptions(i, *seed), + }}, + } + engineSeq.name = engineSeq.configs[0].name + engineSeqs = append(engineSeqs, engineSeq) + } + run := testRun{ - ctx: ctx, - t: t, - seed: *seed, - restarts: false, - inMem: true, - engineSequences: [][]engineImpl{ - {engineImplPebble}, - {engineImplPebbleManySSTs}, - {engineImplPebbleVarOpts}, - }, + ctx: ctx, + t: t, + seed: *seed, + restarts: false, + inMem: true, + engineSequences: engineSeqs, } runMetaTest(run) } @@ -196,6 +211,22 @@ func TestPebbleRestarts(t *testing.T) { // This test times out with the race detector enabled. skip.UnderRace(t) + engineConfigs := make([]engineConfig, 0, numStandardOptions + numRandomOptions) + // Create one config sequence that contains all options. + for i := 0; i < numStandardOptions; i++ { + engineConfigs = append(engineConfigs, engineConfig{ + name: fmt.Sprintf("standard=%d", i), + opts: standardOptions(i), + }) + } + + for i := 0; i < numRandomOptions; i++ { + engineConfigs = append(engineConfigs, engineConfig{ + name: fmt.Sprintf("random=%d", i), + opts: randomOptions(i, *seed), + }) + } + ctx := context.Background() run := testRun{ ctx: ctx, @@ -203,10 +234,12 @@ func TestPebbleRestarts(t *testing.T) { seed: *seed, inMem: true, restarts: true, - engineSequences: [][]engineImpl{ - {engineImplPebble}, - {engineImplPebble, engineImplPebble}, - {engineImplPebble, engineImplPebbleManySSTs, engineImplPebbleVarOpts}, + engineSequences: []engineSequence{ + {name: "standard", configs: []engineConfig{engineConfigStandard}}, + { + name: fmt.Sprintf("standards=%d,randoms=%d", numStandardOptions, numRandomOptions), + configs: engineConfigs, + }, }, } runMetaTest(run) @@ -231,8 +264,8 @@ func TestPebbleCheck(t *testing.T) { checkFile: *check, restarts: true, inMem: true, - engineSequences: [][]engineImpl{ - {engineImplPebble}, + engineSequences: []engineSequence{ + {name: "standard", configs: []engineConfig{engineConfigStandard}}, }, } runMetaTest(run) diff --git a/pkg/storage/metamorphic/operations.go b/pkg/storage/metamorphic/operations.go index 2d00511ccc93..6575220f6bc2 100644 --- a/pkg/storage/metamorphic/operations.go +++ b/pkg/storage/metamorphic/operations.go @@ -728,8 +728,9 @@ func (r restartOp) run(ctx context.Context) string { return "ok" } - oldEngineName, newEngineName := r.m.restart() - r.m.printComment(fmt.Sprintf("restarting: %s -> %s", oldEngineName, newEngineName)) + oldEngine, newEngine := r.m.restart() + r.m.printComment(fmt.Sprintf("restarting: %s -> %s", oldEngine.name, newEngine.name)) + r.m.printComment(fmt.Sprintf("new options: %s", newEngine.opts.String())) return "ok" } diff --git a/pkg/storage/metamorphic/options.go b/pkg/storage/metamorphic/options.go new file mode 100644 index 000000000000..f3763485f7f8 --- /dev/null +++ b/pkg/storage/metamorphic/options.go @@ -0,0 +1,141 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package metamorphic + +import ( + "math/rand" + + "github.com/cockroachdb/cockroach/pkg/storage" + "github.com/cockroachdb/pebble" +) + +const numStandardOptions = 18 +const numRandomOptions = 10 + +func standardOptions(i int) *pebble.Options { + stdOpts := []string{ + 0: "", // default options + 1: ` +[Options] + cache_size=1 +`, + 2: ` +[Options] + l0_compaction_threshold=1 +`, + 3: ` +[Options] + l0_compaction_threshold=1 + l0_stop_writes_threshold=1 +`, + 4: ` +[Options] + lbase_max_bytes=1 +`, + 5: ` +[Options] + max_manifest_file_size=1 +`, + 6: ` +[Options] + max_open_files=1 +`, + 7: ` +[Options] + mem_table_size=1000 +`, + 8: ` +[Options] + mem_table_stop_writes_threshold=2 +`, + 9: ` +[Options] + wal_dir=wal +`, + 10: ` +[Level "0"] + block_restart_interval=1 +`, + 11: ` +[Level "0"] + block_size=1 +`, + 12: ` +[Level "0"] + compression=NoCompression +`, + 13: ` +[Level "0"] + index_block_size=1 +`, + 14: ` +[Level "0"] + target_file_size=1 +`, + 15: ` +[Level "0"] + filter_policy=none +`, + // 1GB + 16: ` +[Options] + bytes_per_sync=1073741824 +`, + 17: ` +[Options] + max_concurrent_compactions=2 +`, + } + if i < 0 || i > len(stdOpts) { + panic("invalid index for standard option") + } + opts := storage.DefaultPebbleOptions() + if err := opts.Parse(stdOpts[i], nil); err != nil { + panic(err) + } + return opts +} + +func randomOptions(offset int, seed int64) *pebble.Options { + opts := storage.DefaultPebbleOptions() + + rng := rand.New(rand.NewSource(seed + int64(offset))) + opts.BytesPerSync = 1 << rngIntRange(rng, 8, 30) + opts.FlushSplitBytes = 1 << rng.Intn(20) + opts.LBaseMaxBytes = 1 << rngIntRange(rng, 8, 30) + opts.L0CompactionThreshold = int(rngIntRange(rng, 1, 10)) + opts.L0StopWritesThreshold = int(rngIntRange(rng, 1, 32)) + if opts.L0StopWritesThreshold < opts.L0CompactionThreshold { + opts.L0StopWritesThreshold = opts.L0CompactionThreshold + } + for i := range opts.Levels { + if i == 0 { + opts.Levels[i].BlockRestartInterval = int(rngIntRange(rng, 1, 64)) + opts.Levels[i].BlockSize = 1 << rngIntRange(rng, 1, 20) + opts.Levels[i].BlockSizeThreshold = int(rngIntRange(rng, 50, 100)) + opts.Levels[i].IndexBlockSize = opts.Levels[i].BlockSize + opts.Levels[i].TargetFileSize = 1 << rngIntRange(rng, 1, 20) + } else { + opts.Levels[i] = opts.Levels[i-1] + opts.Levels[i].TargetFileSize = opts.Levels[i-1].TargetFileSize * 2 + } + } + opts.MaxManifestFileSize = 1 << rngIntRange(rng, 1, 28) + opts.MaxOpenFiles = int(rngIntRange(rng, 20, 2000)) + opts.MemTableSize = 1 << rngIntRange(rng, 10, 28) + opts.MemTableStopWritesThreshold = int(rngIntRange(rng, 2, 7)) + opts.MaxConcurrentCompactions = int(rngIntRange(rng, 1, 4)) + + opts.Cache = pebble.NewCache(1 << rngIntRange(rng, 1, 30)) + defer opts.Cache.Unref() + + return opts +} From fc02fae36f72d1016f65aa98d1615655586a9eef Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Thu, 14 Oct 2021 13:29:19 -0400 Subject: [PATCH 054/205] storage/metamorphic: Don't have writers clash on keys Previously, the key generator would generate any random key for transactional operations, without paying attention to the fact that that could lead to intent rewriting if a different writer on the same txn had already written to the key. This was resulting in some set -> set -> singledels that were non-deterministic based on compaction ordering in Pebble and causing tests to fail. This change causes metamorphic tests to pass again by ensuring that transactional operations get either unused keys, or keys being used by that particular writer. Release note: None. --- pkg/storage/metamorphic/BUILD.bazel | 4 + pkg/storage/metamorphic/generator.go | 83 ++++------ pkg/storage/metamorphic/meta_test.go | 66 +++++--- pkg/storage/metamorphic/operands.go | 208 +++++++++++++++++++++++--- pkg/storage/metamorphic/operations.go | 127 +++++++++------- 5 files changed, 336 insertions(+), 152 deletions(-) diff --git a/pkg/storage/metamorphic/BUILD.bazel b/pkg/storage/metamorphic/BUILD.bazel index 94b1f94c2f67..96badf5dc180 100644 --- a/pkg/storage/metamorphic/BUILD.bazel +++ b/pkg/storage/metamorphic/BUILD.bazel @@ -7,6 +7,7 @@ go_library( "generator.go", "operands.go", "operations.go", + "options.go", ], importpath = "github.com/cockroachdb/cockroach/pkg/storage/metamorphic", visibility = ["//visibility:public"], @@ -22,6 +23,7 @@ go_library( "//pkg/util/uint128", "//pkg/util/uuid", "@com_github_cockroachdb_pebble//:pebble", + "@com_github_cockroachdb_pebble//vfs", ], ) @@ -38,6 +40,8 @@ go_test( "//pkg/testutils/skip", "//pkg/util/leaktest", "//pkg/util/log", + "//pkg/util/randutil", "@com_github_cockroachdb_errors//oserror", + "@com_github_cockroachdb_pebble//vfs", ], ) diff --git a/pkg/storage/metamorphic/generator.go b/pkg/storage/metamorphic/generator.go index 61d4758e546a..1e254c26827c 100644 --- a/pkg/storage/metamorphic/generator.go +++ b/pkg/storage/metamorphic/generator.go @@ -38,58 +38,13 @@ func makeStorageConfig(path string) base.StorageConfig { } } - func rngIntRange(rng *rand.Rand, min int64, max int64) int64 { return min + rng.Int63n(max-min) } -func createTestPebbleVarOpts(path string, seed int64, fs vfs.FS) (storage.Engine, error) { - opts := storage.DefaultPebbleOptions() - - if fs != nil { - opts.FS = fs - } - rng := rand.New(rand.NewSource(seed)) - opts.BytesPerSync = 1 << rngIntRange(rng, 8, 30) - opts.FlushSplitBytes = 1 << rng.Intn(20) - opts.LBaseMaxBytes = 1 << rngIntRange(rng, 8, 30) - opts.L0CompactionThreshold = int(rngIntRange(rng, 1, 10)) - opts.L0StopWritesThreshold = int(rngIntRange(rng, 1, 32)) - if opts.L0StopWritesThreshold < opts.L0CompactionThreshold { - opts.L0StopWritesThreshold = opts.L0CompactionThreshold - } - for i := range opts.Levels { - if i == 0 { - opts.Levels[i].BlockRestartInterval = int(rngIntRange(rng, 1, 64)) - opts.Levels[i].BlockSize = 1 << rngIntRange(rng, 1, 20) - opts.Levels[i].BlockSizeThreshold = int(rngIntRange(rng, 50, 100)) - opts.Levels[i].IndexBlockSize = opts.Levels[i].BlockSize - opts.Levels[i].TargetFileSize = 1 << rngIntRange(rng, 1, 20) - } else { - opts.Levels[i] = opts.Levels[i-1] - opts.Levels[i].TargetFileSize = opts.Levels[i-1].TargetFileSize * 2 - } - } - opts.MaxManifestFileSize = 1 << rngIntRange(rng, 1, 28) - opts.MaxOpenFiles = int(rngIntRange(rng, 20, 2000)) - opts.MemTableSize = 1 << rngIntRange(rng, 10, 28) - opts.MemTableStopWritesThreshold = int(rngIntRange(rng, 2, 7)) - opts.MaxConcurrentCompactions = int(rngIntRange(rng, 1, 4)) - - opts.Cache = pebble.NewCache(1 << rngIntRange(rng, 1, 30)) - defer opts.Cache.Unref() - - pebbleConfig := storage.PebbleConfig{ - StorageConfig: makeStorageConfig(path), - Opts: opts, - } - - return storage.NewPebble(context.Background(), pebbleConfig) -} - type engineConfig struct { - name string - opts *pebble.Options + name string + opts *pebble.Options } func (e *engineConfig) create(path string, fs vfs.FS) (storage.Engine, error) { @@ -143,6 +98,7 @@ type metaTestRunner struct { rwGenerator *readWriterGenerator iterGenerator *iteratorGenerator keyGenerator *keyGenerator + txnKeyGenerator *txnKeyGenerator valueGenerator *valueGenerator pastTSGenerator *pastTSGenerator nextTSGenerator *nextTSGenerator @@ -163,6 +119,7 @@ func (m *metaTestRunner) init() { m.rng = rand.New(rand.NewSource(m.seed)) m.tsGenerator.init(m.rng) m.curEngine = 0 + m.printComment(fmt.Sprintf("seed: %d", m.seed)) var err error m.engine, err = m.engineSeq.configs[0].create(m.path, m.engineFS) @@ -179,6 +136,7 @@ func (m *metaTestRunner) init() { tsGenerator: &m.tsGenerator, txnIDMap: make(map[txnID]*roachpb.Transaction), openBatches: make(map[txnID]map[readWriterID]struct{}), + inUseKeys: make(map[txnID][]writtenKeySpan), openSavepoints: make(map[txnID]int), testRunner: m, } @@ -200,6 +158,10 @@ func (m *metaTestRunner) init() { rng: m.rng, tsGenerator: &m.tsGenerator, } + m.txnKeyGenerator = &txnKeyGenerator{ + txns: m.txnGenerator, + keys: m.keyGenerator, + } m.valueGenerator = &valueGenerator{m.rng} m.pastTSGenerator = &pastTSGenerator{ rng: m.rng, @@ -215,22 +177,27 @@ func (m *metaTestRunner) init() { m.boolGenerator = &boolGenerator{rng: m.rng} m.opGenerators = map[operandType]operandGenerator{ - operandTransaction: m.txnGenerator, - operandReadWriter: m.rwGenerator, - operandMVCCKey: m.keyGenerator, - operandPastTS: m.pastTSGenerator, - operandNextTS: m.nextTSGenerator, - operandValue: m.valueGenerator, - operandIterator: m.iterGenerator, - operandFloat: m.floatGenerator, - operandBool: m.boolGenerator, - operandSavepoint: m.spGenerator, + operandTransaction: m.txnGenerator, + operandReadWriter: m.rwGenerator, + operandMVCCKey: m.keyGenerator, + operandUnusedMVCCKey: m.txnKeyGenerator, + operandPastTS: m.pastTSGenerator, + operandNextTS: m.nextTSGenerator, + operandValue: m.valueGenerator, + operandIterator: m.iterGenerator, + operandFloat: m.floatGenerator, + operandBool: m.boolGenerator, + operandSavepoint: m.spGenerator, } m.nameToGenerator = make(map[string]*opGenerator) m.weights = make([]int, len(opGenerators)) for i := range opGenerators { m.weights[i] = opGenerators[i].weight + if !m.restarts && opGenerators[i].name == "restart" { + // Don't generate restarts. + m.weights[i] = 0 + } m.nameToGenerator[opGenerators[i].name] = &opGenerators[i] } m.ops = nil @@ -445,7 +412,7 @@ func (m *metaTestRunner) parseFileAndRun(f io.Reader) { if strings.Contains(op.expectedOutput, "error") && strings.Contains(actualOutput, "error") { continue } - m.t.Fatalf("mismatching output at line %d: expected %s, got %s", op.lineNum, op.expectedOutput, actualOutput) + m.t.Fatalf("mismatching output at line %d, operation index %d: expected %s, got %s", op.lineNum, i, op.expectedOutput, actualOutput) } } } diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index 5a502eac8a5e..b3c8c4a88da4 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -66,6 +66,8 @@ func runMetaTestForEngines(run testRunForEngines) { var fs vfs.FS if run.inMem && !*keep { fs = vfs.NewMem() + } else { + fs = vfs.Default } testRunner := metaTestRunner{ @@ -167,13 +169,13 @@ func TestPebbleEquivalence(t *testing.T) { // This test times out with the race detector enabled. skip.UnderRace(t) - engineSeqs := make([]engineSequence, 0, numStandardOptions + numRandomOptions) + engineSeqs := make([]engineSequence, 0, numStandardOptions+numRandomOptions) for i := 0; i < numStandardOptions; i++ { engineSeq := engineSequence{ configs: []engineConfig{{ - name: fmt.Sprintf("standard=%d", i), - opts: standardOptions(i), + name: fmt.Sprintf("standard=%d", i), + opts: standardOptions(i), }}, } engineSeq.name = engineSeq.configs[0].name @@ -183,8 +185,8 @@ func TestPebbleEquivalence(t *testing.T) { for i := 0; i < numRandomOptions; i++ { engineSeq := engineSequence{ configs: []engineConfig{{ - name: fmt.Sprintf("random=%d", i), - opts: randomOptions(i, *seed), + name: fmt.Sprintf("random=%d", i), + opts: randomOptions(i, *seed), }}, } engineSeq.name = engineSeq.configs[0].name @@ -211,19 +213,23 @@ func TestPebbleRestarts(t *testing.T) { // This test times out with the race detector enabled. skip.UnderRace(t) - engineConfigs := make([]engineConfig, 0, numStandardOptions + numRandomOptions) + engineConfigs := make([]engineConfig, 0, numStandardOptions+numRandomOptions-1) // Create one config sequence that contains all options. for i := 0; i < numStandardOptions; i++ { + // Skip standard config at index 9 as it's incompatible with restarts. + if i == 9 { + continue + } engineConfigs = append(engineConfigs, engineConfig{ - name: fmt.Sprintf("standard=%d", i), - opts: standardOptions(i), + name: fmt.Sprintf("standard=%d", i), + opts: standardOptions(i), }) } for i := 0; i < numRandomOptions; i++ { engineConfigs = append(engineConfigs, engineConfig{ - name: fmt.Sprintf("random=%d", i), - opts: randomOptions(i, *seed), + name: fmt.Sprintf("random=%d", i), + opts: randomOptions(i, *seed), }) } @@ -237,7 +243,7 @@ func TestPebbleRestarts(t *testing.T) { engineSequences: []engineSequence{ {name: "standard", configs: []engineConfig{engineConfigStandard}}, { - name: fmt.Sprintf("standards=%d,randoms=%d", numStandardOptions, numRandomOptions), + name: fmt.Sprintf("standards=%d,randoms=%d", numStandardOptions-1, numRandomOptions), configs: engineConfigs, }, }, @@ -258,15 +264,37 @@ func TestPebbleCheck(t *testing.T) { t.Fatal(err) } + engineSeqs := make([]engineSequence, 0, numStandardOptions+numRandomOptions) + + for i := 0; i < numStandardOptions; i++ { + engineSeq := engineSequence{ + configs: []engineConfig{{ + name: fmt.Sprintf("standard=%d", i), + opts: standardOptions(i), + }}, + } + engineSeq.name = engineSeq.configs[0].name + engineSeqs = append(engineSeqs, engineSeq) + } + + for i := 0; i < numRandomOptions; i++ { + engineSeq := engineSequence{ + configs: []engineConfig{{ + name: fmt.Sprintf("random=%d", i), + opts: randomOptions(i, *seed), + }}, + } + engineSeq.name = engineSeq.configs[0].name + engineSeqs = append(engineSeqs, engineSeq) + } + run := testRun{ - ctx: ctx, - t: t, - checkFile: *check, - restarts: true, - inMem: true, - engineSequences: []engineSequence{ - {name: "standard", configs: []engineConfig{engineConfigStandard}}, - }, + ctx: ctx, + t: t, + checkFile: *check, + restarts: true, + inMem: false, + engineSequences: engineSeqs, } runMetaTest(run) } diff --git a/pkg/storage/metamorphic/operands.go b/pkg/storage/metamorphic/operands.go index 26d67f3941d0..fc77b623cb37 100644 --- a/pkg/storage/metamorphic/operands.go +++ b/pkg/storage/metamorphic/operands.go @@ -13,6 +13,7 @@ package metamorphic import ( "fmt" "math/rand" + "sort" "strconv" "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -26,6 +27,7 @@ const ( operandTransaction operandType = iota operandReadWriter operandMVCCKey + operandUnusedMVCCKey operandPastTS operandNextTS operandValue @@ -143,6 +145,59 @@ func (k *keyGenerator) parse(input string) storage.MVCCKey { return key } +// txnKeyGenerator generates keys that are currently unused by other writers +// on the same txn. +// +// Requires: the last two args to be (readWriterID, txnID). +type txnKeyGenerator struct { + txns *txnGenerator + keys *keyGenerator +} + +var _ operandGenerator = &keyGenerator{} + +func (k *txnKeyGenerator) opener() string { + return "" +} + +func (k *txnKeyGenerator) count(args []string) int { + // Always return a nonzero value so opener() is never called directly. + txn := txnID(args[1]) + openKeys := k.txns.inUseKeys[txn] + return len(openKeys) + 1 +} + +func (k *txnKeyGenerator) get(args []string) string { + writer := readWriterID(args[0]) + txn := txnID(args[1]) + + for { + rawKey := k.keys.get(args) + key := k.keys.parse(rawKey) + + conflictFound := false + k.txns.forEachConflict(writer, txn, key.Key, nil, func(span roachpb.Span) bool { + conflictFound = true + return false + }) + if !conflictFound { + return rawKey + } + } +} + +func (k *txnKeyGenerator) getNew(args []string) string { + return k.get(args) +} + +func (k *txnKeyGenerator) closeAll() { + // No-op. +} + +func (k *txnKeyGenerator) parse(input string) storage.MVCCKey { + return k.keys.parse(input) +} + type valueGenerator struct { rng *rand.Rand } @@ -179,14 +234,26 @@ func (v *valueGenerator) parse(input string) []byte { type txnID string +type writtenKeySpan struct { + key roachpb.Span + writer readWriterID +} + type txnGenerator struct { - rng *rand.Rand - testRunner *metaTestRunner - tsGenerator *tsGenerator - liveTxns []txnID - txnIDMap map[txnID]*roachpb.Transaction - openBatches map[txnID]map[readWriterID]struct{} + rng *rand.Rand + testRunner *metaTestRunner + tsGenerator *tsGenerator + liveTxns []txnID + txnIDMap map[txnID]*roachpb.Transaction + // Set of batches with written-to keys for each txn. Does not track writes + // directly to the engine. + openBatches map[txnID]map[readWriterID]struct{} + // Number of open savepoints for each transaction. As savepoints cannot be + // "closed", this count can only go up. openSavepoints map[txnID]int + // Stores keys written to by each transaction during operation generation. + // The slices are sorted in key order. + inUseKeys map[txnID][]writtenKeySpan // Counts "generated" transactions - i.e. how many txn_open()s have been // inserted so far. Could stay 0 in check mode. txnGenCounter uint64 @@ -228,6 +295,7 @@ func (t *txnGenerator) generateClose(id txnID) { delete(t.openBatches, id) delete(t.txnIDMap, id) delete(t.openSavepoints, id) + delete(t.inUseKeys, id) for i := range t.liveTxns { if t.liveTxns[i] == id { @@ -244,7 +312,109 @@ func (t *txnGenerator) clearBatch(batch readWriterID) { } } -func (t *txnGenerator) trackWriteOnBatch(w readWriterID, txn txnID) { +func (t *txnGenerator) forEachConflict( + w readWriterID, txn txnID, key roachpb.Key, endKey roachpb.Key, fn func(roachpb.Span) bool, +) { + if endKey == nil { + endKey = key.Next() + } + + openKeys := t.inUseKeys[txn] + start := sort.Search(len(openKeys), func(i int) bool { + return key.Compare(openKeys[i].key.EndKey) < 0 + }) + end := sort.Search(len(openKeys), func(i int) bool { + return endKey.Compare(openKeys[i].key.Key) <= 0 + }) + + for i := start; i < end; i++ { + if openKeys[i].writer != w { + // Conflict found. + if cont := fn(openKeys[i].key); !cont { + return + } + } + } +} + +func (t *txnGenerator) addWrittenKeySpan( + w readWriterID, txn txnID, key roachpb.Key, endKey roachpb.Key, +) { + span := roachpb.Span{Key: key, EndKey: endKey} + if endKey == nil { + endKey = key.Next() + span.EndKey = endKey + } + // writtenKeys is sorted in key order, and no two spans are overlapping. + // However we do _not_ merge perfectly-adjacent spans when adding; + // as it is legal to have [a,b) and [b,c) belonging to two different writers. + writtenKeys := t.inUseKeys[txn] + // start is the earliest span that either contains key, or if there is no such + // span, is the first span beyond key. end is the earliest span that does not + // include any keys in [key, endKey). + start := sort.Search(len(writtenKeys), func(i int) bool { + return key.Compare(writtenKeys[i].key.EndKey) < 0 + }) + end := sort.Search(len(writtenKeys), func(i int) bool { + return endKey.Compare(writtenKeys[i].key.Key) <= 0 + }) + if start == len(writtenKeys) { + // Append at end. + writtenKeys = append(writtenKeys, writtenKeySpan{ + key: span, + writer: w, + }) + t.inUseKeys[txn] = writtenKeys + return + } + if start == end { + // start == end implies that the span cannot contain start, and by + // definition it is beyond end. So [key, endKey) does not overlap with an + // existing span and needs to be placed before start. + writtenKeys = append(writtenKeys, writtenKeySpan{}) + copy(writtenKeys[start+1:], writtenKeys[start:]) + writtenKeys[start] = writtenKeySpan{ + key: span, + writer: w, + } + t.inUseKeys[txn] = writtenKeys + return + } else if start > end { + panic(fmt.Sprintf("written keys not in sorted order: %d > %d", start, end)) + } + // INVARIANT: start < end. The start span may or may not contain key. And we + // know end > 0. We will be merging existing spans, and need to compute which + // spans to merge and what keys to use for the resulting span. The resulting + // span will go into `span`, and will replace writtenKeys[start:end]. + if writtenKeys[start].key.Key.Compare(key) < 0 { + // The start span contains key. So use the start key of the start span since + // it will result in the wider span. + span.Key = writtenKeys[start].key.Key + } + // Else, span.Key is equal to key which is the wider span. Note that no span + // overlaps with key so we are not extending this into the span at start-1. + // + // The span at end-1 may end at a key less or greater than endKey. + if writtenKeys[end-1].key.EndKey.Compare(endKey) > 0 { + // Existing span ends at a key greater than endKey, so construct the wider + // span. + span.EndKey = writtenKeys[end-1].key.EndKey + } + // We blindly replace the existing spans in writtenKeys[start:end] with the + // new one. This is okay as long as any conflicting operation looks at + // inUseKeys before generating itself; this method is only called once no + // conflicts are found and the write operation is successfully generated. + writtenKeys[start] = writtenKeySpan{ + key: span, + writer: w, + } + n := copy(writtenKeys[start+1:], writtenKeys[end:]) + writtenKeys = writtenKeys[:start+1+n] + t.inUseKeys[txn] = writtenKeys +} + +func (t *txnGenerator) trackTransactionalWrite(w readWriterID, txn txnID, key, endKey roachpb.Key) { + t.addWrittenKeySpan(w, txn, key, endKey) if w == "engine" { return } @@ -260,17 +430,20 @@ func (t *txnGenerator) closeAll() { t.liveTxns = nil t.txnIDMap = make(map[txnID]*roachpb.Transaction) t.openBatches = make(map[txnID]map[readWriterID]struct{}) + t.inUseKeys = make(map[txnID][]writtenKeySpan) t.openSavepoints = make(map[txnID]int) } type savepointGenerator struct { - rng *rand.Rand - txnGenerator *txnGenerator + rng *rand.Rand + txnGenerator *txnGenerator } var _ operandGenerator = &savepointGenerator{} func (s *savepointGenerator) get(args []string) string { + // Since get is being called as opposed to getNew, there must be a nonzero + // value at s.txnGenerator.openSavepoints[id]. id := txnID(args[len(args)-1]) n := s.rng.Intn(s.txnGenerator.openSavepoints[id]) return strconv.Itoa(n) @@ -419,11 +592,10 @@ func (w *readWriterGenerator) closeAll() { type iteratorID string type iteratorInfo struct { - id iteratorID - iter storage.MVCCIterator - lowerBound roachpb.Key - isBatchIter bool - isEngineIter bool + id iteratorID + iter storage.MVCCIterator + lowerBound roachpb.Key + isBatchIter bool } type iteratorGenerator struct { @@ -539,12 +711,12 @@ type boolGenerator struct { rng *rand.Rand } -func (f *boolGenerator) get() string { +func (f *boolGenerator) get(args []string) string { return fmt.Sprintf("%t", f.rng.Float32() < 0.5) } -func (f *boolGenerator) getNew() string { - return f.get() +func (f *boolGenerator) getNew(args []string) string { + return f.get(args) } func (f *boolGenerator) parse(input string) bool { @@ -560,7 +732,7 @@ func (f *boolGenerator) opener() string { return "" } -func (f *boolGenerator) count() int { +func (f *boolGenerator) count(args []string) int { return 1 } diff --git a/pkg/storage/metamorphic/operations.go b/pkg/storage/metamorphic/operations.go index 6575220f6bc2..6b047945bd88 100644 --- a/pkg/storage/metamorphic/operations.go +++ b/pkg/storage/metamorphic/operations.go @@ -14,10 +14,10 @@ import ( "context" "fmt" "math" - "os" "path/filepath" "sort" "strconv" + "strings" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -153,6 +153,16 @@ func printIterState(iter storage.MVCCIterator) string { return fmt.Sprintf("key = %s", iter.UnsafeKey().String()) } +func addKeyToLockSpans(txn *roachpb.Transaction, key roachpb.Key) { + // Update the txn's lock spans to account for this intent being written. + newLockSpans := make([]roachpb.Span, 0, len(txn.LockSpans)+1) + newLockSpans = append(newLockSpans, txn.LockSpans...) + newLockSpans = append(newLockSpans, roachpb.Span{ + Key: key, + }) + txn.LockSpans, _ = roachpb.MergeSpans(&newLockSpans) +} + type mvccGetOp struct { m *metaTestRunner reader readWriterID @@ -201,10 +211,7 @@ func (m mvccPutOp) run(ctx context.Context) string { return fmt.Sprintf("error: %s", err) } - // Update the txn's lock spans to account for this intent being written. - txn.LockSpans = append(txn.LockSpans, roachpb.Span{ - Key: m.key, - }) + addKeyToLockSpans(txn, m.key) return "ok" } @@ -227,10 +234,7 @@ func (m mvccCPutOp) run(ctx context.Context) string { return fmt.Sprintf("error: %s", err) } - // Update the txn's lock spans to account for this intent being written. - txn.LockSpans = append(txn.LockSpans, roachpb.Span{ - Key: m.key, - }) + addKeyToLockSpans(txn, m.key) return "ok" } @@ -252,10 +256,7 @@ func (m mvccInitPutOp) run(ctx context.Context) string { return fmt.Sprintf("error: %s", err) } - // Update the txn's lock spans to account for this intent being written. - txn.LockSpans = append(txn.LockSpans, roachpb.Span{ - Key: m.key, - }) + addKeyToLockSpans(txn, m.key) return "ok" } @@ -279,11 +280,17 @@ func (m mvccDeleteRangeOp) run(ctx context.Context) string { // Update the txn's lock spans to account for this intent being written. for _, key := range keys { - txn.LockSpans = append(txn.LockSpans, roachpb.Span{ - Key: key, - }) + addKeyToLockSpans(txn, key) } - return "ok" + var builder strings.Builder + fmt.Fprintf(&builder, "truncated range to delete = %s - %s, deleted keys = ", m.key, m.endKey) + for i, key := range keys { + fmt.Fprintf(&builder, "%s", key) + if i < len(keys)-1 { + fmt.Fprintf(&builder, ", ") + } + } + return builder.String() } type mvccClearTimeRangeOp struct { @@ -324,9 +331,7 @@ func (m mvccDeleteOp) run(ctx context.Context) string { } // Update the txn's lock spans to account for this intent being written. - txn.LockSpans = append(txn.LockSpans, roachpb.Span{ - Key: m.key, - }) + addKeyToLockSpans(txn, m.key) return "ok" } @@ -462,8 +467,11 @@ func (t txnAbortOp) run(ctx context.Context) string { } type txnCreateSavepointOp struct { - m *metaTestRunner - id txnID + m *metaTestRunner + id txnID + // The index of the savepoint (in m.openSavepoints[id]) being created. + // As savepoints are only appended to the end, this must equal + // len(m.openSavepoints[t.id]) at time of running. savepoint int } @@ -482,8 +490,10 @@ func (t txnCreateSavepointOp) run(ctx context.Context) string { } type txnRollbackSavepointOp struct { - m *metaTestRunner - id txnID + m *metaTestRunner + id txnID + // The index of the savepoint (in m.openSavepoints[id]) being rolled back to. + // Txn sequences are generated and stored at runtime in that slice. savepoint int } @@ -696,11 +706,10 @@ type ingestOp struct { func (i ingestOp) run(ctx context.Context) string { sstPath := filepath.Join(i.m.path, "ingest.sst") - f, err := os.Create(sstPath) + f, err := i.m.engineFS.Create(sstPath) if err != nil { return fmt.Sprintf("error = %s", err.Error()) } - defer f.Close() sstWriter := storage.MakeIngestionSSTWriter(f) for _, key := range i.keys { @@ -740,7 +749,6 @@ func (r restartOp) run(ctx context.Context) string { // - MVCCBlindPut // - MVCCMerge // - MVCCIncrement -// - MVCCResolveWriteIntent in the aborted case // - and any others that would be important to test. var opGenerators = []opGenerator{ { @@ -788,13 +796,13 @@ var opGenerators = []opGenerator{ name: "mvcc_put", generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { writer := readWriterID(args[0]) - key := m.keyGenerator.parse(args[1]) - value := roachpb.MakeValueFromBytes(m.valueGenerator.parse(args[2])) - txn := txnID(args[3]) + txn := txnID(args[1]) + key := m.txnKeyGenerator.parse(args[2]) + value := roachpb.MakeValueFromBytes(m.valueGenerator.parse(args[3])) // Track this write in the txn generator. This ensures the batch will be // committed before the transaction is committed - m.txnGenerator.trackWriteOnBatch(writer, txn) + m.txnGenerator.trackTransactionalWrite(writer, txn, key.Key, nil) return &mvccPutOp{ m: m, writer: writer, @@ -805,9 +813,9 @@ var opGenerators = []opGenerator{ }, operands: []operandType{ operandReadWriter, - operandMVCCKey, - operandValue, operandTransaction, + operandUnusedMVCCKey, + operandValue, }, weight: 500, }, @@ -815,14 +823,14 @@ var opGenerators = []opGenerator{ name: "mvcc_conditional_put", generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { writer := readWriterID(args[0]) - key := m.keyGenerator.parse(args[1]) - value := roachpb.MakeValueFromBytes(m.valueGenerator.parse(args[2])) - expVal := m.valueGenerator.parse(args[3]) - txn := txnID(args[4]) + txn := txnID(args[1]) + key := m.txnKeyGenerator.parse(args[2]) + value := roachpb.MakeValueFromBytes(m.valueGenerator.parse(args[3])) + expVal := m.valueGenerator.parse(args[4]) // Track this write in the txn generator. This ensures the batch will be // committed before the transaction is committed - m.txnGenerator.trackWriteOnBatch(writer, txn) + m.txnGenerator.trackTransactionalWrite(writer, txn, key.Key, nil) return &mvccCPutOp{ m: m, writer: writer, @@ -834,10 +842,10 @@ var opGenerators = []opGenerator{ }, operands: []operandType{ operandReadWriter, - operandMVCCKey, + operandTransaction, + operandUnusedMVCCKey, operandValue, operandValue, - operandTransaction, }, weight: 50, }, @@ -845,13 +853,13 @@ var opGenerators = []opGenerator{ name: "mvcc_init_put", generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { writer := readWriterID(args[0]) - key := m.keyGenerator.parse(args[1]) - value := roachpb.MakeValueFromBytes(m.valueGenerator.parse(args[2])) - txn := txnID(args[3]) + txn := txnID(args[1]) + key := m.txnKeyGenerator.parse(args[2]) + value := roachpb.MakeValueFromBytes(m.valueGenerator.parse(args[3])) // Track this write in the txn generator. This ensures the batch will be // committed before the transaction is committed - m.txnGenerator.trackWriteOnBatch(writer, txn) + m.txnGenerator.trackTransactionalWrite(writer, txn, key.Key, nil) return &mvccInitPutOp{ m: m, writer: writer, @@ -862,9 +870,9 @@ var opGenerators = []opGenerator{ }, operands: []operandType{ operandReadWriter, - operandMVCCKey, - operandValue, operandTransaction, + operandUnusedMVCCKey, + operandValue, }, weight: 50, }, @@ -872,17 +880,22 @@ var opGenerators = []opGenerator{ name: "mvcc_delete_range", generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { writer := readWriterID(args[0]) - key := m.keyGenerator.parse(args[1]).Key - endKey := m.keyGenerator.parse(args[2]).Key - txn := txnID(args[3]) + txn := txnID(args[1]) + key := m.keyGenerator.parse(args[2]).Key + endKey := m.keyGenerator.parse(args[3]).Key if endKey.Compare(key) < 0 { key, endKey = endKey, key } + // forEachConflict is guaranteed to iterate + m.txnGenerator.forEachConflict(writer, txn, key, endKey, func(conflict roachpb.Span) bool { + endKey = conflict.Key + return false + }) // Track this write in the txn generator. This ensures the batch will be // committed before the transaction is committed - m.txnGenerator.trackWriteOnBatch(writer, txn) + m.txnGenerator.trackTransactionalWrite(writer, txn, key, endKey) return &mvccDeleteRangeOp{ m: m, writer: writer, @@ -896,9 +909,9 @@ var opGenerators = []opGenerator{ }, operands: []operandType{ operandReadWriter, - operandMVCCKey, - operandMVCCKey, operandTransaction, + operandUnusedMVCCKey, + operandUnusedMVCCKey, }, weight: 20, }, @@ -942,12 +955,12 @@ var opGenerators = []opGenerator{ name: "mvcc_delete", generate: func(ctx context.Context, m *metaTestRunner, args ...string) mvccOp { writer := readWriterID(args[0]) - key := m.keyGenerator.parse(args[1]) - txn := txnID(args[2]) + txn := txnID(args[1]) + key := m.txnKeyGenerator.parse(args[2]) // Track this write in the txn generator. This ensures the batch will be // committed before the transaction is committed - m.txnGenerator.trackWriteOnBatch(writer, txn) + m.txnGenerator.trackTransactionalWrite(writer, txn, key.Key, nil) return &mvccDeleteOp{ m: m, writer: writer, @@ -957,8 +970,8 @@ var opGenerators = []opGenerator{ }, operands: []operandType{ operandReadWriter, - operandMVCCKey, operandTransaction, + operandUnusedMVCCKey, }, weight: 100, }, @@ -1118,7 +1131,7 @@ var opGenerators = []opGenerator{ operandSavepoint, }, isOpener: true, - weight: 10, + weight: 10, }, { name: "txn_rollback_savepoint", From 6713d6288d7b8b9ccfe417b6a0f3a008ef5da364 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Mon, 25 Oct 2021 14:02:32 -0700 Subject: [PATCH 055/205] storage/metamorphic: use global seed A recent change was to move all rng-using tests over to using COCKROACH_RANDOM_SEED for a consistent way to make tests deterministically reproducible. This commit updates the mvcc metamorphic tests to also adhere to the same convention. Release note: None. --- pkg/storage/metamorphic/BUILD.bazel | 1 + pkg/storage/metamorphic/meta_test.go | 13 +++++++------ pkg/storage/metamorphic/options.go | 7 +++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pkg/storage/metamorphic/BUILD.bazel b/pkg/storage/metamorphic/BUILD.bazel index 96badf5dc180..39561d5b6942 100644 --- a/pkg/storage/metamorphic/BUILD.bazel +++ b/pkg/storage/metamorphic/BUILD.bazel @@ -19,6 +19,7 @@ go_library( "//pkg/storage", "//pkg/storage/enginepb", "//pkg/util/hlc", + "//pkg/util/randutil", "//pkg/util/syncutil", "//pkg/util/uint128", "//pkg/util/uuid", diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index b3c8c4a88da4..cc1fdbf339d3 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -31,7 +31,6 @@ import ( var ( keep = flag.Bool("keep", false, "keep temp directories after test") check = flag.String("check", "", "run operations in specified file and check output for equality") - seed = flag.Int64("seed", randutil.NewPseudoSeed(), "specify seed to use for random number generator") opCount = flag.Int("operations", 100000, "number of MVCC operations to generate and run") ) @@ -168,6 +167,7 @@ func TestPebbleEquivalence(t *testing.T) { ctx := context.Background() // This test times out with the race detector enabled. skip.UnderRace(t) + _, seed := randutil.NewTestRand() engineSeqs := make([]engineSequence, 0, numStandardOptions+numRandomOptions) @@ -186,7 +186,7 @@ func TestPebbleEquivalence(t *testing.T) { engineSeq := engineSequence{ configs: []engineConfig{{ name: fmt.Sprintf("random=%d", i), - opts: randomOptions(i, *seed), + opts: randomOptions(), }}, } engineSeq.name = engineSeq.configs[0].name @@ -196,7 +196,7 @@ func TestPebbleEquivalence(t *testing.T) { run := testRun{ ctx: ctx, t: t, - seed: *seed, + seed: seed, restarts: false, inMem: true, engineSequences: engineSeqs, @@ -212,6 +212,7 @@ func TestPebbleRestarts(t *testing.T) { defer log.Scope(t).Close(t) // This test times out with the race detector enabled. skip.UnderRace(t) + _, seed := randutil.NewTestRand() engineConfigs := make([]engineConfig, 0, numStandardOptions+numRandomOptions-1) // Create one config sequence that contains all options. @@ -229,7 +230,7 @@ func TestPebbleRestarts(t *testing.T) { for i := 0; i < numRandomOptions; i++ { engineConfigs = append(engineConfigs, engineConfig{ name: fmt.Sprintf("random=%d", i), - opts: randomOptions(i, *seed), + opts: randomOptions(), }) } @@ -237,7 +238,7 @@ func TestPebbleRestarts(t *testing.T) { run := testRun{ ctx: ctx, t: t, - seed: *seed, + seed: seed, inMem: true, restarts: true, engineSequences: []engineSequence{ @@ -281,7 +282,7 @@ func TestPebbleCheck(t *testing.T) { engineSeq := engineSequence{ configs: []engineConfig{{ name: fmt.Sprintf("random=%d", i), - opts: randomOptions(i, *seed), + opts: randomOptions(), }}, } engineSeq.name = engineSeq.configs[0].name diff --git a/pkg/storage/metamorphic/options.go b/pkg/storage/metamorphic/options.go index f3763485f7f8..672d7b298171 100644 --- a/pkg/storage/metamorphic/options.go +++ b/pkg/storage/metamorphic/options.go @@ -11,9 +11,8 @@ package metamorphic import ( - "math/rand" - "github.com/cockroachdb/cockroach/pkg/storage" + "github.com/cockroachdb/cockroach/pkg/util/randutil" "github.com/cockroachdb/pebble" ) @@ -104,10 +103,10 @@ func standardOptions(i int) *pebble.Options { return opts } -func randomOptions(offset int, seed int64) *pebble.Options { +func randomOptions() *pebble.Options { opts := storage.DefaultPebbleOptions() - rng := rand.New(rand.NewSource(seed + int64(offset))) + rng, _ := randutil.NewTestRand() opts.BytesPerSync = 1 << rngIntRange(rng, 8, 30) opts.FlushSplitBytes = 1 << rng.Intn(20) opts.LBaseMaxBytes = 1 << rngIntRange(rng, 8, 30) From 776643f565f45443502e813ac562928e074f461e Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 27 Oct 2021 07:58:18 +1100 Subject: [PATCH 056/205] pgwire: handle oid.T_unknown Release note (sql change): T_unknown ParamaterTypeOIDs in the PostgreSQL Frontend/Backend protocol are now correctly handled. --- pkg/sql/conn_executor_prepare.go | 2 +- pkg/sql/pgwire/conn.go | 6 +-- pkg/sql/pgwire/pgwirebase/encoding.go | 2 +- pkg/sql/pgwire/testdata/pgtest/unknown | 65 ++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 pkg/sql/pgwire/testdata/pgtest/unknown diff --git a/pkg/sql/conn_executor_prepare.go b/pkg/sql/conn_executor_prepare.go index 9abcecba59a7..4af2c167c8d1 100644 --- a/pkg/sql/conn_executor_prepare.go +++ b/pkg/sql/conn_executor_prepare.go @@ -76,7 +76,7 @@ func (ex *connExecutor) execPrepare( // OID to Datum is not a 1-1 mapping (for example, int4 and int8 // both map to TypeInt), so we need to maintain the types sent by // the client. - if inferredTypes[i] == 0 { + if inferredTypes[i] == 0 || inferredTypes[i] == oid.T_unknown { t, _ := ps.ValueType(tree.PlaceholderIdx(i)) inferredTypes[i] = t.Oid() } diff --git a/pkg/sql/pgwire/conn.go b/pkg/sql/pgwire/conn.go index 7ea89d543978..51161ae744ee 100644 --- a/pkg/sql/pgwire/conn.go +++ b/pkg/sql/pgwire/conn.go @@ -856,9 +856,9 @@ func (c *conn) handleParse( if t == 0 { continue } - // If the OID is user defined, then write nil into the type hints and let - // the consumer of the PrepareStmt resolve the types. - if types.IsOIDUserDefinedType(t) { + // If the OID is user defined or unknown, then write nil into the type + // hints and let the consumer of the PrepareStmt resolve the types. + if t == oid.T_unknown || types.IsOIDUserDefinedType(t) { sqlTypeHints[i] = nil continue } diff --git a/pkg/sql/pgwire/pgwirebase/encoding.go b/pkg/sql/pgwire/pgwirebase/encoding.go index 9ff011844a76..03613a70ae76 100644 --- a/pkg/sql/pgwire/pgwirebase/encoding.go +++ b/pkg/sql/pgwire/pgwirebase/encoding.go @@ -783,7 +783,7 @@ func DecodeDatum( return tree.MakeDEnumFromLogicalRepresentation(t, string(b)) } switch id { - case oid.T_text, oid.T_varchar: + case oid.T_text, oid.T_varchar, oid.T_unknown: if err := validateStringBytes(b); err != nil { return nil, err } diff --git a/pkg/sql/pgwire/testdata/pgtest/unknown b/pkg/sql/pgwire/testdata/pgtest/unknown new file mode 100644 index 000000000000..39726a381f46 --- /dev/null +++ b/pkg/sql/pgwire/testdata/pgtest/unknown @@ -0,0 +1,65 @@ +send +Query {"String": "DROP TABLE IF EXISTS unknown_test"} +---- + +until ignore=NoticeResponse +ReadyForQuery +---- +{"Type":"CommandComplete","CommandTag":"DROP TABLE"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "CREATE TABLE unknown_test (i INT8, t TEXT, f FLOAT8)"} +---- + +until +ReadyForQuery +---- +{"Type":"CommandComplete","CommandTag":"CREATE TABLE"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +# 'S' for Statement +# ParameterFormatCodes = [0] for text format, [1] for binary format +send +Parse {"Name": "s1", "Query": "INSERT INTO unknown_test VALUES($1, $2, $3)", "ParameterOIDs":[705, 705, 705]} +Describe {"ObjectType": "S", "Name": "s1"} +Bind {"DestinationPortal": "p1", "PreparedStatement": "s1", "ParameterFormatCodes": [0,0,0], "Parameters": [{"text":"1"}, {"text":"one"}, {"text":"1.5"}]} +Execute {"Portal": "p1"} +Sync +---- + +until +ReadyForQuery +---- +{"Type":"ParseComplete"} +{"Type":"ParameterDescription","ParameterOIDs":[20,25,701]} +{"Type":"NoData"} +{"Type":"BindComplete"} +{"Type":"CommandComplete","CommandTag":"INSERT 0 1"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +send +Bind {"DestinationPortal": "p1", "PreparedStatement": "s1", "ParameterFormatCodes": [1,1,1], "Parameters": [{"binary":"0000000000000004"}, {"binary":"46"}, {"binary": "bff3333333333333"}]} +Execute {"Portal": "p1"} +Sync +---- + +until +ReadyForQuery +---- +{"Type":"BindComplete"} +{"Type":"CommandComplete","CommandTag":"INSERT 0 1"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "SELECT * FROM unknown_test"} +---- + +until ignore_table_oids +ReadyForQuery +---- +{"Type":"RowDescription","Fields":[{"Name":"i","TableOID":0,"TableAttributeNumber":1,"DataTypeOID":20,"DataTypeSize":8,"TypeModifier":-1,"Format":0},{"Name":"t","TableOID":0,"TableAttributeNumber":2,"DataTypeOID":25,"DataTypeSize":-1,"TypeModifier":-1,"Format":0},{"Name":"f","TableOID":0,"TableAttributeNumber":3,"DataTypeOID":701,"DataTypeSize":8,"TypeModifier":-1,"Format":0}]} +{"Type":"DataRow","Values":[{"text":"1"},{"text":"one"},{"text":"1.5"}]} +{"Type":"DataRow","Values":[{"text":"4"},{"text":"F"},{"text":"-1.2"}]} +{"Type":"CommandComplete","CommandTag":"SELECT 2"} +{"Type":"ReadyForQuery","TxStatus":"I"} From bba05aabcbba78dbec0a309f0b96440cb8b71d26 Mon Sep 17 00:00:00 2001 From: Andrew Werner Date: Mon, 25 Oct 2021 12:31:43 -0400 Subject: [PATCH 057/205] sql/catalog/descs: optimize access to the system db MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We tend to access the system database a lot and we never cache it in the lease manager. Before this patch, we'd always copy and re-allocate a pair of them. There's no need to do that. ``` name old time/op new time/op delta FlowSetup/vectorize=true/distribute=true-16 141µs ± 3% 132µs ± 4% -6.35% (p=0.000 n=19+18) FlowSetup/vectorize=true/distribute=false-16 138µs ± 4% 129µs ± 3% -6.80% (p=0.000 n=19+18) FlowSetup/vectorize=false/distribute=true-16 134µs ± 2% 124µs ± 4% -7.55% (p=0.000 n=20+17) FlowSetup/vectorize=false/distribute=false-16 129µs ± 3% 120µs ± 3% -6.98% (p=0.000 n=20+18) name old alloc/op new alloc/op delta FlowSetup/vectorize=true/distribute=true-16 38.1kB ± 2% 36.8kB ± 3% -3.53% (p=0.000 n=18+19) FlowSetup/vectorize=true/distribute=false-16 36.2kB ± 0% 34.8kB ± 0% -3.93% (p=0.000 n=17+17) FlowSetup/vectorize=false/distribute=true-16 42.6kB ± 0% 41.2kB ± 0% -3.30% (p=0.000 n=18+16) FlowSetup/vectorize=false/distribute=false-16 41.0kB ± 0% 39.6kB ± 0% -3.44% (p=0.000 n=16+18) name old allocs/op new allocs/op delta FlowSetup/vectorize=true/distribute=true-16 368 ± 0% 345 ± 0% -6.25% (p=0.000 n=16+16) FlowSetup/vectorize=true/distribute=false-16 354 ± 0% 331 ± 0% -6.50% (p=0.000 n=18+18) FlowSetup/vectorize=false/distribute=true-16 337 ± 0% 315 ± 1% -6.69% (p=0.000 n=19+19) FlowSetup/vectorize=false/distribute=false-16 325 ± 0% 302 ± 0% -7.08% (p=0.000 n=17+18) ``` Release note: None --- .../catalog/descs/uncommitted_descriptors.go | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/pkg/sql/catalog/descs/uncommitted_descriptors.go b/pkg/sql/catalog/descs/uncommitted_descriptors.go index f3712dbe5c97..89d9317a414b 100644 --- a/pkg/sql/catalog/descs/uncommitted_descriptors.go +++ b/pkg/sql/catalog/descs/uncommitted_descriptors.go @@ -12,9 +12,12 @@ package descs import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/dbdesc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/lease" "github.com/cockroachdb/cockroach/pkg/sql/catalog/nstree" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/systemschema" "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" "github.com/cockroachdb/cockroach/pkg/util/iterutil" "github.com/cockroachdb/errors" @@ -23,8 +26,14 @@ import ( // uncommittedDescriptor is a descriptor that has been modified in the current // transaction. type uncommittedDescriptor struct { - mutable catalog.MutableDescriptor immutable catalog.Descriptor + + // mutable generally holds the descriptor as it was read from the database. + // In the rare case that this struct corresponds to a singleton which is + // added to optimize for special cases of system descriptors. + // It should be accessed through getMutable() which will construct a new + // value in cases where it is nil. + mutable catalog.MutableDescriptor } // GetName implements the catalog.NameEntry interface. @@ -47,6 +56,16 @@ func (u uncommittedDescriptor) GetID() descpb.ID { return u.immutable.GetID() } +// getMutable is how the mutable descriptor should be accessed. It constructs +// a new descriptor in the case that this descriptor is a cached, in-memory +// singleton for a system descriptor. +func (u *uncommittedDescriptor) getMutable() catalog.MutableDescriptor { + if u.mutable != nil { + return u.mutable + } + return catalogkv.NewBuilder(u.immutable.DescriptorProto()).BuildExistingMutable() +} + var _ catalog.NameEntry = (*uncommittedDescriptor)(nil) // uncommittedDescriptors is the data structure holding all @@ -113,6 +132,7 @@ func (ud *uncommittedDescriptors) add(mut catalog.MutableDescriptor) (catalog.De // checkOut checks out an uncommitted mutable descriptor for use in the // transaction. This descriptor should later be checked in again. func (ud *uncommittedDescriptors) checkOut(id descpb.ID) (catalog.MutableDescriptor, error) { + ud.maybeInitialize() entry := ud.descs.GetByID(id) if entry == nil { return nil, errors.NewAssertionErrorWithWrappedErrf( @@ -122,7 +142,7 @@ func (ud *uncommittedDescriptors) checkOut(id descpb.ID) (catalog.MutableDescrip } u := entry.(*uncommittedDescriptor) - return u.mutable, nil + return u.getMutable(), nil } // checkIn checks in an uncommitted mutable descriptor that was previously @@ -170,6 +190,7 @@ func maybeRefreshCachedFieldsOnTypeDescriptor( // getByID looks up an uncommitted descriptor by ID. func (ud *uncommittedDescriptors) getByID(id descpb.ID) catalog.Descriptor { + ud.maybeInitialize() entry := ud.descs.GetByID(id) if entry == nil { return nil @@ -187,6 +208,7 @@ func (ud *uncommittedDescriptors) getByID(id descpb.ID) catalog.Descriptor { func (ud *uncommittedDescriptors) getByName( dbID descpb.ID, schemaID descpb.ID, name string, ) (hasKnownRename bool, desc catalog.Descriptor) { + ud.maybeInitialize() // Walk latest to earliest so that a DROP followed by a CREATE with the same // name will result in the CREATE being seen. if got := ud.descs.GetByName(dbID, schemaID, name); got != nil { @@ -202,9 +224,10 @@ func (ud *uncommittedDescriptors) getByName( func (ud *uncommittedDescriptors) iterateNewVersionByID( fn func(entry catalog.NameEntry, originalVersion lease.IDVersion) error, ) error { + ud.maybeInitialize() return ud.descs.IterateByID(func(entry catalog.NameEntry) error { mut := entry.(*uncommittedDescriptor).mutable - if mut.IsNew() || !mut.IsUncommittedVersion() { + if mut == nil || mut.IsNew() || !mut.IsUncommittedVersion() { return nil } return fn(entry, lease.NewIDVersionPrev(mut.OriginalName(), mut.OriginalID(), mut.OriginalVersion())) @@ -214,6 +237,7 @@ func (ud *uncommittedDescriptors) iterateNewVersionByID( func (ud *uncommittedDescriptors) iterateImmutableByID( fn func(imm catalog.Descriptor) error, ) error { + ud.maybeInitialize() return ud.descs.IterateByID(func(entry catalog.NameEntry) error { return fn(entry.(*uncommittedDescriptor).immutable) }) @@ -262,3 +286,16 @@ func (ud *uncommittedDescriptors) hasUncommittedTypes() (has bool) { }) return has } + +var systemUncommittedDatabase = &uncommittedDescriptor{ + immutable: dbdesc.NewBuilder(systemschema.SystemDB.DatabaseDesc()). + BuildImmutableDatabase(), + // Note that the mutable field is left as nil. We'll generate a new + // value lazily when this is needed, which ought to be exceedingly rare. +} + +func (ud *uncommittedDescriptors) maybeInitialize() { + if ud.descs.Len() == 0 { + ud.descs.Upsert(systemUncommittedDatabase) + } +} From 61f736ae88f84bb53003d5c63f82857d9c8e551e Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Tue, 12 Oct 2021 13:59:05 -0700 Subject: [PATCH 058/205] colbuilder: reuse the SemaCtx Also inline a single function in the flow diagram. Release note: None --- pkg/sql/colexec/colbuilder/execplan.go | 17 +++++++---------- pkg/sql/colexec/colexecargs/expr.go | 12 +++++------- pkg/sql/colfetcher/BUILD.bazel | 1 + pkg/sql/colfetcher/index_join.go | 5 +++-- pkg/sql/colflow/vectorized_flow.go | 6 ++++++ pkg/sql/execinfrapb/flow_diagram.go | 14 ++++---------- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/pkg/sql/colexec/colbuilder/execplan.go b/pkg/sql/colexec/colbuilder/execplan.go index 040c6ec43b65..9fd4d25de80b 100644 --- a/pkg/sql/colexec/colbuilder/execplan.go +++ b/pkg/sql/colexec/colbuilder/execplan.go @@ -698,6 +698,7 @@ func NewColOperator( useStreamingMemAccountForBuffering := args.TestingKnobs.UseStreamingMemAccountForBuffering if args.ExprHelper == nil { args.ExprHelper = colexecargs.NewExprHelper() + args.ExprHelper.SemaCtx = flowCtx.TypeResolverFactory.NewSemaContext(evalCtx.Txn) } core := &spec.Core @@ -783,12 +784,12 @@ func NewColOperator( kvFetcherMemAcc := result.createUnlimitedMemAccount( ctx, flowCtx, "kvfetcher" /* opName */, spec.ProcessorID, ) - semaCtx := flowCtx.TypeResolverFactory.NewSemaContext(evalCtx.Txn) inputTypes := make([]*types.T, len(spec.Input[0].ColumnTypes)) copy(inputTypes, spec.Input[0].ColumnTypes) indexJoinOp, err := colfetcher.NewColIndexJoin( ctx, streamingAllocator, colmem.NewAllocator(ctx, cFetcherMemAcc, factory), kvFetcherMemAcc, - flowCtx, evalCtx, semaCtx, inputs[0].Root, core.JoinReader, post, inputTypes) + flowCtx, evalCtx, args.ExprHelper, inputs[0].Root, core.JoinReader, post, inputTypes, + ) if err != nil { return r, err } @@ -849,9 +850,8 @@ func NewColOperator( Spec: aggSpec, EvalCtx: evalCtx, } - semaCtx := flowCtx.TypeResolverFactory.NewSemaContext(evalCtx.Txn) newAggArgs.Constructors, newAggArgs.ConstArguments, newAggArgs.OutputTypes, err = colexecagg.ProcessAggregations( - evalCtx, semaCtx, aggSpec.Aggregations, inputTypes, + evalCtx, args.ExprHelper.SemaCtx, aggSpec.Aggregations, inputTypes, ) if err != nil { return r, err @@ -1392,9 +1392,8 @@ func NewColOperator( Func: aggType, ColIdx: colIdx, }} - semaCtx := flowCtx.TypeResolverFactory.NewSemaContext(evalCtx.Txn) aggArgs.Constructors, aggArgs.ConstArguments, aggArgs.OutputTypes, err = - colexecagg.ProcessAggregations(evalCtx, semaCtx, aggregations, argTypes) + colexecagg.ProcessAggregations(evalCtx, args.ExprHelper.SemaCtx, aggregations, argTypes) var toClose colexecop.Closers var aggFnsAlloc *colexecagg.AggregateFuncsAlloc if (aggType != execinfrapb.Min && aggType != execinfrapb.Max) || @@ -1629,10 +1628,9 @@ func (r *postProcessResult) planPostProcessSpec( if post.Projection { r.Op, r.ColumnTypes = addProjection(r.Op, r.ColumnTypes, post.OutputColumns) } else if post.RenderExprs != nil { - semaCtx := flowCtx.TypeResolverFactory.NewSemaContext(evalCtx.Txn) var renderedCols []uint32 for _, renderExpr := range post.RenderExprs { - expr, err := args.ExprHelper.ProcessExpr(renderExpr, semaCtx, evalCtx, r.ColumnTypes) + expr, err := args.ExprHelper.ProcessExpr(renderExpr, evalCtx, r.ColumnTypes) if err != nil { return err } @@ -1809,8 +1807,7 @@ func planFilterExpr( helper *colexecargs.ExprHelper, releasables *[]execinfra.Releasable, ) (colexecop.Operator, error) { - semaCtx := flowCtx.TypeResolverFactory.NewSemaContext(evalCtx.Txn) - expr, err := helper.ProcessExpr(filter, semaCtx, evalCtx, columnTypes) + expr, err := helper.ProcessExpr(filter, evalCtx, columnTypes) if err != nil { return nil, err } diff --git a/pkg/sql/colexec/colexecargs/expr.go b/pkg/sql/colexec/colexecargs/expr.go index 01b06abb7ff3..c17cd905c577 100644 --- a/pkg/sql/colexec/colexecargs/expr.go +++ b/pkg/sql/colexec/colexecargs/expr.go @@ -33,23 +33,21 @@ func NewExprHelper() *ExprHelper { // ExprHelper is a utility struct that helps with expression handling in the // vectorized engine. type ExprHelper struct { - helper execinfrapb.ExprHelper + helper execinfrapb.ExprHelper + SemaCtx *tree.SemaContext } // ProcessExpr processes the given expression and returns a well-typed -// expression. +// expression. Note that SemaCtx must be already set on h. func (h *ExprHelper) ProcessExpr( - expr execinfrapb.Expression, - semaCtx *tree.SemaContext, - evalCtx *tree.EvalContext, - typs []*types.T, + expr execinfrapb.Expression, evalCtx *tree.EvalContext, typs []*types.T, ) (tree.TypedExpr, error) { if expr.LocalExpr != nil { return expr.LocalExpr, nil } h.helper.Types = typs tempVars := tree.MakeIndexedVarHelper(&h.helper, len(typs)) - return execinfrapb.DeserializeExpr(expr.Expr, semaCtx, evalCtx, &tempVars) + return execinfrapb.DeserializeExpr(expr.Expr, h.SemaCtx, evalCtx, &tempVars) } // Remove unused warning. diff --git a/pkg/sql/colfetcher/BUILD.bazel b/pkg/sql/colfetcher/BUILD.bazel index 3781415676ab..d66e76710756 100644 --- a/pkg/sql/colfetcher/BUILD.bazel +++ b/pkg/sql/colfetcher/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "//pkg/sql/catalog/tabledesc", "//pkg/sql/colconv", "//pkg/sql/colencoding", + "//pkg/sql/colexec/colexecargs", "//pkg/sql/colexec/colexecspan", "//pkg/sql/colexecerror", "//pkg/sql/colexecop", diff --git a/pkg/sql/colfetcher/index_join.go b/pkg/sql/colfetcher/index_join.go index 9ff61f15409d..f4a0c951b226 100644 --- a/pkg/sql/colfetcher/index_join.go +++ b/pkg/sql/colfetcher/index_join.go @@ -18,6 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/col/coldata" "github.com/cockroachdb/cockroach/pkg/col/typeconv" "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecspan" "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/colexecop" @@ -388,7 +389,7 @@ func NewColIndexJoin( kvFetcherMemAcc *mon.BoundAccount, flowCtx *execinfra.FlowCtx, evalCtx *tree.EvalContext, - semaCtx *tree.SemaContext, + helper *colexecargs.ExprHelper, input colexecop.Operator, spec *execinfrapb.JoinReaderSpec, post *execinfrapb.PostProcessSpec, @@ -429,7 +430,7 @@ func NewColIndexJoin( } } else { proc := &execinfra.ProcOutputHelper{} - if err = proc.Init(post, tableArgs.typs, semaCtx, evalCtx); err != nil { + if err = proc.Init(post, tableArgs.typs, helper.SemaCtx, evalCtx); err != nil { return nil, err } tableArgs.ValNeededForCol = proc.NeededColumns() diff --git a/pkg/sql/colflow/vectorized_flow.go b/pkg/sql/colflow/vectorized_flow.go index 242740ecf784..64c940f71146 100644 --- a/pkg/sql/colflow/vectorized_flow.go +++ b/pkg/sql/colflow/vectorized_flow.go @@ -646,6 +646,9 @@ func (s *vectorizedFlowCreator) Release() { for i := range s.releasables { s.releasables[i] = nil } + if s.exprHelper != nil { + s.exprHelper.SemaCtx = nil + } *s = vectorizedFlowCreator{ streamIDToInputOp: s.streamIDToInputOp, streamIDToSpecIdx: s.streamIDToSpecIdx, @@ -1167,6 +1170,9 @@ func (s *vectorizedFlowCreator) setupFlow( ExprHelper: s.exprHelper, Factory: factory, } + if args.ExprHelper.SemaCtx == nil { + args.ExprHelper.SemaCtx = flowCtx.TypeResolverFactory.NewSemaContext(flowCtx.EvalCtx.Txn) + } var result *colexecargs.NewColOperatorResult result, err = colbuilder.NewColOperator(ctx, flowCtx, args) if result != nil { diff --git a/pkg/sql/execinfrapb/flow_diagram.go b/pkg/sql/execinfrapb/flow_diagram.go index 630ca5d1c6f7..09955c01630c 100644 --- a/pkg/sql/execinfrapb/flow_diagram.go +++ b/pkg/sql/execinfrapb/flow_diagram.go @@ -442,23 +442,17 @@ func (r *OutputRouterSpec) summary() (string, []string) { // summary implements the diagramCellType interface. func (post *PostProcessSpec) summary() []string { - return post.summaryWithPrefix("") -} - -// prefix is prepended to every line outputted to disambiguate processors -// (namely InterleavedReaderJoiner) that have multiple PostProcessors. -func (post *PostProcessSpec) summaryWithPrefix(prefix string) []string { var res []string if post.Projection { outputColumns := "None" if len(post.OutputColumns) > 0 { outputColumns = colListStr(post.OutputColumns) } - res = append(res, fmt.Sprintf("%sOut: %s", prefix, outputColumns)) + res = append(res, fmt.Sprintf("Out: %s", outputColumns)) } if len(post.RenderExprs) > 0 { var buf bytes.Buffer - buf.WriteString(fmt.Sprintf("%sRender: ", prefix)) + buf.WriteString("Render: ") for i, expr := range post.RenderExprs { if i > 0 { buf.WriteString(", ") @@ -472,13 +466,13 @@ func (post *PostProcessSpec) summaryWithPrefix(prefix string) []string { if post.Limit != 0 || post.Offset != 0 { var buf bytes.Buffer if post.Limit != 0 { - fmt.Fprintf(&buf, "%sLimit %d", prefix, post.Limit) + fmt.Fprintf(&buf, "Limit %d", post.Limit) } if post.Offset != 0 { if buf.Len() != 0 { buf.WriteByte(' ') } - fmt.Fprintf(&buf, "%sOffset %d", prefix, post.Offset) + fmt.Fprintf(&buf, "Offset %d", post.Offset) } res = append(res, buf.String()) } From 6105cb7df1e0a5ab36e0e2aaf113468bea9e611d Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Fri, 8 Oct 2021 15:16:38 -0400 Subject: [PATCH 059/205] build: add more instructions to vendor management README Previously, the instructions for installing and updating vendored dependencies were hard to follow, espcially for folks new to crbd. This commit adds significantly more detail to the instructions on vendor dependency managment. Fixes: #71297 Release note: None --- build/README.md | 137 ++++++++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 58 deletions(-) diff --git a/build/README.md b/build/README.md index 04fba47c17f5..6a9aeae47c74 100644 --- a/build/README.md +++ b/build/README.md @@ -143,67 +143,77 @@ The `bazelbuilder` image is used exclusively for performing builds using Bazel. # Dependencies -Dependencies are managed using `go mod`. We use `go mod vendor` so that we can import and use non-Go files (e.g. protobuf files) using the [modvendor](https://github.com/goware/modvendor) script. - -## Usage - -### Installing or updating a dependency - -Run `go get -u `. To get a specific version, run `go get -u @`. -You should see changes in `go.mod` when running `git diff`. - -When updating a dependency, you should run `go mod tidy` after `go get` to ensure the old entries -are removed from go.sum. - -You must then run `make vendor_rebuild` to ensure the modules are installed. -Ensure the vendor changes are as expected by running `cd vendor && git status`. If your import -is missing, ensure it is used in code. This can be a blank dependency, e.g. -`import _ "golang.org/api/compute/v1"`. These changes must then be committed in the submodule directory -(see [Working with Submodules](#working-with-submodules)). - -Finally, run `./dev generate bazel` to regenerate `DEPS.bzl` with the updated Go dependency information. - -Programs can then be run using `go build ...` or `go test ...`. +Dependencies are managed using `go mod`. We use `go mod vendor` so that we can import and use +non-Go files (e.g. protobuf files) using the [modvendor](https://github.com/goware/modvendor) +script. Adding or updating a dependecy is a two step process: 1) import the dependency in a go +file on your local branch, 2) push a commit containing this import to the `vendored` git submodule. + +## Working Locally with Dependencies + +### Installing a Dependency +1. In `cockroachdb/cockroach`, switch to the local branch you plan to import the external package + into +2. Run `go get -u `. To get a specific version, run `go get -u + @`. You should see changes in `go.mod` when running `git diff`. +3. Import the dependency to a go file in `cockorachdb/cockroach`. You may use an anonymous + import, e.g. `import _ "golang.org/api/compute/v1"`, if you haven't written any code that + references the dependency. This ensures cockroach's make file will properly add the package(s) to the vendor directory. Note that IDEs may bicker that + these import's paths don't exist. That's ok! +4. Run `go mod tidy` to ensure stale dependencies are removed. +5. Run `make vendor_rebuild` to add the package to the vendor directory. Note this command will only + add packages you have imported in the codebase (and any of the package's dependencies), so you + may want to add import statements for each package you plan to use (i.e. repeat step 3 a couple times). +6. Run `cd vendor && git diff && cd ..` to ensure the vendor directory contains the package(s) + you imported +7. Run `make buildshort` to ensure your code compiles. +8. Run `./dev generate bazel` to regenerate DEPS.bzl with the updated Go dependency information. +9. Follow instructions for [pushing the dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule) + +### Updating a Dependency +Follow the instructions for [Installing a Dependency](#installing-a-dependency). Note: +- If you're only importing a new package from an existing module in `go.mod`, you don't need to + re-download the module, step 2 above. +- If you're only updating the package version, you probably don't need to update the import + statements, step 3 above. + +When [pushing the dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule), you may either checkout a new branch, or create a new commit in the original branch you used to publicize the vendor + dependency. ### Removing a dependency - When a dependency has been removed, run `go mod tidy` and then `make vendor_rebuild`. -Then follow the [Working with Submodules](#working-with-submodules) steps. - -### Requiring a new tool - -When installing a tool, you may need to add blank import to `pkg/cmd/import-tools/main.go` so that -`go mod tidy` does not clean it up. - - -## Working with Submodules +Then follow the [Pushing the Dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule) steps. +## Working With Submodules To keep the bloat of all the changes in all our dependencies out of our main repository, we embed `vendor` as a git submodule, storing its content and history in [`vendored`](https://github.com/cockroachdb/vendored) instead. This split across two repositories however means that changes involving -changed dependencies require a two step process. +changed dependencies require a two step process. After altering dependencies and making related code +changes, follow the steps below. -- After altering dependencies and making related code -changes, `git status` in `cockroachdb/cockroach` checkout will report that the +### Pushing the Dependency to the `vendored` submodule +- Notice that `git status` in `cockroachdb/cockroach` checkout will report that the `vendor` submodule has `modified/untracked content`. -- Switch into `vendor` and commit all changes (or use `git -C vendor`), on a -new named branch. +- `cd` into `vendor`, and ... + + Checkout a **new** named branch + + Run `git add .` + + Commit all changes, with a nice short message. There's no explicit policy related to commit + messages in the vendored submodule. - + At this point the `git status` in your `cockroachdb/cockroach` checkout -will report `new commits` for `vendor` instead of `modified content`. +- At this point the `git status` in your `cockroachdb/cockroach` checkout will report `new commits` +for `vendor` instead of `modified content`. +- Back in your `cockroachdb/cockroach` branch, commit your code changes and the new `vendor` + submodule ref. -- Commit your code changes and new `vendor` submodule ref. +- Before the `cockroachdb/cockroach` commit can be submitted in a pull request, the submodule commit + it references must be available on `github.com/cockroachdb/vendored`. So, when you're ready to + publicize your vendor changes, push the `vendored` commit to remote: -- Before this commit can be submitted in a pull request to -`cockroachdb/cockroach`, the submodule commit it references must be available -on `github.com/cockroachdb/vendored`. - -* Organization members can push their named branches there directly. - -* Non-members should fork the `vendored` repo and submit a pull request to + + Organization members can push their named branches there directly, via: + + `git push [remote vendor name, probably 'origin'] [your vendor branch] ` + + Non-members should fork the `vendored` repo and submit a pull request to `cockroachdb/vendored`, and need wait for it to merge before they will be able to use it in a `cockroachdb/cockroach` PR. @@ -215,21 +225,32 @@ hashes in `vendored`, there is little significance to the `master` branch in previously referenced commit as their parent, regardless of what `master` happens to be. -That said, it is critical that any ref in `vendored` that is referenced from -`cockroachdb/cockroach` remain available in `vendored` in perpetuity: after a -PR referencing a ref merges, the `vendored` `master` branch should be updated -to point to it before the named feature branch can be deleted, to ensure the -ref remains reachable and thus is never garbage collected. +It is critical that any ref in vendored that is referenced from `cockroachdb/cockroach` remain +available in vendored in perpetuity. One way to ensure this is to leave the vendored branch that +you pushed your changes to in place. -### Conflicting Submodule Changes +If you would like to delete your feature branch in the vendored repository, you must first ensure +that another branch in vendored contains the commit referenced by `cockroachdb/cockroach`. You can +update the master branch in vendored to point at the git SHA currently referenced in +`cockroachdb/cockroach`. -The canonical linearization of history is always the main repo. In the event -of concurrent changes to `vendor`, the first should cause the second to see a -conflict on the `vendor` submodule pointer. When resolving that conflict, it -is important to re-run `go mod tidy `and `make vendor_rebuild` -against the fetched, updated `vendor` ref, thus generating a new commit in -the submodule that has as its parent the one from the earlier change. +### Conflicting Submodule Changes +If you pull/rebase from `cockroach/cockroachdb` and encounter a conflict in the vendor directory, +it is often easiest to take the master branch's vendored directory and then recreate your vendor +changes on top of it. For example:: +1. Remove your local changes to `vendored` by resetting local `vendored`'s head to `origin/master`'s +`vendored` dir: + + In vendor: `git reset --hard origin/master` +2. In `cockroach/cockroachdb`, amend the commit that contained the dirty vendor pointer. +3. Try pulling/rebasing again, and if that works, rebuild your local vendor repo with +`go mod tidy` and `make vendor_rebuild` +4. Push the clean vendor changes to the remote vendor submodule, following the [Pushing the Dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule) + +Note: you may also observe conflicts in `go.mod` and `go.sum`. Resolve the conflict like +any vanilla conflict on `cockroach/cockroachdb`, preferring master's +version. Then, `make vendor_rebuild` to re-add your local changes to `go. +mod` and `go.sum`. ### Recovering from a broken vendor directory If you happen to run into a broken `vendor` directory which is irrecoverable, From e83b38f7120f9a389ecadeae52bf04a41668e45a Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Tue, 26 Oct 2021 16:14:52 -0700 Subject: [PATCH 060/205] colexec: allow for some sort functions to be inlined This commit partially reverts the changes in 1bfa1a62818e8a8efdd009c656e7ef1dcc77bd12 so that in some cases `Less` implementations by the sorters could be inlined. It additionally enforces those inlines by a linter and keeps the allocator field only when it is actually used. Release note: None --- pkg/sql/colexec/sort.eg.go | 474 +++++++++-------------------------- pkg/sql/colexec/sort_tmpl.go | 28 ++- 2 files changed, 131 insertions(+), 371 deletions(-) diff --git a/pkg/sql/colexec/sort.eg.go b/pkg/sql/colexec/sort.eg.go index 302151e4d540..51111fae9fc4 100644 --- a/pkg/sql/colexec/sort.eg.go +++ b/pkg/sql/colexec/sort.eg.go @@ -423,7 +423,6 @@ func newSingleSorter( } type sortBoolAscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Bools nulls *coldata.Nulls order []int @@ -433,7 +432,6 @@ type sortBoolAscWithNullsOp struct { func (s *sortBoolAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bool() s.nulls = col.Nulls() s.order = order @@ -444,7 +442,6 @@ func (s *sortBoolAscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBoolAscWithNullsOp) sort() { @@ -482,13 +479,10 @@ func (s *sortBoolAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -517,8 +511,8 @@ func (s *sortBoolAscWithNullsOp) Len() int { } type sortBytesAscWithNullsOp struct { - allocator *colmem.Allocator sortCol *coldata.Bytes + allocator *colmem.Allocator abbreviatedSortCol []uint64 nulls *coldata.Nulls order []int @@ -528,8 +522,8 @@ type sortBytesAscWithNullsOp struct { func (s *sortBytesAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bytes() + s.allocator = allocator s.allocator.AdjustMemoryUsage(memsize.Uint64 * int64(s.sortCol.Len())) s.abbreviatedSortCol = s.sortCol.Abbreviated() s.nulls = col.Nulls() @@ -539,11 +533,11 @@ func (s *sortBytesAscWithNullsOp) init( func (s *sortBytesAscWithNullsOp) reset() { s.allocator.AdjustMemoryUsage(0 - memsize.Uint64*int64(s.sortCol.Len())) + s.allocator = nil s.abbreviatedSortCol = nil s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBytesAscWithNullsOp) sort() { @@ -581,22 +575,19 @@ func (s *sortBytesAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - // If the type can be abbreviated as a uint64, compare the abbreviated // values first. If they are not equal, we are done with the comparison. If // they are equal, we must fallback to a full comparison of the datums. - abbr1 := s.abbreviatedSortCol[order1] - abbr2 := s.abbreviatedSortCol[order2] + abbr1 := s.abbreviatedSortCol[s.order[i]] + abbr2 := s.abbreviatedSortCol[s.order[j]] if abbr1 != abbr2 { return abbr1 < abbr2 } var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -617,7 +608,6 @@ func (s *sortBytesAscWithNullsOp) Len() int { } type sortDecimalAscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Decimals nulls *coldata.Nulls order []int @@ -627,7 +617,6 @@ type sortDecimalAscWithNullsOp struct { func (s *sortDecimalAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Decimal() s.nulls = col.Nulls() s.order = order @@ -638,7 +627,6 @@ func (s *sortDecimalAscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDecimalAscWithNullsOp) sort() { @@ -676,13 +664,10 @@ func (s *sortDecimalAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -703,7 +688,6 @@ func (s *sortDecimalAscWithNullsOp) Len() int { } type sortInt16AscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Int16s nulls *coldata.Nulls order []int @@ -713,7 +697,6 @@ type sortInt16AscWithNullsOp struct { func (s *sortInt16AscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int16() s.nulls = col.Nulls() s.order = order @@ -724,7 +707,6 @@ func (s *sortInt16AscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt16AscWithNullsOp) sort() { @@ -762,13 +744,10 @@ func (s *sortInt16AscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -800,7 +779,6 @@ func (s *sortInt16AscWithNullsOp) Len() int { } type sortInt32AscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Int32s nulls *coldata.Nulls order []int @@ -810,7 +788,6 @@ type sortInt32AscWithNullsOp struct { func (s *sortInt32AscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int32() s.nulls = col.Nulls() s.order = order @@ -821,7 +798,6 @@ func (s *sortInt32AscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt32AscWithNullsOp) sort() { @@ -859,13 +835,10 @@ func (s *sortInt32AscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -897,7 +870,6 @@ func (s *sortInt32AscWithNullsOp) Len() int { } type sortInt64AscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Int64s nulls *coldata.Nulls order []int @@ -907,7 +879,6 @@ type sortInt64AscWithNullsOp struct { func (s *sortInt64AscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int64() s.nulls = col.Nulls() s.order = order @@ -918,7 +889,6 @@ func (s *sortInt64AscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt64AscWithNullsOp) sort() { @@ -956,13 +926,10 @@ func (s *sortInt64AscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -994,7 +961,6 @@ func (s *sortInt64AscWithNullsOp) Len() int { } type sortFloat64AscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Float64s nulls *coldata.Nulls order []int @@ -1004,7 +970,6 @@ type sortFloat64AscWithNullsOp struct { func (s *sortFloat64AscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Float64() s.nulls = col.Nulls() s.order = order @@ -1015,7 +980,6 @@ func (s *sortFloat64AscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortFloat64AscWithNullsOp) sort() { @@ -1053,13 +1017,10 @@ func (s *sortFloat64AscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1099,7 +1060,6 @@ func (s *sortFloat64AscWithNullsOp) Len() int { } type sortTimestampAscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Times nulls *coldata.Nulls order []int @@ -1109,7 +1069,6 @@ type sortTimestampAscWithNullsOp struct { func (s *sortTimestampAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Timestamp() s.nulls = col.Nulls() s.order = order @@ -1120,7 +1079,6 @@ func (s *sortTimestampAscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortTimestampAscWithNullsOp) sort() { @@ -1158,13 +1116,10 @@ func (s *sortTimestampAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1192,7 +1147,6 @@ func (s *sortTimestampAscWithNullsOp) Len() int { } type sortIntervalAscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Durations nulls *coldata.Nulls order []int @@ -1202,7 +1156,6 @@ type sortIntervalAscWithNullsOp struct { func (s *sortIntervalAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Interval() s.nulls = col.Nulls() s.order = order @@ -1213,7 +1166,6 @@ func (s *sortIntervalAscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortIntervalAscWithNullsOp) sort() { @@ -1251,13 +1203,10 @@ func (s *sortIntervalAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1278,7 +1227,6 @@ func (s *sortIntervalAscWithNullsOp) Len() int { } type sortJSONAscWithNullsOp struct { - allocator *colmem.Allocator sortCol *coldata.JSONs nulls *coldata.Nulls order []int @@ -1288,7 +1236,6 @@ type sortJSONAscWithNullsOp struct { func (s *sortJSONAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.JSON() s.nulls = col.Nulls() s.order = order @@ -1299,7 +1246,6 @@ func (s *sortJSONAscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortJSONAscWithNullsOp) sort() { @@ -1337,13 +1283,10 @@ func (s *sortJSONAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1370,7 +1313,6 @@ func (s *sortJSONAscWithNullsOp) Len() int { } type sortDatumAscWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.DatumVec nulls *coldata.Nulls order []int @@ -1380,7 +1322,6 @@ type sortDatumAscWithNullsOp struct { func (s *sortDatumAscWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Datum() s.nulls = col.Nulls() s.order = order @@ -1391,7 +1332,6 @@ func (s *sortDatumAscWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDatumAscWithNullsOp) sort() { @@ -1429,13 +1369,10 @@ func (s *sortDatumAscWithNullsOp) Less(i, j int) bool { return false } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1458,7 +1395,6 @@ func (s *sortDatumAscWithNullsOp) Len() int { } type sortBoolDescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Bools nulls *coldata.Nulls order []int @@ -1468,7 +1404,6 @@ type sortBoolDescWithNullsOp struct { func (s *sortBoolDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bool() s.nulls = col.Nulls() s.order = order @@ -1479,7 +1414,6 @@ func (s *sortBoolDescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBoolDescWithNullsOp) sort() { @@ -1517,13 +1451,10 @@ func (s *sortBoolDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1552,8 +1483,8 @@ func (s *sortBoolDescWithNullsOp) Len() int { } type sortBytesDescWithNullsOp struct { - allocator *colmem.Allocator sortCol *coldata.Bytes + allocator *colmem.Allocator abbreviatedSortCol []uint64 nulls *coldata.Nulls order []int @@ -1563,8 +1494,8 @@ type sortBytesDescWithNullsOp struct { func (s *sortBytesDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bytes() + s.allocator = allocator s.allocator.AdjustMemoryUsage(memsize.Uint64 * int64(s.sortCol.Len())) s.abbreviatedSortCol = s.sortCol.Abbreviated() s.nulls = col.Nulls() @@ -1574,11 +1505,11 @@ func (s *sortBytesDescWithNullsOp) init( func (s *sortBytesDescWithNullsOp) reset() { s.allocator.AdjustMemoryUsage(0 - memsize.Uint64*int64(s.sortCol.Len())) + s.allocator = nil s.abbreviatedSortCol = nil s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBytesDescWithNullsOp) sort() { @@ -1616,22 +1547,19 @@ func (s *sortBytesDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - // If the type can be abbreviated as a uint64, compare the abbreviated // values first. If they are not equal, we are done with the comparison. If // they are equal, we must fallback to a full comparison of the datums. - abbr1 := s.abbreviatedSortCol[order1] - abbr2 := s.abbreviatedSortCol[order2] + abbr1 := s.abbreviatedSortCol[s.order[i]] + abbr2 := s.abbreviatedSortCol[s.order[j]] if abbr1 != abbr2 { return abbr1 > abbr2 } var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1652,7 +1580,6 @@ func (s *sortBytesDescWithNullsOp) Len() int { } type sortDecimalDescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Decimals nulls *coldata.Nulls order []int @@ -1662,7 +1589,6 @@ type sortDecimalDescWithNullsOp struct { func (s *sortDecimalDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Decimal() s.nulls = col.Nulls() s.order = order @@ -1673,7 +1599,6 @@ func (s *sortDecimalDescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDecimalDescWithNullsOp) sort() { @@ -1711,13 +1636,10 @@ func (s *sortDecimalDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1738,7 +1660,6 @@ func (s *sortDecimalDescWithNullsOp) Len() int { } type sortInt16DescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Int16s nulls *coldata.Nulls order []int @@ -1748,7 +1669,6 @@ type sortInt16DescWithNullsOp struct { func (s *sortInt16DescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int16() s.nulls = col.Nulls() s.order = order @@ -1759,7 +1679,6 @@ func (s *sortInt16DescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt16DescWithNullsOp) sort() { @@ -1797,13 +1716,10 @@ func (s *sortInt16DescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1835,7 +1751,6 @@ func (s *sortInt16DescWithNullsOp) Len() int { } type sortInt32DescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Int32s nulls *coldata.Nulls order []int @@ -1845,7 +1760,6 @@ type sortInt32DescWithNullsOp struct { func (s *sortInt32DescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int32() s.nulls = col.Nulls() s.order = order @@ -1856,7 +1770,6 @@ func (s *sortInt32DescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt32DescWithNullsOp) sort() { @@ -1894,13 +1807,10 @@ func (s *sortInt32DescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -1932,7 +1842,6 @@ func (s *sortInt32DescWithNullsOp) Len() int { } type sortInt64DescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Int64s nulls *coldata.Nulls order []int @@ -1942,7 +1851,6 @@ type sortInt64DescWithNullsOp struct { func (s *sortInt64DescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int64() s.nulls = col.Nulls() s.order = order @@ -1953,7 +1861,6 @@ func (s *sortInt64DescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt64DescWithNullsOp) sort() { @@ -1991,13 +1898,10 @@ func (s *sortInt64DescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2029,7 +1933,6 @@ func (s *sortInt64DescWithNullsOp) Len() int { } type sortFloat64DescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Float64s nulls *coldata.Nulls order []int @@ -2039,7 +1942,6 @@ type sortFloat64DescWithNullsOp struct { func (s *sortFloat64DescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Float64() s.nulls = col.Nulls() s.order = order @@ -2050,7 +1952,6 @@ func (s *sortFloat64DescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortFloat64DescWithNullsOp) sort() { @@ -2088,13 +1989,10 @@ func (s *sortFloat64DescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2134,7 +2032,6 @@ func (s *sortFloat64DescWithNullsOp) Len() int { } type sortTimestampDescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Times nulls *coldata.Nulls order []int @@ -2144,7 +2041,6 @@ type sortTimestampDescWithNullsOp struct { func (s *sortTimestampDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Timestamp() s.nulls = col.Nulls() s.order = order @@ -2155,7 +2051,6 @@ func (s *sortTimestampDescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortTimestampDescWithNullsOp) sort() { @@ -2193,13 +2088,10 @@ func (s *sortTimestampDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2227,7 +2119,6 @@ func (s *sortTimestampDescWithNullsOp) Len() int { } type sortIntervalDescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.Durations nulls *coldata.Nulls order []int @@ -2237,7 +2128,6 @@ type sortIntervalDescWithNullsOp struct { func (s *sortIntervalDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Interval() s.nulls = col.Nulls() s.order = order @@ -2248,7 +2138,6 @@ func (s *sortIntervalDescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortIntervalDescWithNullsOp) sort() { @@ -2286,13 +2175,10 @@ func (s *sortIntervalDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2313,7 +2199,6 @@ func (s *sortIntervalDescWithNullsOp) Len() int { } type sortJSONDescWithNullsOp struct { - allocator *colmem.Allocator sortCol *coldata.JSONs nulls *coldata.Nulls order []int @@ -2323,7 +2208,6 @@ type sortJSONDescWithNullsOp struct { func (s *sortJSONDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.JSON() s.nulls = col.Nulls() s.order = order @@ -2334,7 +2218,6 @@ func (s *sortJSONDescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortJSONDescWithNullsOp) sort() { @@ -2372,13 +2255,10 @@ func (s *sortJSONDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2405,7 +2285,6 @@ func (s *sortJSONDescWithNullsOp) Len() int { } type sortDatumDescWithNullsOp struct { - allocator *colmem.Allocator sortCol coldata.DatumVec nulls *coldata.Nulls order []int @@ -2415,7 +2294,6 @@ type sortDatumDescWithNullsOp struct { func (s *sortDatumDescWithNullsOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Datum() s.nulls = col.Nulls() s.order = order @@ -2426,7 +2304,6 @@ func (s *sortDatumDescWithNullsOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDatumDescWithNullsOp) sort() { @@ -2464,13 +2341,10 @@ func (s *sortDatumDescWithNullsOp) Less(i, j int) bool { return true } - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2493,7 +2367,6 @@ func (s *sortDatumDescWithNullsOp) Len() int { } type sortBoolAscOp struct { - allocator *colmem.Allocator sortCol coldata.Bools nulls *coldata.Nulls order []int @@ -2503,7 +2376,6 @@ type sortBoolAscOp struct { func (s *sortBoolAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bool() s.nulls = col.Nulls() s.order = order @@ -2514,7 +2386,6 @@ func (s *sortBoolAscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBoolAscOp) sort() { @@ -2542,13 +2413,10 @@ func (s *sortBoolAscOp) sortPartitions(partitions []int) { func (s *sortBoolAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2577,8 +2445,8 @@ func (s *sortBoolAscOp) Len() int { } type sortBytesAscOp struct { - allocator *colmem.Allocator sortCol *coldata.Bytes + allocator *colmem.Allocator abbreviatedSortCol []uint64 nulls *coldata.Nulls order []int @@ -2588,8 +2456,8 @@ type sortBytesAscOp struct { func (s *sortBytesAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bytes() + s.allocator = allocator s.allocator.AdjustMemoryUsage(memsize.Uint64 * int64(s.sortCol.Len())) s.abbreviatedSortCol = s.sortCol.Abbreviated() s.nulls = col.Nulls() @@ -2599,11 +2467,11 @@ func (s *sortBytesAscOp) init( func (s *sortBytesAscOp) reset() { s.allocator.AdjustMemoryUsage(0 - memsize.Uint64*int64(s.sortCol.Len())) + s.allocator = nil s.abbreviatedSortCol = nil s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBytesAscOp) sort() { @@ -2631,22 +2499,19 @@ func (s *sortBytesAscOp) sortPartitions(partitions []int) { func (s *sortBytesAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - // If the type can be abbreviated as a uint64, compare the abbreviated // values first. If they are not equal, we are done with the comparison. If // they are equal, we must fallback to a full comparison of the datums. - abbr1 := s.abbreviatedSortCol[order1] - abbr2 := s.abbreviatedSortCol[order2] + abbr1 := s.abbreviatedSortCol[s.order[i]] + abbr2 := s.abbreviatedSortCol[s.order[j]] if abbr1 != abbr2 { return abbr1 < abbr2 } var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2667,7 +2532,6 @@ func (s *sortBytesAscOp) Len() int { } type sortDecimalAscOp struct { - allocator *colmem.Allocator sortCol coldata.Decimals nulls *coldata.Nulls order []int @@ -2677,7 +2541,6 @@ type sortDecimalAscOp struct { func (s *sortDecimalAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Decimal() s.nulls = col.Nulls() s.order = order @@ -2688,7 +2551,6 @@ func (s *sortDecimalAscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDecimalAscOp) sort() { @@ -2716,13 +2578,10 @@ func (s *sortDecimalAscOp) sortPartitions(partitions []int) { func (s *sortDecimalAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2743,7 +2602,6 @@ func (s *sortDecimalAscOp) Len() int { } type sortInt16AscOp struct { - allocator *colmem.Allocator sortCol coldata.Int16s nulls *coldata.Nulls order []int @@ -2753,7 +2611,6 @@ type sortInt16AscOp struct { func (s *sortInt16AscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int16() s.nulls = col.Nulls() s.order = order @@ -2764,7 +2621,6 @@ func (s *sortInt16AscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt16AscOp) sort() { @@ -2790,15 +2646,13 @@ func (s *sortInt16AscOp) sortPartitions(partitions []int) { } } +//gcassert:inline func (s *sortInt16AscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2830,7 +2684,6 @@ func (s *sortInt16AscOp) Len() int { } type sortInt32AscOp struct { - allocator *colmem.Allocator sortCol coldata.Int32s nulls *coldata.Nulls order []int @@ -2840,7 +2693,6 @@ type sortInt32AscOp struct { func (s *sortInt32AscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int32() s.nulls = col.Nulls() s.order = order @@ -2851,7 +2703,6 @@ func (s *sortInt32AscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt32AscOp) sort() { @@ -2877,15 +2728,13 @@ func (s *sortInt32AscOp) sortPartitions(partitions []int) { } } +//gcassert:inline func (s *sortInt32AscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -2917,7 +2766,6 @@ func (s *sortInt32AscOp) Len() int { } type sortInt64AscOp struct { - allocator *colmem.Allocator sortCol coldata.Int64s nulls *coldata.Nulls order []int @@ -2927,7 +2775,6 @@ type sortInt64AscOp struct { func (s *sortInt64AscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int64() s.nulls = col.Nulls() s.order = order @@ -2938,7 +2785,6 @@ func (s *sortInt64AscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt64AscOp) sort() { @@ -2964,15 +2810,13 @@ func (s *sortInt64AscOp) sortPartitions(partitions []int) { } } +//gcassert:inline func (s *sortInt64AscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3004,7 +2848,6 @@ func (s *sortInt64AscOp) Len() int { } type sortFloat64AscOp struct { - allocator *colmem.Allocator sortCol coldata.Float64s nulls *coldata.Nulls order []int @@ -3014,7 +2857,6 @@ type sortFloat64AscOp struct { func (s *sortFloat64AscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Float64() s.nulls = col.Nulls() s.order = order @@ -3025,7 +2867,6 @@ func (s *sortFloat64AscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortFloat64AscOp) sort() { @@ -3053,13 +2894,10 @@ func (s *sortFloat64AscOp) sortPartitions(partitions []int) { func (s *sortFloat64AscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3099,7 +2937,6 @@ func (s *sortFloat64AscOp) Len() int { } type sortTimestampAscOp struct { - allocator *colmem.Allocator sortCol coldata.Times nulls *coldata.Nulls order []int @@ -3109,7 +2946,6 @@ type sortTimestampAscOp struct { func (s *sortTimestampAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Timestamp() s.nulls = col.Nulls() s.order = order @@ -3120,7 +2956,6 @@ func (s *sortTimestampAscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortTimestampAscOp) sort() { @@ -3148,13 +2983,10 @@ func (s *sortTimestampAscOp) sortPartitions(partitions []int) { func (s *sortTimestampAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3182,7 +3014,6 @@ func (s *sortTimestampAscOp) Len() int { } type sortIntervalAscOp struct { - allocator *colmem.Allocator sortCol coldata.Durations nulls *coldata.Nulls order []int @@ -3192,7 +3023,6 @@ type sortIntervalAscOp struct { func (s *sortIntervalAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Interval() s.nulls = col.Nulls() s.order = order @@ -3203,7 +3033,6 @@ func (s *sortIntervalAscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortIntervalAscOp) sort() { @@ -3231,13 +3060,10 @@ func (s *sortIntervalAscOp) sortPartitions(partitions []int) { func (s *sortIntervalAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3258,7 +3084,6 @@ func (s *sortIntervalAscOp) Len() int { } type sortJSONAscOp struct { - allocator *colmem.Allocator sortCol *coldata.JSONs nulls *coldata.Nulls order []int @@ -3268,7 +3093,6 @@ type sortJSONAscOp struct { func (s *sortJSONAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.JSON() s.nulls = col.Nulls() s.order = order @@ -3279,7 +3103,6 @@ func (s *sortJSONAscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortJSONAscOp) sort() { @@ -3307,13 +3130,10 @@ func (s *sortJSONAscOp) sortPartitions(partitions []int) { func (s *sortJSONAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3340,7 +3160,6 @@ func (s *sortJSONAscOp) Len() int { } type sortDatumAscOp struct { - allocator *colmem.Allocator sortCol coldata.DatumVec nulls *coldata.Nulls order []int @@ -3350,7 +3169,6 @@ type sortDatumAscOp struct { func (s *sortDatumAscOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Datum() s.nulls = col.Nulls() s.order = order @@ -3361,7 +3179,6 @@ func (s *sortDatumAscOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDatumAscOp) sort() { @@ -3389,13 +3206,10 @@ func (s *sortDatumAscOp) sortPartitions(partitions []int) { func (s *sortDatumAscOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3418,7 +3232,6 @@ func (s *sortDatumAscOp) Len() int { } type sortBoolDescOp struct { - allocator *colmem.Allocator sortCol coldata.Bools nulls *coldata.Nulls order []int @@ -3428,7 +3241,6 @@ type sortBoolDescOp struct { func (s *sortBoolDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bool() s.nulls = col.Nulls() s.order = order @@ -3439,7 +3251,6 @@ func (s *sortBoolDescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBoolDescOp) sort() { @@ -3467,13 +3278,10 @@ func (s *sortBoolDescOp) sortPartitions(partitions []int) { func (s *sortBoolDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3502,8 +3310,8 @@ func (s *sortBoolDescOp) Len() int { } type sortBytesDescOp struct { - allocator *colmem.Allocator sortCol *coldata.Bytes + allocator *colmem.Allocator abbreviatedSortCol []uint64 nulls *coldata.Nulls order []int @@ -3513,8 +3321,8 @@ type sortBytesDescOp struct { func (s *sortBytesDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Bytes() + s.allocator = allocator s.allocator.AdjustMemoryUsage(memsize.Uint64 * int64(s.sortCol.Len())) s.abbreviatedSortCol = s.sortCol.Abbreviated() s.nulls = col.Nulls() @@ -3524,11 +3332,11 @@ func (s *sortBytesDescOp) init( func (s *sortBytesDescOp) reset() { s.allocator.AdjustMemoryUsage(0 - memsize.Uint64*int64(s.sortCol.Len())) + s.allocator = nil s.abbreviatedSortCol = nil s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortBytesDescOp) sort() { @@ -3556,22 +3364,19 @@ func (s *sortBytesDescOp) sortPartitions(partitions []int) { func (s *sortBytesDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - // If the type can be abbreviated as a uint64, compare the abbreviated // values first. If they are not equal, we are done with the comparison. If // they are equal, we must fallback to a full comparison of the datums. - abbr1 := s.abbreviatedSortCol[order1] - abbr2 := s.abbreviatedSortCol[order2] + abbr1 := s.abbreviatedSortCol[s.order[i]] + abbr2 := s.abbreviatedSortCol[s.order[j]] if abbr1 != abbr2 { return abbr1 > abbr2 } var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3592,7 +3397,6 @@ func (s *sortBytesDescOp) Len() int { } type sortDecimalDescOp struct { - allocator *colmem.Allocator sortCol coldata.Decimals nulls *coldata.Nulls order []int @@ -3602,7 +3406,6 @@ type sortDecimalDescOp struct { func (s *sortDecimalDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Decimal() s.nulls = col.Nulls() s.order = order @@ -3613,7 +3416,6 @@ func (s *sortDecimalDescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDecimalDescOp) sort() { @@ -3641,13 +3443,10 @@ func (s *sortDecimalDescOp) sortPartitions(partitions []int) { func (s *sortDecimalDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3668,7 +3467,6 @@ func (s *sortDecimalDescOp) Len() int { } type sortInt16DescOp struct { - allocator *colmem.Allocator sortCol coldata.Int16s nulls *coldata.Nulls order []int @@ -3678,7 +3476,6 @@ type sortInt16DescOp struct { func (s *sortInt16DescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int16() s.nulls = col.Nulls() s.order = order @@ -3689,7 +3486,6 @@ func (s *sortInt16DescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt16DescOp) sort() { @@ -3715,15 +3511,13 @@ func (s *sortInt16DescOp) sortPartitions(partitions []int) { } } +//gcassert:inline func (s *sortInt16DescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3755,7 +3549,6 @@ func (s *sortInt16DescOp) Len() int { } type sortInt32DescOp struct { - allocator *colmem.Allocator sortCol coldata.Int32s nulls *coldata.Nulls order []int @@ -3765,7 +3558,6 @@ type sortInt32DescOp struct { func (s *sortInt32DescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int32() s.nulls = col.Nulls() s.order = order @@ -3776,7 +3568,6 @@ func (s *sortInt32DescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt32DescOp) sort() { @@ -3802,15 +3593,13 @@ func (s *sortInt32DescOp) sortPartitions(partitions []int) { } } +//gcassert:inline func (s *sortInt32DescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3842,7 +3631,6 @@ func (s *sortInt32DescOp) Len() int { } type sortInt64DescOp struct { - allocator *colmem.Allocator sortCol coldata.Int64s nulls *coldata.Nulls order []int @@ -3852,7 +3640,6 @@ type sortInt64DescOp struct { func (s *sortInt64DescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Int64() s.nulls = col.Nulls() s.order = order @@ -3863,7 +3650,6 @@ func (s *sortInt64DescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortInt64DescOp) sort() { @@ -3889,15 +3675,13 @@ func (s *sortInt64DescOp) sortPartitions(partitions []int) { } } +//gcassert:inline func (s *sortInt64DescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -3929,7 +3713,6 @@ func (s *sortInt64DescOp) Len() int { } type sortFloat64DescOp struct { - allocator *colmem.Allocator sortCol coldata.Float64s nulls *coldata.Nulls order []int @@ -3939,7 +3722,6 @@ type sortFloat64DescOp struct { func (s *sortFloat64DescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Float64() s.nulls = col.Nulls() s.order = order @@ -3950,7 +3732,6 @@ func (s *sortFloat64DescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortFloat64DescOp) sort() { @@ -3978,13 +3759,10 @@ func (s *sortFloat64DescOp) sortPartitions(partitions []int) { func (s *sortFloat64DescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -4024,7 +3802,6 @@ func (s *sortFloat64DescOp) Len() int { } type sortTimestampDescOp struct { - allocator *colmem.Allocator sortCol coldata.Times nulls *coldata.Nulls order []int @@ -4034,7 +3811,6 @@ type sortTimestampDescOp struct { func (s *sortTimestampDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Timestamp() s.nulls = col.Nulls() s.order = order @@ -4045,7 +3821,6 @@ func (s *sortTimestampDescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortTimestampDescOp) sort() { @@ -4073,13 +3848,10 @@ func (s *sortTimestampDescOp) sortPartitions(partitions []int) { func (s *sortTimestampDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -4107,7 +3879,6 @@ func (s *sortTimestampDescOp) Len() int { } type sortIntervalDescOp struct { - allocator *colmem.Allocator sortCol coldata.Durations nulls *coldata.Nulls order []int @@ -4117,7 +3888,6 @@ type sortIntervalDescOp struct { func (s *sortIntervalDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Interval() s.nulls = col.Nulls() s.order = order @@ -4128,7 +3898,6 @@ func (s *sortIntervalDescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortIntervalDescOp) sort() { @@ -4156,13 +3925,10 @@ func (s *sortIntervalDescOp) sortPartitions(partitions []int) { func (s *sortIntervalDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -4183,7 +3949,6 @@ func (s *sortIntervalDescOp) Len() int { } type sortJSONDescOp struct { - allocator *colmem.Allocator sortCol *coldata.JSONs nulls *coldata.Nulls order []int @@ -4193,7 +3958,6 @@ type sortJSONDescOp struct { func (s *sortJSONDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.JSON() s.nulls = col.Nulls() s.order = order @@ -4204,7 +3968,6 @@ func (s *sortJSONDescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortJSONDescOp) sort() { @@ -4232,13 +3995,10 @@ func (s *sortJSONDescOp) sortPartitions(partitions []int) { func (s *sortJSONDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int @@ -4265,7 +4025,6 @@ func (s *sortJSONDescOp) Len() int { } type sortDatumDescOp struct { - allocator *colmem.Allocator sortCol coldata.DatumVec nulls *coldata.Nulls order []int @@ -4275,7 +4034,6 @@ type sortDatumDescOp struct { func (s *sortDatumDescOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.Datum() s.nulls = col.Nulls() s.order = order @@ -4286,7 +4044,6 @@ func (s *sortDatumDescOp) reset() { s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sortDatumDescOp) sort() { @@ -4314,13 +4071,10 @@ func (s *sortDatumDescOp) sortPartitions(partitions []int) { func (s *sortDatumDescOp) Less(i, j int) bool { - order1 := s.order[i] - order2 := s.order[j] - var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) { var cmpResult int diff --git a/pkg/sql/colexec/sort_tmpl.go b/pkg/sql/colexec/sort_tmpl.go index 7a75138596ec..7ddc99a6711f 100644 --- a/pkg/sql/colexec/sort_tmpl.go +++ b/pkg/sql/colexec/sort_tmpl.go @@ -135,9 +135,9 @@ func newSingleSorter( // {{range .WidthOverloads}} type sort_TYPE_DIR_HANDLES_NULLSOp struct { - allocator *colmem.Allocator - sortCol _GOTYPESLICE + sortCol _GOTYPESLICE // {{if .CanAbbreviate}} + allocator *colmem.Allocator abbreviatedSortCol []uint64 // {{end}} nulls *coldata.Nulls @@ -148,9 +148,9 @@ type sort_TYPE_DIR_HANDLES_NULLSOp struct { func (s *sort_TYPE_DIR_HANDLES_NULLSOp) init( ctx context.Context, allocator *colmem.Allocator, col coldata.Vec, order []int, ) { - s.allocator = allocator s.sortCol = col.TemplateType() // {{if .CanAbbreviate}} + s.allocator = allocator s.allocator.AdjustMemoryUsage(memsize.Uint64 * int64(s.sortCol.Len())) s.abbreviatedSortCol = s.sortCol.Abbreviated() // {{end}} @@ -162,12 +162,12 @@ func (s *sort_TYPE_DIR_HANDLES_NULLSOp) init( func (s *sort_TYPE_DIR_HANDLES_NULLSOp) reset() { // {{if .CanAbbreviate}} s.allocator.AdjustMemoryUsage(0 - memsize.Uint64*int64(s.sortCol.Len())) + s.allocator = nil s.abbreviatedSortCol = nil // {{end}} s.sortCol = nil s.nulls = nil s.order = nil - s.allocator = nil } func (s *sort_TYPE_DIR_HANDLES_NULLSOp) sort() { @@ -193,6 +193,15 @@ func (s *sort_TYPE_DIR_HANDLES_NULLSOp) sortPartitions(partitions []int) { } } +// {{/* +// TODO(yuzefovich): think through how we can inline more implementations of +// Less method - this has non-trivial performance improvements. +// */}} +// {{$isInt := or (eq .VecMethod "Int16") (eq .VecMethod "Int32")}} +// {{$isInt = or ($isInt) (eq .VecMethod "Int64")}} +// {{if and ($isInt) (not $nulls)}} +//gcassert:inline +// {{end}} func (s *sort_TYPE_DIR_HANDLES_NULLSOp) Less(i, j int) bool { // {{if $nulls}} n1 := s.nulls.MaybeHasNulls() && s.nulls.NullAt(s.order[i]) @@ -218,15 +227,12 @@ func (s *sort_TYPE_DIR_HANDLES_NULLSOp) Less(i, j int) bool { // {{end}} // {{end}} - order1 := s.order[i] - order2 := s.order[j] - // {{if .CanAbbreviate}} // If the type can be abbreviated as a uint64, compare the abbreviated // values first. If they are not equal, we are done with the comparison. If // they are equal, we must fallback to a full comparison of the datums. - abbr1 := s.abbreviatedSortCol[order1] - abbr2 := s.abbreviatedSortCol[order2] + abbr1 := s.abbreviatedSortCol[s.order[i]] + abbr2 := s.abbreviatedSortCol[s.order[j]] if abbr1 != abbr2 { // {{if eq $dir "Asc"}} return abbr1 < abbr2 @@ -238,8 +244,8 @@ func (s *sort_TYPE_DIR_HANDLES_NULLSOp) Less(i, j int) bool { var lt bool // We always indirect via the order vector. - arg1 := s.sortCol.Get(order1) - arg2 := s.sortCol.Get(order2) + arg1 := s.sortCol.Get(s.order[i]) + arg2 := s.sortCol.Get(s.order[j]) _ASSIGN_LT(lt, arg1, arg2, _, s.sortCol, s.sortCol) return lt } From 8e40f1ce9ccc5e5b9aa00c48f5369ab50bfae156 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Tue, 12 Oct 2021 14:13:54 -0700 Subject: [PATCH 061/205] colfetcher: produce batches only of needed columns This commit updates the cFetcher to operate only on the needed columns. Previously, it would get all columns present in the whole table and create `coldata.Batch`es for each of the columns, even when only a subset of columns is needed (or even available if the index is not covering). For example, imagine we have a table like ``` t (a INT PRIMARY KEY, b INT, c INT, d INT, INDEX b_idx (b)) ``` and we have a query like `SELECT b FROM t@b_idx`. Previously, we would create a batch with 4 `int64` columns with only 1 (for column `b`) being actually populated and all others marked as "not needed" and set to all NULL values. This is suboptimal, and this commit refactors things so that a batch with only a single vector is created. This is achieved in two steps: - first, we populate the slice of column descriptors that are accessible from the index. These are all columns of the table for covering indexes. For non-covering secondary indexes we only keep columns present in the index - next, we examine the set of columns that need to be fetched and prune all not needed columns away. Note that we are always careful to update the post-processing spec accordingly so that the spec always refers to correct new ordinals. It's worth pointing out that since we're modifying the spec directly, we had to introduce some special logic to keep the original state of the `PostProcessSpec` in order for the flow diagrams to refer to the original columns. Release note: None --- pkg/sql/colexec/colbuilder/execplan.go | 2 +- .../colexec/colexecspan/span_assembler.eg.go | 7 +- .../colexecspan/span_assembler_tmpl.go | 7 +- pkg/sql/colexec/hash_aggregator.go | 2 +- pkg/sql/colexec/ordered_synchronizer.eg.go | 2 +- pkg/sql/colexec/ordered_synchronizer_tmpl.go | 2 +- pkg/sql/colfetcher/BUILD.bazel | 2 + pkg/sql/colfetcher/cfetcher.go | 310 ++++++---------- pkg/sql/colfetcher/cfetcher_setup.go | 336 ++++++++++++++++++ pkg/sql/colfetcher/colbatch_scan.go | 105 +----- pkg/sql/colfetcher/index_join.go | 34 +- pkg/sql/colmem/allocator.go | 10 +- pkg/sql/colmem/allocator_test.go | 6 +- pkg/sql/distsql/server.go | 5 + pkg/sql/distsql_running.go | 3 + pkg/sql/execinfra/flow_context.go | 4 + pkg/sql/execinfrapb/flow_diagram.go | 16 +- pkg/sql/execinfrapb/processors_base.pb.go | 253 ++++++++++--- pkg/sql/execinfrapb/processors_base.proto | 10 + .../exec/execbuilder/testdata/dist_vectorize | 35 +- pkg/sql/opt/exec/execbuilder/testdata/window | 12 +- pkg/sql/physicalplan/aggregator_funcs_test.go | 11 +- pkg/sql/physicalplan/expression.go | 6 +- 23 files changed, 781 insertions(+), 399 deletions(-) create mode 100644 pkg/sql/colfetcher/cfetcher_setup.go diff --git a/pkg/sql/colexec/colbuilder/execplan.go b/pkg/sql/colexec/colbuilder/execplan.go index 9fd4d25de80b..007601d622c5 100644 --- a/pkg/sql/colexec/colbuilder/execplan.go +++ b/pkg/sql/colexec/colbuilder/execplan.go @@ -760,7 +760,7 @@ func NewColOperator( estimatedRowCount := spec.EstimatedRowCount scanOp, err := colfetcher.NewColBatchScan( ctx, colmem.NewAllocator(ctx, cFetcherMemAcc, factory), kvFetcherMemAcc, - flowCtx, evalCtx, core.TableReader, post, estimatedRowCount, + flowCtx, evalCtx, args.ExprHelper, core.TableReader, post, estimatedRowCount, ) if err != nil { return r, err diff --git a/pkg/sql/colexec/colexecspan/span_assembler.eg.go b/pkg/sql/colexec/colexecspan/span_assembler.eg.go index e9868f6106c1..556b945fb46a 100644 --- a/pkg/sql/colexec/colexecspan/span_assembler.eg.go +++ b/pkg/sql/colexec/colexecspan/span_assembler.eg.go @@ -30,16 +30,19 @@ import ( // NewColSpanAssembler returns a ColSpanAssembler operator that is able to // generate lookup spans from input batches. +// - neededColOrdsInWholeTable is a set containing the ordinals of all columns +// that need to be fetched. These ordinals are based on the schema of the whole +// table rather than only among the needed columns. func NewColSpanAssembler( codec keys.SQLCodec, allocator *colmem.Allocator, table catalog.TableDescriptor, index catalog.Index, inputTypes []*types.T, - neededCols util.FastIntSet, + neededColOrdsInWholeTable util.FastIntSet, ) ColSpanAssembler { base := spanAssemblerPool.Get().(*spanAssemblerBase) - base.colFamStartKeys, base.colFamEndKeys = getColFamilyEncodings(neededCols, table, index) + base.colFamStartKeys, base.colFamEndKeys = getColFamilyEncodings(neededColOrdsInWholeTable, table, index) keyPrefix := rowenc.MakeIndexKeyPrefix(codec, table, index.GetID()) base.scratchKey = append(base.scratchKey[:0], keyPrefix...) base.prefixLength = len(keyPrefix) diff --git a/pkg/sql/colexec/colexecspan/span_assembler_tmpl.go b/pkg/sql/colexec/colexecspan/span_assembler_tmpl.go index 3a2f11aaeb28..18cd975543cb 100644 --- a/pkg/sql/colexec/colexecspan/span_assembler_tmpl.go +++ b/pkg/sql/colexec/colexecspan/span_assembler_tmpl.go @@ -42,16 +42,19 @@ import ( // NewColSpanAssembler returns a ColSpanAssembler operator that is able to // generate lookup spans from input batches. +// - neededColOrdsInWholeTable is a set containing the ordinals of all columns +// that need to be fetched. These ordinals are based on the schema of the whole +// table rather than only among the needed columns. func NewColSpanAssembler( codec keys.SQLCodec, allocator *colmem.Allocator, table catalog.TableDescriptor, index catalog.Index, inputTypes []*types.T, - neededCols util.FastIntSet, + neededColOrdsInWholeTable util.FastIntSet, ) ColSpanAssembler { base := spanAssemblerPool.Get().(*spanAssemblerBase) - base.colFamStartKeys, base.colFamEndKeys = getColFamilyEncodings(neededCols, table, index) + base.colFamStartKeys, base.colFamEndKeys = getColFamilyEncodings(neededColOrdsInWholeTable, table, index) keyPrefix := rowenc.MakeIndexKeyPrefix(codec, table, index.GetID()) base.scratchKey = append(base.scratchKey[:0], keyPrefix...) base.prefixLength = len(keyPrefix) diff --git a/pkg/sql/colexec/hash_aggregator.go b/pkg/sql/colexec/hash_aggregator.go index 21101ed149ee..0c1c62c95c54 100644 --- a/pkg/sql/colexec/hash_aggregator.go +++ b/pkg/sql/colexec/hash_aggregator.go @@ -222,7 +222,7 @@ func NewHashAggregator( aggFnsAlloc: aggFnsAlloc, hashAlloc: aggBucketAlloc{allocator: args.Allocator}, } - hashAgg.accountingHelper.Init(outputUnlimitedAllocator, args.OutputTypes, nil /* notNeededVecIdxs */) + hashAgg.accountingHelper.Init(outputUnlimitedAllocator, args.OutputTypes) hashAgg.bufferingState.tuples = colexecutils.NewAppendOnlyBufferedBatch(args.Allocator, args.InputTypes, nil /* colsToStore */) hashAgg.datumAlloc.AllocSize = hashAggregatorAllocSize hashAgg.aggHelper = newAggregatorHelper(args, &hashAgg.datumAlloc, true /* isHashAgg */, hashAggregatorMaxBuffered) diff --git a/pkg/sql/colexec/ordered_synchronizer.eg.go b/pkg/sql/colexec/ordered_synchronizer.eg.go index fb011362b88b..65135b7bd097 100644 --- a/pkg/sql/colexec/ordered_synchronizer.eg.go +++ b/pkg/sql/colexec/ordered_synchronizer.eg.go @@ -118,7 +118,7 @@ func NewOrderedSynchronizer( typs: typs, canonicalTypeFamilies: typeconv.ToCanonicalTypeFamilies(typs), } - os.accountingHelper.Init(allocator, typs, nil /* notNeededVecIdxs */) + os.accountingHelper.Init(allocator, typs) return os } diff --git a/pkg/sql/colexec/ordered_synchronizer_tmpl.go b/pkg/sql/colexec/ordered_synchronizer_tmpl.go index 0dac97833d4c..8922ebc926f8 100644 --- a/pkg/sql/colexec/ordered_synchronizer_tmpl.go +++ b/pkg/sql/colexec/ordered_synchronizer_tmpl.go @@ -138,7 +138,7 @@ func NewOrderedSynchronizer( typs: typs, canonicalTypeFamilies: typeconv.ToCanonicalTypeFamilies(typs), } - os.accountingHelper.Init(allocator, typs, nil /* notNeededVecIdxs */) + os.accountingHelper.Init(allocator, typs) return os } diff --git a/pkg/sql/colfetcher/BUILD.bazel b/pkg/sql/colfetcher/BUILD.bazel index d66e76710756..a4cb289bbc1c 100644 --- a/pkg/sql/colfetcher/BUILD.bazel +++ b/pkg/sql/colfetcher/BUILD.bazel @@ -5,6 +5,7 @@ go_library( name = "colfetcher", srcs = [ "cfetcher.go", + "cfetcher_setup.go", "colbatch_scan.go", "index_join.go", ":gen-fetcherstate-stringer", # keep @@ -31,6 +32,7 @@ go_library( "//pkg/sql/execinfra", "//pkg/sql/execinfrapb", "//pkg/sql/memsize", + "//pkg/sql/physicalplan", "//pkg/sql/row", "//pkg/sql/rowenc", "//pkg/sql/rowinfra", diff --git a/pkg/sql/colfetcher/cfetcher.go b/pkg/sql/colfetcher/cfetcher.go index c80651b31459..6a6fb1553773 100644 --- a/pkg/sql/colfetcher/cfetcher.go +++ b/pkg/sql/colfetcher/cfetcher.go @@ -52,25 +52,17 @@ type cTableInfo struct { *cFetcherTableArgs indexColumnDirs []descpb.IndexDescriptor_Direction - // The ordered list of ColumnIDs that are required. - neededColsList []int - - // The set of required value-component column ordinals in the table. + // The set of required value-component column ordinals among only needed + // columns. neededValueColsByIdx util.FastIntSet - // The set of ordinals of the columns that are **not** required. cFetcher - // creates an output batch that includes all columns of the table, yet only - // needed columns are actually populated. The vectors at positions in - // notNeededColOrdinals will be set to have all null values. - notNeededColOrdinals []int - // Map used to get the column index based on the descpb.ColumnID. // It's kept as a pointer so we don't have to re-allocate to sort it each // time. - colIdxMap *colIdxMap + orderedColIdxMap *colIdxMap // One value per column that is part of the key; each value is a column - // ordinal among all columns of the table; -1 if we don't need the value for + // ordinal among only needed columns; -1 if we don't need the value for // that column. // // Note that if the tracing is enabled on the cFetcher (traceKV == true), @@ -83,8 +75,8 @@ type cTableInfo struct { compositeIndexColOrdinals util.FastIntSet // One number per column coming from the "key suffix" that is part of the - // value; each number is a column ordinal among all columns of the table; -1 - // if we don't need the value for that column. + // value; each number is a column ordinal among only needed columns; -1 if + // we don't need the value for that column. // // The "key suffix" columns are only used for secondary indexes: // - for non-unique indexes, these columns are appended to the key (and will @@ -98,7 +90,7 @@ type cTableInfo struct { // extraValColOrdinals. extraValColOrdinals []int - // invertedColOrdinal is a column ordinal among all columns of the table, + // invertedColOrdinal is a column ordinal among only needed columns, // indicating the inverted column; -1 if there is no inverted column or we // don't need the value for that column. invertedColOrdinal int @@ -118,10 +110,11 @@ type cTableInfo struct { // rowLastModified is the timestamp of the last time any family in the row // was modified in any way. rowLastModified hlc.Timestamp - // timestampOutputIdx controls at what row ordinal to write the timestamp. + // timestampOutputIdx controls at what column ordinal in the output batch to + // write the timestamp for the MVCC timestamp system column. timestampOutputIdx int - - // Fields for outputting the tableoid system column. + // oidOutputIdx controls at what column ordinal in the output batch to write + // the value for the tableoid system column. oidOutputIdx int keyValTypes []*types.T @@ -138,7 +131,7 @@ var _ execinfra.Releasable = &cTableInfo{} var cTableInfoPool = sync.Pool{ New: func() interface{} { return &cTableInfo{ - colIdxMap: &colIdxMap{}, + orderedColIdxMap: &colIdxMap{}, } }, } @@ -152,28 +145,26 @@ func (c *cTableInfo) Release() { c.cFetcherTableArgs.Release() // Note that all slices are being reused, but there is no need to deeply // reset them since all of the slices are of Go native types. - c.colIdxMap.ords = c.colIdxMap.ords[:0] - c.colIdxMap.vals = c.colIdxMap.vals[:0] + c.orderedColIdxMap.ords = c.orderedColIdxMap.ords[:0] + c.orderedColIdxMap.vals = c.orderedColIdxMap.vals[:0] *c = cTableInfo{ - neededColsList: c.neededColsList[:0], - notNeededColOrdinals: c.notNeededColOrdinals[:0], - colIdxMap: c.colIdxMap, - indexColOrdinals: c.indexColOrdinals[:0], - extraValColOrdinals: c.extraValColOrdinals[:0], - keyValTypes: c.keyValTypes[:0], - extraTypes: c.extraTypes[:0], - extraValDirections: c.extraValDirections[:0], + orderedColIdxMap: c.orderedColIdxMap, + indexColOrdinals: c.indexColOrdinals[:0], + extraValColOrdinals: c.extraValColOrdinals[:0], + keyValTypes: c.keyValTypes[:0], + extraTypes: c.extraTypes[:0], + extraValDirections: c.extraValDirections[:0], } cTableInfoPool.Put(c) } -// colIdxMap is a "map" that contains the ordinal among all columns of the table -// for each ColumnID to fetch. This map is used to figure out what index within -// a row a particular value-component column goes into. Value-component columns -// are encoded with a column id prefix, with the guarantee that within any given -// row, the column ids are always increasing. Because of this guarantee, we can -// store this map as two sorted lists that the fetcher keeps an index into, -// giving fast access during decoding. +// colIdxMap is a "map" that contains the ordinals for each ColumnID among the +// columns that need to be fetched. This map is used to figure out what index +// within a row a particular value-component column goes into. Value-component +// columns are encoded with a column id prefix, with the guarantee that within +// any given row, the column ids are always increasing. Because of this +// guarantee, we can store this map as two sorted lists that the fetcher keeps +// an index into, giving fast access during decoding. // // It implements sort.Interface to be sortable on vals, while keeping ords // matched up to the order of vals. @@ -202,15 +193,6 @@ func (m colIdxMap) Swap(i, j int) { m.ords[i], m.ords[j] = m.ords[j], m.ords[i] } -func (m colIdxMap) get(c descpb.ColumnID) (int, bool) { - for i, v := range m.vals { - if v == c { - return m.ords[i], true - } - } - return 0, false -} - type cFetcherArgs struct { // lockStrength represents the row-level locking mode to use when fetching // rows. @@ -386,75 +368,72 @@ func (rf *cFetcher) resetBatch() { } } -// Init sets up a Fetcher based on the table args. If we are using a non-primary -// index, tableArgs.ValNeededForCol can only refer to columns in the index. +// Init sets up a Fetcher based on the table args. Only columns present in +// tableArgs.cols will be fetched. func (rf *cFetcher) Init( codec keys.SQLCodec, allocator *colmem.Allocator, kvFetcherMemAcc *mon.BoundAccount, tableArgs *cFetcherTableArgs, + hasSystemColumns bool, ) error { rf.kvFetcherMemAcc = kvFetcherMemAcc table := newCTableInfo() nCols := tableArgs.ColIdxMap.Len() - if cap(table.colIdxMap.vals) < nCols { - table.colIdxMap.vals = make(descpb.ColumnIDs, 0, nCols) - table.colIdxMap.ords = make([]int, 0, nCols) + if cap(table.orderedColIdxMap.vals) < nCols { + table.orderedColIdxMap.vals = make(descpb.ColumnIDs, 0, nCols) + table.orderedColIdxMap.ords = make([]int, 0, nCols) } colDescriptors := tableArgs.cols for i := range colDescriptors { //gcassert:bce id := colDescriptors[i].GetID() - table.colIdxMap.vals = append(table.colIdxMap.vals, id) - table.colIdxMap.ords = append(table.colIdxMap.ords, tableArgs.ColIdxMap.GetDefault(id)) + table.orderedColIdxMap.vals = append(table.orderedColIdxMap.vals, id) + table.orderedColIdxMap.ords = append(table.orderedColIdxMap.ords, tableArgs.ColIdxMap.GetDefault(id)) } - sort.Sort(table.colIdxMap) + sort.Sort(table.orderedColIdxMap) *table = cTableInfo{ - cFetcherTableArgs: tableArgs, - neededColsList: table.neededColsList[:0], - notNeededColOrdinals: table.notNeededColOrdinals[:0], - colIdxMap: table.colIdxMap, - indexColOrdinals: table.indexColOrdinals[:0], - extraValColOrdinals: table.extraValColOrdinals[:0], - keyValTypes: table.keyValTypes[:0], - extraTypes: table.extraTypes[:0], - extraValDirections: table.extraValDirections[:0], - timestampOutputIdx: noOutputColumn, - oidOutputIdx: noOutputColumn, + cFetcherTableArgs: tableArgs, + orderedColIdxMap: table.orderedColIdxMap, + indexColOrdinals: table.indexColOrdinals[:0], + extraValColOrdinals: table.extraValColOrdinals[:0], + keyValTypes: table.keyValTypes[:0], + extraTypes: table.extraTypes[:0], + extraValDirections: table.extraValDirections[:0], + timestampOutputIdx: noOutputColumn, + oidOutputIdx: noOutputColumn, } - var neededCols util.FastIntSet - numNeededCols := tableArgs.ValNeededForCol.Len() - // Scan through the entire columns map to see which columns are required and - // which columns are not needed (the latter will be set to all null values). - if cap(table.neededColsList) < numNeededCols { - table.neededColsList = make([]int, 0, numNeededCols) - } - if cap(table.notNeededColOrdinals) < len(colDescriptors)-numNeededCols { - table.notNeededColOrdinals = make([]int, 0, len(colDescriptors)-numNeededCols) + if nCols > 0 { + table.neededValueColsByIdx.AddRange(0 /* start */, nCols-1) } - for i := range colDescriptors { - //gcassert:bce - col := colDescriptors[i].GetID() - idx := tableArgs.ColIdxMap.GetDefault(col) - if tableArgs.ValNeededForCol.Contains(idx) { - // The idx-th column is required. - neededCols.Add(int(col)) - table.neededColsList = append(table.neededColsList, int(col)) - // Set up extra metadata for system columns, if this is a system column. + + if hasSystemColumns { + // System columns, if present, are at the end of colDescriptors. + nonSystemColOffset := nCols - len(colinfo.AllSystemColumnDescs) + if nonSystemColOffset < 0 { + nonSystemColOffset = 0 + } + for idx := nonSystemColOffset; idx < nCols; idx++ { + col := colDescriptors[idx].GetID() + // Set up extra metadata for system columns, if this is a system + // column. + // + // Currently the system columns are present in neededValueColsByIdx, + // but we don't want to include them in that set because the + // handling of system columns is separate from the standard value + // decoding process. switch colinfo.GetSystemColumnKindFromColumnID(col) { case descpb.SystemColumnKind_MVCCTIMESTAMP: table.timestampOutputIdx = idx rf.mvccDecodeStrategy = row.MVCCDecodingRequired + table.neededValueColsByIdx.Remove(idx) case descpb.SystemColumnKind_TABLEOID: table.oidOutputIdx = idx + table.neededValueColsByIdx.Remove(idx) } - } else { - table.notNeededColOrdinals = append(table.notNeededColOrdinals, idx) } } - sort.Ints(table.neededColsList) - sort.Ints(table.notNeededColOrdinals) table.knownPrefixLength = len(rowenc.MakeIndexKeyPrefix(codec, table.desc, table.index.GetID())) @@ -467,19 +446,6 @@ func (rf *cFetcher) Init( compositeColumnIDs.Add(int(id)) } - table.neededValueColsByIdx = tableArgs.ValNeededForCol.Copy() - - // If system columns are requested, they are present in ValNeededForCol. - // However, we don't want to include them in neededValueColsByIdx, because - // the handling of system columns is separate from the standard value - // decoding process. - if table.timestampOutputIdx != noOutputColumn { - table.neededValueColsByIdx.Remove(table.timestampOutputIdx) - } - if table.oidOutputIdx != noOutputColumn { - table.neededValueColsByIdx.Remove(table.oidOutputIdx) - } - nIndexCols := len(indexColumnIDs) if cap(table.indexColOrdinals) >= nIndexCols { table.indexColOrdinals = table.indexColOrdinals[:nIndexCols] @@ -491,7 +457,7 @@ func (rf *cFetcher) Init( needToDecodeDecimalKey := false for i, id := range indexColumnIDs { colIdx, ok := tableArgs.ColIdxMap.Get(id) - if (ok && neededCols.Contains(int(id))) || rf.traceKV { + if ok { //gcassert:bce indexColOrdinals[i] = colIdx rf.mustDecodeIndexKey = true @@ -506,9 +472,6 @@ func (rf *cFetcher) Init( } else { //gcassert:bce indexColOrdinals[i] = -1 - if neededCols.Contains(int(id)) { - return errors.AssertionFailedf("needed column %d not in colIdxMap", id) - } } } if needToDecodeDecimalKey && cap(rf.scratch) < 64 { @@ -522,10 +485,13 @@ func (rf *cFetcher) Init( if table.index.GetType() == descpb.IndexDescriptor_INVERTED { id := table.index.InvertedColumnID() colIdx, ok := tableArgs.ColIdxMap.Get(id) - if ok && neededCols.Contains(int(id)) { + if ok { table.invertedColOrdinal = colIdx - } else if neededCols.Contains(int(id)) { - return errors.AssertionFailedf("needed column %d not in colIdxMap", id) + // TODO(yuzefovich): for some reason the setup of ColBatchScan + // sometimes doesn't find the inverted column, so we have to be a + // bit tricky here and overwrite the type to what we need for the + // inverted column. Figure it out. + table.typs[colIdx] = types.Bytes } } // Unique secondary indexes contain the extra column IDs as part of @@ -535,7 +501,7 @@ func (rf *cFetcher) Init( for i := 0; i < table.index.NumKeySuffixColumns(); i++ { id := table.index.GetKeySuffixColumnID(i) colIdx, ok := tableArgs.ColIdxMap.Get(id) - if ok && neededCols.Contains(int(id)) { + if ok { if compositeColumnIDs.Contains(int(id)) { table.compositeIndexColOrdinals.Add(colIdx) table.neededValueColsByIdx.Remove(colIdx) @@ -544,19 +510,6 @@ func (rf *cFetcher) Init( } } - if table.isSecondaryIndex { - colIDs := table.index.CollectKeyColumnIDs() - colIDs.UnionWith(table.index.CollectSecondaryStoredColumnIDs()) - colIDs.UnionWith(table.index.CollectKeySuffixColumnIDs()) - for i := range colDescriptors { - //gcassert:bce - id := colDescriptors[i].GetID() - if neededCols.Contains(int(id)) && !colIDs.Contains(id) { - return errors.Errorf("requested column %s not in index", colDescriptors[i].GetName()) - } - } - } - // Prepare our index key vals slice. table.keyValTypes = colinfo.GetColumnTypesFromColDescs( colDescriptors, indexColumnIDs, table.keyValTypes, @@ -589,8 +542,8 @@ func (rf *cFetcher) Init( _ = extraValColOrdinals[nExtraColumns-1] for i := 0; i < nExtraColumns; i++ { id := table.index.GetKeySuffixColumnID(i) - idx := tableArgs.ColIdxMap.GetDefault(id) - if neededCols.Contains(int(id)) || rf.traceKV { + idx, ok := tableArgs.ColIdxMap.Get(id) + if ok { //gcassert:bce extraValColOrdinals[i] = idx } else { @@ -619,7 +572,7 @@ func (rf *cFetcher) Init( }) rf.table = table - rf.accountingHelper.Init(allocator, rf.table.typs, rf.table.notNeededColOrdinals) + rf.accountingHelper.Init(allocator, rf.table.typs) return nil } @@ -1109,11 +1062,10 @@ func (rf *cFetcher) processValue(ctx context.Context, familyID descpb.FamilyID) prettyKey = buf.String() } - if len(table.neededColsList) == 0 { - // We don't need to decode any values. - if rf.traceKV { - prettyValue = tree.DNull.String() - } + if len(table.cols) == 0 { + // We don't need to decode any values. Note that this branch can only be + // executed if the tracing is disabled (if it was enabled, we would + // decode values from all columns). return nil } @@ -1237,44 +1189,30 @@ func (rf *cFetcher) processValueSingle( return "", "", errors.Errorf("single entry value with no default column id") } - var needDecode bool - if rf.traceKV { - needDecode = true - } else { - for i := range table.neededColsList { - if table.neededColsList[i] == int(colID) { - needDecode = true - break - } + if idx, ok := table.ColIdxMap.Get(colID); ok { + if rf.traceKV { + prettyKey = fmt.Sprintf("%s/%s", prettyKey, table.desc.DeletableColumns()[idx].GetName()) } - } - - if needDecode { - if idx, ok := table.colIdxMap.get(colID); ok { - if rf.traceKV { - prettyKey = fmt.Sprintf("%s/%s", prettyKey, table.desc.DeletableColumns()[idx].GetName()) - } - val := rf.machine.nextKV.Value - if len(val.RawBytes) == 0 { - return prettyKey, "", nil - } - typ := rf.table.typs[idx] - err := colencoding.UnmarshalColumnValueToCol( - &table.da, rf.machine.colvecs[idx], rf.machine.rowIdx, typ, val, - ) - if err != nil { - return "", "", err - } - rf.machine.remainingValueColsByIdx.Remove(idx) + val := rf.machine.nextKV.Value + if len(val.RawBytes) == 0 { + return prettyKey, "", nil + } + typ := rf.table.typs[idx] + err := colencoding.UnmarshalColumnValueToCol( + &table.da, rf.machine.colvecs[idx], rf.machine.rowIdx, typ, val, + ) + if err != nil { + return "", "", err + } + rf.machine.remainingValueColsByIdx.Remove(idx) - if rf.traceKV { - prettyValue = rf.getDatumAt(idx, rf.machine.rowIdx).String() - } - if row.DebugRowFetch { - log.Infof(ctx, "Scan %s -> %v", rf.machine.nextKV.Key, "?") - } - return prettyKey, prettyValue, nil + if rf.traceKV { + prettyValue = rf.getDatumAt(idx, rf.machine.rowIdx).String() + } + if row.DebugRowFetch { + log.Infof(ctx, "Scan %s -> %v", rf.machine.nextKV.Key, "?") } + return prettyKey, prettyValue, nil } // No need to unmarshal the column value. Either the column was part of @@ -1305,13 +1243,14 @@ func (rf *cFetcher) processValueBytes( }) var ( - colIDDiff uint32 - lastColID descpb.ColumnID - dataOffset int - typ encoding.Type - lastColIDIndex int - lastNeededColIndex int + colIDDiff uint32 + lastColID descpb.ColumnID + dataOffset int + typ encoding.Type + lastColIDIndex int ) + // Continue reading data until there's none left or we've finished + // populating the data for all of the requested columns. for len(valueBytes) > 0 && rf.machine.remainingValueColsByIdx.Len() > 0 { _, dataOffset, colIDDiff, typ, err = encoding.DecodeValueTag(valueBytes) if err != nil { @@ -1319,17 +1258,20 @@ func (rf *cFetcher) processValueBytes( } colID := lastColID + descpb.ColumnID(colIDDiff) lastColID = colID - var colIsNeeded bool - for ; lastNeededColIndex < len(table.neededColsList); lastNeededColIndex++ { - nextNeededColID := table.neededColsList[lastNeededColIndex] - if nextNeededColID == int(colID) { - colIsNeeded = true + idx := -1 + // Find the ordinal into table.cols for the column ID we just decoded, + // by advancing through the sorted list of needed value columns until + // there's a match, or we passed the column ID we're looking for. + for ; lastColIDIndex < len(table.orderedColIdxMap.vals); lastColIDIndex++ { + nextID := table.orderedColIdxMap.vals[lastColIDIndex] + if nextID == colID { + idx = table.orderedColIdxMap.ords[lastColIDIndex] break - } else if nextNeededColID > int(colID) { + } else if nextID > colID { break } } - if !colIsNeeded { + if idx == -1 { // This column wasn't requested, so read its length and skip it. len, err := encoding.PeekValueLengthWithOffsetsAndType(valueBytes, dataOffset, typ) if err != nil { @@ -1341,16 +1283,6 @@ func (rf *cFetcher) processValueBytes( } continue } - idx := -1 - for ; lastColIDIndex < len(table.colIdxMap.vals); lastColIDIndex++ { - if table.colIdxMap.vals[lastColIDIndex] == colID { - idx = table.colIdxMap.ords[lastColIDIndex] - break - } - } - if idx == -1 { - return "", "", errors.Errorf("missing colid %d", colID) - } if rf.traceKV { prettyKey = fmt.Sprintf("%s/%s", prettyKey, table.desc.DeletableColumns()[idx].GetName()) @@ -1410,12 +1342,6 @@ func (rf *cFetcher) fillNulls() error { } func (rf *cFetcher) finalizeBatch() { - // We need to set all values in "not needed" vectors to nulls because if the - // batch is materialized (i.e. values are converted to datums), the - // conversion of unset values might encounter an error. - for _, notNeededIdx := range rf.table.notNeededColOrdinals { - rf.machine.colvecs[notNeededIdx].Nulls().SetNulls() - } rf.machine.batch.SetLength(rf.machine.rowIdx) rf.machine.rowIdx = 0 } diff --git a/pkg/sql/colfetcher/cfetcher_setup.go b/pkg/sql/colfetcher/cfetcher_setup.go new file mode 100644 index 000000000000..fcb91ecf9bf2 --- /dev/null +++ b/pkg/sql/colfetcher/cfetcher_setup.go @@ -0,0 +1,336 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package colfetcher + +import ( + "context" + "sync" + + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" + "github.com/cockroachdb/cockroach/pkg/sql/execinfra" + "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" + "github.com/cockroachdb/cockroach/pkg/sql/physicalplan" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util" +) + +// cFetcherTableArgs describes the information about the index we're fetching +// from. Note that only columns that need to be fetched (i.e. requested by the +// caller) are included in the internal state. +type cFetcherTableArgs struct { + desc catalog.TableDescriptor + index catalog.Index + // ColIdxMap is a mapping from ColumnID of each column to its ordinal. Only + // needed columns are present. + ColIdxMap catalog.TableColMap + isSecondaryIndex bool + // cols are all needed columns of the table that are present in the index. + // The system columns, if requested, are at the end of cols. + cols []catalog.Column + // typs are the types of only needed columns from the table. + typs []*types.T +} + +var cFetcherTableArgsPool = sync.Pool{ + New: func() interface{} { + return &cFetcherTableArgs{} + }, +} + +func (a *cFetcherTableArgs) Release() { + // Deeply reset the column descriptors. + for i := range a.cols { + a.cols[i] = nil + } + *a = cFetcherTableArgs{ + cols: a.cols[:0], + // The types are small objects, so we don't bother deeply resetting this + // slice. + typs: a.typs[:0], + } + cFetcherTableArgsPool.Put(a) +} + +func (a *cFetcherTableArgs) populateTypes(cols []catalog.Column) { + if cap(a.typs) < len(cols) { + a.typs = make([]*types.T, len(cols)) + } else { + a.typs = a.typs[:len(cols)] + } + for i := range cols { + a.typs[i] = cols[i].GetType() + } +} + +// populateTableArgs fills all fields of the cFetcherTableArgs except for +// ColIdxMap. Note that all columns accessible from the index (i.e. present in +// the key or value part) will be included in the result. In order to prune +// the unnecessary columns away, use keepOnlyNeededColumns. +// +// If index is a secondary index, then all inaccessible columns are pruned away. +// In such a scenario a non-nil idxMap is returned that allows to remap ordinals +// referring to columns from the whole table to the correct positions among only +// accessible columns. post will be adjusted automatically. Columns that are +// not accessible from the secondary index have an undefined value corresponding +// to them if idxMap is non-nil. +// +// For example, say the table has 4 columns (@1, @2, @3, @4), but only 2 columns +// are present in the index we're reading from (@3, @1). In this case, the +// returned table args only contains columns (@1, @3) and we get an index map as +// idxMap = [0, x, 1, x] (where 'x' indicates an undefined value). +// Note that although @3 appears earlier than @1 in the index, because we +// iterate over all columns of the table according to their column ordinals, we +// will see @1 first, so it gets the 0th slot, and @3 second, so it gets the 1st +// slot. +func populateTableArgs( + ctx context.Context, + flowCtx *execinfra.FlowCtx, + evalCtx *tree.EvalContext, + table catalog.TableDescriptor, + index catalog.Index, + invertedCol catalog.Column, + visibility execinfrapb.ScanVisibility, + hasSystemColumns bool, + post *execinfrapb.PostProcessSpec, + helper *colexecargs.ExprHelper, +) (_ *cFetcherTableArgs, idxMap []int, _ error) { + args := cFetcherTableArgsPool.Get().(*cFetcherTableArgs) + // First, find all columns present in the table and possibly include the + // system columns (when requested). + cols := args.cols[:0] + if visibility == execinfra.ScanVisibilityPublicAndNotPublic { + cols = append(cols, table.ReadableColumns()...) + } else { + cols = append(cols, table.PublicColumns()...) + } + if invertedCol != nil { + for i, col := range cols { + if col.GetID() == invertedCol.GetID() { + cols[i] = invertedCol + break + } + } + } + numSystemCols := 0 + if hasSystemColumns { + systemCols := table.SystemColumns() + numSystemCols = len(systemCols) + cols = append(cols, systemCols...) + } + + if !index.Primary() { + // If we have a secondary index, not all columns might be available from + // the index, so we'll prune the unavailable columns away. + colIDs := index.CollectKeyColumnIDs() + colIDs.UnionWith(index.CollectSecondaryStoredColumnIDs()) + colIDs.UnionWith(index.CollectKeySuffixColumnIDs()) + if colIDs.Len() < len(cols)-numSystemCols { + needTypesBeforeRemapping := post.RenderExprs != nil + if needTypesBeforeRemapping { + args.populateTypes(cols) + } + idxMap = make([]int, len(cols)) + colIdx := 0 + for i := range cols { + //gcassert:bce + id := cols[i].GetID() + if colIDs.Contains(id) || (hasSystemColumns && i >= len(cols)-numSystemCols) { + idxMap[i] = colIdx + cols[colIdx] = cols[i] + colIdx++ + } + } + cols = cols[:colIdx] + if err := remapPostProcessSpec( + post, idxMap, helper, evalCtx, args.typs, flowCtx.PreserveFlowSpecs, + ); err != nil { + return nil, nil, err + } + } + } + + *args = cFetcherTableArgs{ + desc: table, + index: index, + isSecondaryIndex: !index.Primary(), + cols: cols, + typs: args.typs, + } + args.populateTypes(cols) + + // Before we can safely use types from the table descriptor, we need to + // make sure they are hydrated. In row execution engine it is done during + // the processor initialization, but neither ColBatchScan nor cFetcher are + // processors, so we need to do the hydration ourselves. + resolver := flowCtx.TypeResolverFactory.NewTypeResolver(evalCtx.Txn) + return args, idxMap, resolver.HydrateTypeSlice(ctx, args.typs) +} + +// keepOnlyNeededColumns updates the tableArgs to prune all unnecessary columns +// away based on neededColumns slice. If we're reading of the secondary index +// that is not covering all columns, idxMap must be non-nil describing the +// remapping that needs to be used for column ordinals from neededColumns. +// post is updated accordingly to refer to new ordinals of columns. The method +// also populates tableArgs.ColIdxMap. +// +// If traceKV is true, then all columns are considered as needed, and +// neededColumns is ignored. +func keepOnlyNeededColumns( + evalCtx *tree.EvalContext, + tableArgs *cFetcherTableArgs, + idxMap []int, + neededColumns []uint32, + post *execinfrapb.PostProcessSpec, + helper *colexecargs.ExprHelper, + traceKV bool, + preserveFlowSpecs bool, +) error { + if !traceKV && len(neededColumns) < len(tableArgs.cols) { + // If the tracing is not enabled and we don't need all of the available + // columns, we will prune all of the not needed columns away. + + // First, populate a set of needed columns. + var neededColumnsSet util.FastIntSet + for _, neededColumn := range neededColumns { + neededColIdx := int(neededColumn) + if idxMap != nil { + neededColIdx = idxMap[neededColIdx] + } + neededColumnsSet.Add(neededColIdx) + } + + // When idxMap is non-nil, we can reuse that. Note that in this case + // the length of idxMap is equal to the number of columns in the + // whole table, and we are reading from the secondary index, so the + // slice will have the sufficient size. We also don't need to reset + // it since we'll update the needed positions below. + if idxMap == nil { + idxMap = make([]int, len(tableArgs.typs)) + } + + // Iterate over all needed columns, populate the idxMap, and adjust + // the post-processing spec to refer only to the needed columns + // directly. + // + // If non-nil idxMap was passed into this method, we have to update it + // by essentially applying a projection on top of the already present + // projection. Consider the following example: + // idxMap = [0, x, 1, x] (where 'x' indicates an undefined value) + // and + // neededColumns = [2]. + // Such a setup means that only columns with ordinals @1 and @3 are + // present in the secondary index while only @3 is actually needed. + // Above, we have already remapped neededColIdx = 2 to be 1, so now + // neededColumnsSet only contains 1. The post-processing already refers + // to this column as having index 1. + // However, since we are pruning the column with index 0 away, the + // post-processing stage will see a single column. Thus, we have to + // update the index map to be + // idxMap = [x, 0, x, x] + // and then remap the post-processing spec below so that it refers to + // the single needed column with the correct ordinal. + neededColIdx := 0 + for idx, ok := neededColumnsSet.Next(0); ok; idx, ok = neededColumnsSet.Next(idx + 1) { + idxMap[idx] = neededColIdx + neededColIdx++ + } + if err := remapPostProcessSpec( + post, idxMap, helper, evalCtx, tableArgs.typs, preserveFlowSpecs, + ); err != nil { + return err + } + + // Now we have to actually prune out the unnecessary columns. + neededColIdx = 0 + for idx, ok := neededColumnsSet.Next(0); ok; idx, ok = neededColumnsSet.Next(idx + 1) { + tableArgs.cols[neededColIdx] = tableArgs.cols[idx] + tableArgs.typs[neededColIdx] = tableArgs.typs[idx] + neededColIdx++ + } + tableArgs.cols = tableArgs.cols[:neededColIdx] + tableArgs.typs = tableArgs.typs[:neededColIdx] + } + + // Populate the ColIdxMap. + for i := range tableArgs.cols { + tableArgs.ColIdxMap.Set(tableArgs.cols[i].GetID(), i) + } + return nil +} + +// remapPostProcessSpec updates post so that all IndexedVars refer to the new +// ordinals according to idxMap. +// +// For example, say we have idxMap = [0, 0, 1, 2, 0, 0] and a render expression +// like '(@1 + @4) / @3`, then it'll be updated into '(@1 + @3) / @2'. Such an +// idxMap indicates that the table has 6 columns and only 3 of them (0th, 2nd, +// 3rd) are needed. +// +// typsBeforeRemapping need to contain all the types of columns before the +// mapping of idxMap was applied. These will only be used if post.RenderExprs is +// not nil. +// +// If preserveFlowSpecs is true, then this method updates post to store the +// original output columns or render expressions. Notably, in order to not +// corrupt the flow specs that have been scheduled to run on the remote nodes, +// this method will allocate fresh slices instead of updating the old slices in +// place (the flow specs for the remote nodes have shallow copies of this +// PostProcessSpec). +// NB: it is ok that we're modifying the specs - we are in the flow setup path +// which occurs **after** we have sent out SetupFlowRequest RPCs. In other +// words, every node must have gotten the unmodified version of the spec and is +// now free to modify it as it pleases. +func remapPostProcessSpec( + post *execinfrapb.PostProcessSpec, + idxMap []int, + helper *colexecargs.ExprHelper, + evalCtx *tree.EvalContext, + typsBeforeRemapping []*types.T, + preserveFlowSpecs bool, +) error { + if post.Projection { + outputColumns := post.OutputColumns + if preserveFlowSpecs && post.OriginalOutputColumns == nil { + // This is the first time we're modifying this PostProcessSpec, but + // we've been asked to preserve the specs, so we have to set the + // original output columns. We are also careful to allocate a new + // slice to populate the updated projection. + post.OriginalOutputColumns = outputColumns + post.OutputColumns = make([]uint32, len(outputColumns)) + } + for i, colIdx := range outputColumns { + post.OutputColumns[i] = uint32(idxMap[colIdx]) + } + } else if post.RenderExprs != nil { + renderExprs := post.RenderExprs + if preserveFlowSpecs && post.OriginalRenderExprs == nil { + // This is the first time we're modifying this PostProcessSpec, but + // we've been asked to preserve the specs, so we have to set the + // original render expressions. We are also careful to allocate a + // new slice to populate the updated render expressions. + post.OriginalRenderExprs = renderExprs + post.RenderExprs = make([]execinfrapb.Expression, len(renderExprs)) + } + var err error + for i := range renderExprs { + // Make sure that the render expression is deserialized if we + // are on the remote node. + post.RenderExprs[i].LocalExpr, err = helper.ProcessExpr(renderExprs[i], evalCtx, typsBeforeRemapping) + if err != nil { + return err + } + post.RenderExprs[i].LocalExpr = physicalplan.RemapIVarsInTypedExpr(renderExprs[i].LocalExpr, idxMap) + } + } + return nil +} diff --git a/pkg/sql/colfetcher/colbatch_scan.go b/pkg/sql/colfetcher/colbatch_scan.go index ecf2265d415a..c78717cbe003 100644 --- a/pkg/sql/colfetcher/colbatch_scan.go +++ b/pkg/sql/colfetcher/colbatch_scan.go @@ -17,8 +17,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/col/coldata" "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" + "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/colexecop" "github.com/cockroachdb/cockroach/pkg/sql/colmem" @@ -27,7 +27,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/rowinfra" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/types" - "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/mon" "github.com/cockroachdb/cockroach/pkg/util/syncutil" "github.com/cockroachdb/cockroach/pkg/util/tracing" @@ -188,6 +187,7 @@ func NewColBatchScan( kvFetcherMemAcc *mon.BoundAccount, flowCtx *execinfra.FlowCtx, evalCtx *tree.EvalContext, + helper *colexecargs.ExprHelper, spec *execinfrapb.TableReaderSpec, post *execinfrapb.PostProcessSpec, estimatedRowCount uint64, @@ -208,16 +208,18 @@ func NewColBatchScan( // retrieving the hydrated immutable from cache. table := spec.BuildTableDescriptor() invertedColumn := tabledesc.FindInvertedColumn(table, spec.InvertedColumn) - tableArgs, err := populateTableArgs( + tableArgs, idxMap, err := populateTableArgs( ctx, flowCtx, evalCtx, table, table.ActiveIndexes()[spec.IndexIdx], - invertedColumn, spec.Visibility, spec.HasSystemColumns, + invertedColumn, spec.Visibility, spec.HasSystemColumns, post, helper, ) if err != nil { return nil, err } - for _, neededColumn := range spec.NeededColumns { - tableArgs.ValNeededForCol.Add(int(neededColumn)) + if err = keepOnlyNeededColumns( + evalCtx, tableArgs, idxMap, spec.NeededColumns, post, helper, flowCtx.TraceKV, flowCtx.PreserveFlowSpecs, + ); err != nil { + return nil, err } fetcher := cFetcherPool.Get().(*cFetcher) @@ -231,7 +233,7 @@ func NewColBatchScan( flowCtx.TraceKV, } - if err = fetcher.Init(flowCtx.Codec(), allocator, kvFetcherMemAcc, tableArgs); err != nil { + if err = fetcher.Init(flowCtx.Codec(), allocator, kvFetcherMemAcc, tableArgs, spec.HasSystemColumns); err != nil { fetcher.Release() return nil, err } @@ -293,95 +295,6 @@ func NewColBatchScan( return s, nil } -type cFetcherTableArgs struct { - desc catalog.TableDescriptor - index catalog.Index - // ColIdxMap is a mapping from ColumnID of each column to its ordinal. - ColIdxMap catalog.TableColMap - isSecondaryIndex bool - // cols are all columns of the table. - cols []catalog.Column - // The indexes (0 to # of columns - 1) of the columns to return. - ValNeededForCol util.FastIntSet - // typs are types of all columns of the table. - typs []*types.T -} - -var cFetcherTableArgsPool = sync.Pool{ - New: func() interface{} { - return &cFetcherTableArgs{} - }, -} - -func (a *cFetcherTableArgs) Release() { - oldCols := a.cols - for i := range oldCols { - oldCols[i] = nil - } - *a = cFetcherTableArgs{ - // The types are small objects, so we don't bother deeply resetting this - // slice. - typs: a.typs[:0], - } - a.cols = oldCols[:0] - cFetcherTableArgsPool.Put(a) -} - -// populateTableArgs fills all fields of the cFetcherTableArgs except for -// ValNeededForCol. -func populateTableArgs( - ctx context.Context, - flowCtx *execinfra.FlowCtx, - evalCtx *tree.EvalContext, - table catalog.TableDescriptor, - index catalog.Index, - invertedCol catalog.Column, - visibility execinfrapb.ScanVisibility, - hasSystemColumns bool, -) (*cFetcherTableArgs, error) { - args := cFetcherTableArgsPool.Get().(*cFetcherTableArgs) - cols := args.cols[:0] - if visibility == execinfra.ScanVisibilityPublicAndNotPublic { - cols = append(cols, table.ReadableColumns()...) - } else { - cols = append(cols, table.PublicColumns()...) - } - if invertedCol != nil { - for i, col := range cols { - if col.GetID() == invertedCol.GetID() { - cols[i] = invertedCol - break - } - } - } - if hasSystemColumns { - cols = append(cols, table.SystemColumns()...) - } - - *args = cFetcherTableArgs{ - desc: table, - index: index, - ColIdxMap: catalog.ColumnIDToOrdinalMap(cols), - isSecondaryIndex: !index.Primary(), - cols: cols, - } - if cap(args.typs) < len(cols) { - args.typs = make([]*types.T, len(cols)) - } else { - args.typs = args.typs[:len(cols)] - } - for i := range cols { - args.typs[i] = cols[i].GetType() - } - - // Before we can safely use types from the table descriptor, we need to - // make sure they are hydrated. In row execution engine it is done during - // the processor initialization, but neither ColBatchScan nor cFetcher are - // processors, so we need to do the hydration ourselves. - resolver := flowCtx.TypeResolverFactory.NewTypeResolver(evalCtx.Txn) - return args, resolver.HydrateTypeSlice(ctx, args.typs) -} - // Release implements the execinfra.Releasable interface. func (s *ColBatchScan) Release() { s.rf.Release() diff --git a/pkg/sql/colfetcher/index_join.go b/pkg/sql/colfetcher/index_join.go index f4a0c951b226..d42263a43074 100644 --- a/pkg/sql/colfetcher/index_join.go +++ b/pkg/sql/colfetcher/index_join.go @@ -415,25 +415,43 @@ func NewColIndexJoin( // retrieving the hydrated immutable from cache. table := spec.BuildTableDescriptor() index := table.ActiveIndexes()[spec.IndexIdx] - tableArgs, err := populateTableArgs( - ctx, flowCtx, evalCtx, table, index, - nil /* invertedCol */, spec.Visibility, spec.HasSystemColumns, + tableArgs, idxMap, err := populateTableArgs( + ctx, flowCtx, evalCtx, table, index, nil, /* invertedCol */ + spec.Visibility, spec.HasSystemColumns, post, helper, ) if err != nil { return nil, err } + if idxMap != nil { + // The index join is fetching from the primary index, so there should be + // no mapping needed. + return nil, errors.AssertionFailedf("unexpectedly non-nil idx map for the index join") + } // Retrieve the set of columns that the index join needs to fetch. + var neededColumns []uint32 + var neededColOrdsInWholeTable util.FastIntSet if post.OutputColumns != nil { - for _, neededColumn := range post.OutputColumns { - tableArgs.ValNeededForCol.Add(int(neededColumn)) + neededColumns = post.OutputColumns + for _, neededColOrd := range neededColumns { + neededColOrdsInWholeTable.Add(int(neededColOrd)) } } else { proc := &execinfra.ProcOutputHelper{} if err = proc.Init(post, tableArgs.typs, helper.SemaCtx, evalCtx); err != nil { return nil, err } - tableArgs.ValNeededForCol = proc.NeededColumns() + neededColOrdsInWholeTable = proc.NeededColumns() + neededColumns = make([]uint32, 0, neededColOrdsInWholeTable.Len()) + for i, ok := neededColOrdsInWholeTable.Next(0); ok; i, ok = neededColOrdsInWholeTable.Next(i + 1) { + neededColumns = append(neededColumns, uint32(i)) + } + } + + if err = keepOnlyNeededColumns( + evalCtx, tableArgs, idxMap, neededColumns, post, helper, flowCtx.TraceKV, flowCtx.PreserveFlowSpecs, + ); err != nil { + return nil, err } fetcher := cFetcherPool.Get().(*cFetcher) @@ -448,13 +466,13 @@ func NewColIndexJoin( false, /* reverse */ flowCtx.TraceKV, } - if err = fetcher.Init(flowCtx.Codec(), fetcherAllocator, kvFetcherMemAcc, tableArgs); err != nil { + if err = fetcher.Init(flowCtx.Codec(), fetcherAllocator, kvFetcherMemAcc, tableArgs, spec.HasSystemColumns); err != nil { fetcher.Release() return nil, err } spanAssembler := colexecspan.NewColSpanAssembler( - flowCtx.Codec(), allocator, table, index, inputTypes, tableArgs.ValNeededForCol, + flowCtx.Codec(), allocator, table, index, inputTypes, neededColOrdsInWholeTable, ) op := &ColIndexJoin{ diff --git a/pkg/sql/colmem/allocator.go b/pkg/sql/colmem/allocator.go index 34b6ceda7c01..747ea4f26ca6 100644 --- a/pkg/sql/colmem/allocator.go +++ b/pkg/sql/colmem/allocator.go @@ -549,19 +549,11 @@ type SetAccountingHelper struct { } // Init initializes the helper. -// - notNeededVecIdxs specifies the indices into typs the corresponding vectors -// for which will not be set to a non-null value. notNeededVecIdxs must be -// sorted. -func (h *SetAccountingHelper) Init(allocator *Allocator, typs []*types.T, notNeededVecIdxs []int) { +func (h *SetAccountingHelper) Init(allocator *Allocator, typs []*types.T) { h.Allocator = allocator - curNotNeededPos := 0 numDecimalVecs := 0 for vecIdx, typ := range typs { - if len(notNeededVecIdxs) > curNotNeededPos && vecIdx == notNeededVecIdxs[curNotNeededPos] { - curNotNeededPos++ - continue - } switch typeconv.TypeFamilyToCanonicalTypeFamily(typ.Family()) { case types.BytesFamily, types.JsonFamily: h.bytesLikeVecIdxs.Add(vecIdx) diff --git a/pkg/sql/colmem/allocator_test.go b/pkg/sql/colmem/allocator_test.go index 6e790caa43b9..874e0ff3e44f 100644 --- a/pkg/sql/colmem/allocator_test.go +++ b/pkg/sql/colmem/allocator_test.go @@ -277,11 +277,7 @@ func TestSetAccountingHelper(t *testing.T) { } var helper colmem.SetAccountingHelper - // We don't use notNeededVecIdxs because it is difficult to calculate - // expected value for the memory used (the vectors are appropriately - // allocated when creating a new batch but then aren't modified - and, thus, - // ignored by the helper. - helper.Init(testAllocator, typs, nil /* notNeededVecIdxs */) + helper.Init(testAllocator, typs) numIterations := rng.Intn(10) + 1 numRows := rng.Intn(coldata.BatchSize()) + 1 diff --git a/pkg/sql/distsql/server.go b/pkg/sql/distsql/server.go index 7679e4f84f10..cdfa98c3f984 100644 --- a/pkg/sql/distsql/server.go +++ b/pkg/sql/distsql/server.go @@ -465,6 +465,7 @@ func (ds *ServerImpl) newFlowContext( DiskMonitor: execinfra.NewMonitor( ctx, ds.ParentDiskMonitor, "flow-disk-monitor", ), + PreserveFlowSpecs: localState.PreserveFlowSpecs, } if localState.IsLocal && localState.Collection != nil { @@ -532,6 +533,10 @@ type LocalState struct { // LocalProcs is an array of planNodeToRowSource processors. It's in order and // will be indexed into by the RowSourceIdx field in LocalPlanNodeSpec. LocalProcs []execinfra.LocalProcessor + + // PreserveFlowSpecs is true when the flow setup code needs to be careful + // when modifying the specifications of processors. + PreserveFlowSpecs bool } // MustUseLeafTxn returns true if a LeafTxn must be used. It is valid to call diff --git a/pkg/sql/distsql_running.go b/pkg/sql/distsql_running.go index 7a632b39f7a2..8db798b40576 100644 --- a/pkg/sql/distsql_running.go +++ b/pkg/sql/distsql_running.go @@ -417,6 +417,9 @@ func (dsp *DistSQLPlanner) Run( localState.EvalContext = &evalCtx.EvalContext localState.Txn = txn localState.LocalProcs = plan.LocalProcessors + // If we need to perform some operation on the flow specs, we want to + // preserve the specs during the flow setup. + localState.PreserveFlowSpecs = planCtx.saveFlows != nil // If we have access to a planner and are currently being used to plan // statements in a user transaction, then take the descs.Collection to resolve // types with during flow execution. This is necessary to do in the case of diff --git a/pkg/sql/execinfra/flow_context.go b/pkg/sql/execinfra/flow_context.go index c8646785f0cf..f653d7f55a48 100644 --- a/pkg/sql/execinfra/flow_context.go +++ b/pkg/sql/execinfra/flow_context.go @@ -81,6 +81,10 @@ type FlowCtx struct { // DiskMonitor is this flow's disk monitor. All disk usage for this flow must // be registered through this monitor. DiskMonitor *mon.BytesMonitor + + // PreserveFlowSpecs is true when the flow setup code needs to be careful + // when modifying the specifications of processors. + PreserveFlowSpecs bool } // NewEvalCtx returns a modifiable copy of the FlowCtx's EvalContext. diff --git a/pkg/sql/execinfrapb/flow_diagram.go b/pkg/sql/execinfrapb/flow_diagram.go index 09955c01630c..5900e114a3ec 100644 --- a/pkg/sql/execinfrapb/flow_diagram.go +++ b/pkg/sql/execinfrapb/flow_diagram.go @@ -445,15 +445,23 @@ func (post *PostProcessSpec) summary() []string { var res []string if post.Projection { outputColumns := "None" - if len(post.OutputColumns) > 0 { - outputColumns = colListStr(post.OutputColumns) + outputCols := post.OutputColumns + if post.OriginalOutputColumns != nil { + outputCols = post.OriginalOutputColumns + } + if len(outputCols) > 0 { + outputColumns = colListStr(outputCols) } res = append(res, fmt.Sprintf("Out: %s", outputColumns)) } - if len(post.RenderExprs) > 0 { + renderExprs := post.RenderExprs + if post.OriginalRenderExprs != nil { + renderExprs = post.OriginalRenderExprs + } + if len(renderExprs) > 0 { var buf bytes.Buffer buf.WriteString("Render: ") - for i, expr := range post.RenderExprs { + for i, expr := range renderExprs { if i > 0 { buf.WriteString(", ") } diff --git a/pkg/sql/execinfrapb/processors_base.pb.go b/pkg/sql/execinfrapb/processors_base.pb.go index 4727455fca17..42a025a730f5 100644 --- a/pkg/sql/execinfrapb/processors_base.pb.go +++ b/pkg/sql/execinfrapb/processors_base.pb.go @@ -43,11 +43,21 @@ type PostProcessSpec struct { // Can only be set if projection is true. Cannot be set at the same time with // render expressions. OutputColumns []uint32 `protobuf:"varint,3,rep,packed,name=output_columns,json=outputColumns" json:"output_columns,omitempty"` + // OriginalOutputColumns will be set if OutputColumns are destructively + // modified during the vectorized flow setup. This field is only used for + // population of the DistSQL diagrams, and if set, it takes precedence over + // OutputColumns. + OriginalOutputColumns []uint32 `protobuf:"varint,7,rep,packed,name=original_output_columns,json=originalOutputColumns" json:"original_output_columns,omitempty"` // If set, the output is the result of rendering these expressions. The // expressions reference the internal columns of the processor. // // Cannot be set at the same time with output columns. RenderExprs []Expression `protobuf:"bytes,4,rep,name=render_exprs,json=renderExprs" json:"render_exprs"` + // OriginalRenderExprs will be set if RenderExprs are destructively + // modified during the vectorized flow setup. This field is only used for + // population of the DistSQL diagrams, and if set, it takes precedence over + // RenderExprs. + OriginalRenderExprs []Expression `protobuf:"bytes,8,rep,name=original_render_exprs,json=originalRenderExprs" json:"original_render_exprs"` // If nonzero, the first rows will be suppressed. Offset uint64 `protobuf:"varint,5,opt,name=offset" json:"offset"` // If nonzero, the processor will stop after emitting this many rows. The rows @@ -164,31 +174,33 @@ func init() { } var fileDescriptor_de929fd066e9cd6d = []byte{ - // 370 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xcf, 0xce, 0xd2, 0x40, - 0x14, 0xc5, 0x3b, 0x50, 0xfe, 0x64, 0x2a, 0x42, 0x26, 0x26, 0x36, 0x0d, 0x19, 0x1b, 0x82, 0xb1, - 0x2e, 0x2c, 0x91, 0x47, 0x40, 0xdd, 0x98, 0x98, 0x10, 0x70, 0xe5, 0x86, 0x0c, 0xd3, 0x01, 0xab, - 0x65, 0x66, 0x98, 0x3b, 0x4d, 0x78, 0x0c, 0x1f, 0x8b, 0x25, 0x4b, 0x56, 0x46, 0xcb, 0x13, 0xf8, - 0x06, 0x06, 0x5a, 0x4c, 0xfd, 0x92, 0x6f, 0x77, 0xf3, 0xbb, 0xe7, 0xf4, 0x9c, 0xdb, 0xc1, 0x2f, - 0x61, 0x9f, 0x4d, 0xc4, 0x41, 0xf0, 0x54, 0x6e, 0x0c, 0xd3, 0xeb, 0x89, 0x36, 0x8a, 0x0b, 0x00, - 0x65, 0x60, 0xb5, 0x66, 0x20, 0x62, 0x6d, 0x94, 0x55, 0xc4, 0xe7, 0x8a, 0x7f, 0x37, 0x8a, 0xf1, - 0xaf, 0x31, 0xec, 0xb3, 0x38, 0x49, 0xc1, 0xc2, 0x3e, 0x33, 0xb9, 0x0c, 0x82, 0x87, 0x1f, 0x48, - 0x98, 0x65, 0xa5, 0x2b, 0x20, 0x37, 0xc7, 0xff, 0xec, 0xd9, 0x56, 0x6d, 0xd5, 0x6d, 0x9c, 0x5c, - 0xa7, 0x92, 0x8e, 0xfe, 0x20, 0xdc, 0x9f, 0x2b, 0xb0, 0xf3, 0x32, 0x7d, 0xa9, 0x05, 0x27, 0x63, - 0x8c, 0xb5, 0x51, 0xdf, 0x04, 0xb7, 0xa9, 0x92, 0x7e, 0x23, 0x44, 0x51, 0x77, 0xe6, 0x1e, 0x7f, - 0xbe, 0x70, 0x16, 0x35, 0x4e, 0x5e, 0xe3, 0xa7, 0x2a, 0xb7, 0x3a, 0xb7, 0x2b, 0xae, 0xb2, 0x7c, - 0x27, 0xc1, 0x6f, 0x86, 0xcd, 0xa8, 0x37, 0x6b, 0x0c, 0xd0, 0xa2, 0x57, 0x6e, 0xde, 0x95, 0x0b, - 0xf2, 0x09, 0x3f, 0x31, 0x42, 0x26, 0xc2, 0xac, 0xc4, 0x41, 0x1b, 0xf0, 0xdd, 0xb0, 0x19, 0x79, - 0xd3, 0x71, 0xfc, 0xd8, 0x6d, 0xf1, 0x87, 0x83, 0x36, 0x02, 0x20, 0x55, 0xb2, 0x0a, 0xf6, 0x4a, - 0xff, 0x95, 0x03, 0x19, 0xe2, 0xb6, 0xda, 0x6c, 0x40, 0x58, 0xbf, 0x15, 0xa2, 0xc8, 0xad, 0x24, - 0x15, 0x23, 0x01, 0x6e, 0x65, 0xe9, 0x2e, 0xb5, 0x7e, 0xbb, 0xb6, 0x2c, 0xd1, 0x47, 0xb7, 0x8b, - 0x06, 0x8d, 0xd1, 0x2b, 0xdc, 0xb9, 0x37, 0x1b, 0xe2, 0xce, 0xbd, 0x3d, 0xfa, 0xd7, 0xfe, 0x8e, - 0x46, 0xef, 0x71, 0xff, 0x33, 0x5b, 0x67, 0x62, 0x21, 0x58, 0x22, 0xcc, 0x52, 0x33, 0x49, 0xde, - 0x62, 0x17, 0x34, 0x93, 0x3e, 0x0a, 0x51, 0xe4, 0x4d, 0x9f, 0xd7, 0x4e, 0xa8, 0x7e, 0x79, 0x7c, - 0x95, 0x55, 0xa9, 0x37, 0xe9, 0xec, 0xcd, 0xf1, 0x37, 0x75, 0x8e, 0x05, 0x45, 0xa7, 0x82, 0xa2, - 0x73, 0x41, 0xd1, 0xaf, 0x82, 0xa2, 0x1f, 0x17, 0xea, 0x9c, 0x2e, 0xd4, 0x39, 0x5f, 0xa8, 0xf3, - 0xc5, 0xab, 0x3d, 0xe3, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x51, 0xd4, 0xc6, 0xca, 0x19, 0x02, - 0x00, 0x00, + // 411 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xc1, 0x6e, 0x13, 0x31, + 0x10, 0x86, 0xd7, 0xc9, 0xb6, 0x89, 0x1c, 0x4a, 0x2b, 0x03, 0xea, 0x2a, 0xaa, 0xcc, 0x2a, 0x2a, + 0x62, 0x39, 0xb0, 0x11, 0x3d, 0x72, 0x0c, 0x70, 0x41, 0x42, 0x54, 0x29, 0x27, 0x0e, 0xac, 0x1c, + 0xc7, 0x09, 0x86, 0xad, 0xc7, 0xf1, 0x38, 0x52, 0x5e, 0x02, 0x89, 0xc7, 0xca, 0xb1, 0xc7, 0x9e, + 0x10, 0x24, 0x2f, 0x82, 0x92, 0xdd, 0x8d, 0x36, 0x91, 0x38, 0x70, 0xb3, 0xbe, 0xf9, 0xff, 0x7f, + 0x3c, 0xa3, 0xa1, 0xcf, 0x70, 0x96, 0xf7, 0xd5, 0x42, 0x49, 0x6d, 0x26, 0x4e, 0xd8, 0x51, 0xdf, + 0x3a, 0x90, 0x0a, 0x11, 0x1c, 0x66, 0x23, 0x81, 0x2a, 0xb5, 0x0e, 0x3c, 0xb0, 0x48, 0x82, 0xfc, + 0xee, 0x40, 0xc8, 0xaf, 0x29, 0xce, 0xf2, 0x74, 0xac, 0xd1, 0xe3, 0x2c, 0x77, 0x73, 0xd3, 0xed, + 0x1e, 0x06, 0x8c, 0x85, 0x17, 0x85, 0xab, 0xcb, 0xb6, 0x8e, 0x7d, 0xf6, 0x78, 0x0a, 0x53, 0xd8, + 0x3e, 0xfb, 0x9b, 0x57, 0x41, 0x7b, 0x3f, 0x9a, 0xf4, 0xf4, 0x1a, 0xd0, 0x5f, 0x17, 0xdd, 0x6f, + 0xac, 0x92, 0xec, 0x92, 0x52, 0xeb, 0xe0, 0x9b, 0x92, 0x5e, 0x83, 0x89, 0x1a, 0x31, 0x49, 0xda, + 0x83, 0x70, 0xf9, 0xeb, 0x69, 0x30, 0xac, 0x71, 0xf6, 0x82, 0x3e, 0x84, 0xb9, 0xb7, 0x73, 0x9f, + 0x49, 0xc8, 0xe7, 0xb7, 0x06, 0xa3, 0x66, 0xdc, 0x4c, 0x4e, 0x06, 0x8d, 0x33, 0x32, 0x3c, 0x29, + 0x2a, 0x6f, 0x8a, 0x02, 0x7b, 0x4d, 0xcf, 0xc1, 0xe9, 0xa9, 0x36, 0x22, 0xcf, 0x0e, 0x3c, 0xad, + 0x9d, 0xe7, 0x49, 0x25, 0xf9, 0xb8, 0xe7, 0xfd, 0x40, 0x1f, 0x38, 0x65, 0xc6, 0xca, 0x65, 0x6a, + 0x61, 0x1d, 0x46, 0x61, 0xdc, 0x4c, 0x3a, 0x57, 0x97, 0xe9, 0xbf, 0xf6, 0x92, 0xbe, 0x5b, 0x58, + 0xa7, 0x10, 0x35, 0x98, 0xf2, 0xd3, 0x9d, 0xc2, 0xbf, 0xe1, 0xc8, 0xbe, 0xd0, 0x5d, 0x9f, 0x6c, + 0x2f, 0xb7, 0xfd, 0xdf, 0xb9, 0x8f, 0xaa, 0xa0, 0x61, 0x2d, 0xff, 0x82, 0x1e, 0xc3, 0x64, 0x82, + 0xca, 0x47, 0x47, 0x31, 0x49, 0xc2, 0x52, 0x5a, 0x32, 0xd6, 0xa5, 0x47, 0xb9, 0xbe, 0xd5, 0x3e, + 0x3a, 0xae, 0x15, 0x0b, 0xf4, 0x3e, 0x6c, 0x93, 0xb3, 0x46, 0xef, 0x39, 0x6d, 0x55, 0x93, 0x5f, + 0xd0, 0x56, 0xb5, 0x25, 0xb2, 0xdb, 0x52, 0x85, 0x7a, 0x6f, 0xe9, 0xe9, 0x27, 0x31, 0xca, 0xd5, + 0x50, 0x89, 0xb1, 0x72, 0x37, 0x56, 0x18, 0xf6, 0x8a, 0x86, 0x68, 0x85, 0x89, 0x48, 0x4c, 0x92, + 0xce, 0xd5, 0x79, 0x6d, 0x94, 0xf2, 0x1c, 0xd2, 0x8d, 0xac, 0xec, 0xba, 0x95, 0x0e, 0x5e, 0x2e, + 0xff, 0xf0, 0x60, 0xb9, 0xe2, 0xe4, 0x6e, 0xc5, 0xc9, 0xfd, 0x8a, 0x93, 0xdf, 0x2b, 0x4e, 0x7e, + 0xae, 0x79, 0x70, 0xb7, 0xe6, 0xc1, 0xfd, 0x9a, 0x07, 0x9f, 0x3b, 0xb5, 0x13, 0xfb, 0x1b, 0x00, + 0x00, 0xff, 0xff, 0x0d, 0xef, 0xdf, 0xb2, 0xb5, 0x02, 0x00, 0x00, } func (m *PostProcessSpec) Marshal() (dAtA []byte, err error) { @@ -211,6 +223,38 @@ func (m *PostProcessSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.OriginalRenderExprs) > 0 { + for iNdEx := len(m.OriginalRenderExprs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.OriginalRenderExprs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProcessorsBase(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.OriginalOutputColumns) > 0 { + dAtA2 := make([]byte, len(m.OriginalOutputColumns)*10) + var j1 int + for _, num := range m.OriginalOutputColumns { + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintProcessorsBase(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x3a + } i = encodeVarintProcessorsBase(dAtA, i, uint64(m.Limit)) i-- dAtA[i] = 0x30 @@ -232,20 +276,20 @@ func (m *PostProcessSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.OutputColumns) > 0 { - dAtA2 := make([]byte, len(m.OutputColumns)*10) - var j1 int + dAtA4 := make([]byte, len(m.OutputColumns)*10) + var j3 int for _, num := range m.OutputColumns { for num >= 1<<7 { - dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j1++ + j3++ } - dAtA2[j1] = uint8(num) - j1++ + dAtA4[j3] = uint8(num) + j3++ } - i -= j1 - copy(dAtA[i:], dAtA2[:j1]) - i = encodeVarintProcessorsBase(dAtA, i, uint64(j1)) + i -= j3 + copy(dAtA[i:], dAtA4[:j3]) + i = encodeVarintProcessorsBase(dAtA, i, uint64(j3)) i-- dAtA[i] = 0x1a } @@ -281,20 +325,20 @@ func (m *Columns) MarshalToSizedBuffer(dAtA []byte) (int, error) { var l int _ = l if len(m.Columns) > 0 { - dAtA4 := make([]byte, len(m.Columns)*10) - var j3 int + dAtA6 := make([]byte, len(m.Columns)*10) + var j5 int for _, num := range m.Columns { for num >= 1<<7 { - dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80) + dAtA6[j5] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j3++ + j5++ } - dAtA4[j3] = uint8(num) - j3++ + dAtA6[j5] = uint8(num) + j5++ } - i -= j3 - copy(dAtA[i:], dAtA4[:j3]) - i = encodeVarintProcessorsBase(dAtA, i, uint64(j3)) + i -= j5 + copy(dAtA[i:], dAtA6[:j5]) + i = encodeVarintProcessorsBase(dAtA, i, uint64(j5)) i-- dAtA[i] = 0xa } @@ -367,6 +411,19 @@ func (m *PostProcessSpec) Size() (n int) { } n += 1 + sovProcessorsBase(uint64(m.Offset)) n += 1 + sovProcessorsBase(uint64(m.Limit)) + if len(m.OriginalOutputColumns) > 0 { + l = 0 + for _, e := range m.OriginalOutputColumns { + l += sovProcessorsBase(uint64(e)) + } + n += 1 + sovProcessorsBase(uint64(l)) + l + } + if len(m.OriginalRenderExprs) > 0 { + for _, e := range m.OriginalRenderExprs { + l = e.Size() + n += 1 + l + sovProcessorsBase(uint64(l)) + } + } return n } @@ -600,6 +657,116 @@ func (m *PostProcessSpec) Unmarshal(dAtA []byte) error { break } } + case 7: + if wireType == 0 { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.OriginalOutputColumns = append(m.OriginalOutputColumns, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthProcessorsBase + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBase + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.OriginalOutputColumns) == 0 { + m.OriginalOutputColumns = make([]uint32, 0, elementCount) + } + for iNdEx < postIndex { + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.OriginalOutputColumns = append(m.OriginalOutputColumns, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field OriginalOutputColumns", wireType) + } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OriginalRenderExprs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProcessorsBase + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBase + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OriginalRenderExprs = append(m.OriginalRenderExprs, Expression{}) + if err := m.OriginalRenderExprs[len(m.OriginalRenderExprs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProcessorsBase(dAtA[iNdEx:]) diff --git a/pkg/sql/execinfrapb/processors_base.proto b/pkg/sql/execinfrapb/processors_base.proto index a11766ad5606..bd7412985f3a 100644 --- a/pkg/sql/execinfrapb/processors_base.proto +++ b/pkg/sql/execinfrapb/processors_base.proto @@ -39,12 +39,22 @@ message PostProcessSpec { // Can only be set if projection is true. Cannot be set at the same time with // render expressions. repeated uint32 output_columns = 3 [packed = true]; + // OriginalOutputColumns will be set if OutputColumns are destructively + // modified during the vectorized flow setup. This field is only used for + // population of the DistSQL diagrams, and if set, it takes precedence over + // OutputColumns. + repeated uint32 original_output_columns = 7 [packed = true]; // If set, the output is the result of rendering these expressions. The // expressions reference the internal columns of the processor. // // Cannot be set at the same time with output columns. repeated Expression render_exprs = 4 [(gogoproto.nullable) = false]; + // OriginalRenderExprs will be set if RenderExprs are destructively + // modified during the vectorized flow setup. This field is only used for + // population of the DistSQL diagrams, and if set, it takes precedence over + // RenderExprs. + repeated Expression original_render_exprs = 8 [(gogoproto.nullable) = false]; // If nonzero, the first rows will be suppressed. optional uint64 offset = 5 [(gogoproto.nullable) = false]; diff --git a/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize b/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize index 9e6a06842f3f..7039aae6ac5d 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize +++ b/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize @@ -139,10 +139,9 @@ EXPLAIN (VEC, VERBOSE) SELECT count(*) FROM kv │ ├ *colexec.invariantsChecker │ │ └ *colexec.countOp │ │ └ *colexec.invariantsChecker -│ │ └ *colexecbase.simpleProjectOp -│ │ └ *colexecutils.CancelChecker -│ │ └ *colexec.invariantsChecker -│ │ └ *colfetcher.ColBatchScan +│ │ └ *colexecutils.CancelChecker +│ │ └ *colexec.invariantsChecker +│ │ └ *colfetcher.ColBatchScan │ ├ *colexec.invariantsChecker │ │ └ *colrpc.Inbox │ ├ *colexec.invariantsChecker @@ -157,40 +156,36 @@ EXPLAIN (VEC, VERBOSE) SELECT count(*) FROM kv │ └ *colexec.invariantsChecker │ └ *colexec.countOp │ └ *colexec.invariantsChecker -│ └ *colexecbase.simpleProjectOp -│ └ *colexecutils.CancelChecker -│ └ *colexec.invariantsChecker -│ └ *colfetcher.ColBatchScan +│ └ *colexecutils.CancelChecker +│ └ *colexec.invariantsChecker +│ └ *colfetcher.ColBatchScan ├ Node 3 │ └ *colrpc.Outbox │ └ *colexecutils.deselectorOp │ └ *colexec.invariantsChecker │ └ *colexec.countOp │ └ *colexec.invariantsChecker -│ └ *colexecbase.simpleProjectOp -│ └ *colexecutils.CancelChecker -│ └ *colexec.invariantsChecker -│ └ *colfetcher.ColBatchScan +│ └ *colexecutils.CancelChecker +│ └ *colexec.invariantsChecker +│ └ *colfetcher.ColBatchScan ├ Node 4 │ └ *colrpc.Outbox │ └ *colexecutils.deselectorOp │ └ *colexec.invariantsChecker │ └ *colexec.countOp │ └ *colexec.invariantsChecker -│ └ *colexecbase.simpleProjectOp -│ └ *colexecutils.CancelChecker -│ └ *colexec.invariantsChecker -│ └ *colfetcher.ColBatchScan +│ └ *colexecutils.CancelChecker +│ └ *colexec.invariantsChecker +│ └ *colfetcher.ColBatchScan └ Node 5 └ *colrpc.Outbox └ *colexecutils.deselectorOp └ *colexec.invariantsChecker └ *colexec.countOp └ *colexec.invariantsChecker - └ *colexecbase.simpleProjectOp - └ *colexecutils.CancelChecker - └ *colexec.invariantsChecker - └ *colfetcher.ColBatchScan + └ *colexecutils.CancelChecker + └ *colexec.invariantsChecker + └ *colfetcher.ColBatchScan query T EXPLAIN (VEC, VERBOSE) SELECT count(*) FROM kv NATURAL INNER HASH JOIN kv kv2 diff --git a/pkg/sql/opt/exec/execbuilder/testdata/window b/pkg/sql/opt/exec/execbuilder/testdata/window index f540c0418c6c..396862171de0 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/window +++ b/pkg/sql/opt/exec/execbuilder/testdata/window @@ -31,21 +31,21 @@ SELECT message FROM [SHOW KV TRACE FOR SESSION] WITH ORDINALITY WHERE message LIKE 'fetched:%' OR message LIKE 'output row%' ORDER BY message LIKE 'fetched:%' DESC, ordinality ASC ---- -fetched: /kv/kv_pkey/1/v -> /2 +fetched: /kv/kv_pkey/1/v/w/f/b -> /2/3/1.0/true fetched: /kv/kv_pkey/1/d -> 1 fetched: /kv/kv_pkey/1/s -> 'a' -fetched: /kv/kv_pkey/3/v -> /4 +fetched: /kv/kv_pkey/3/v/w/f/b -> /4/5/2.0/true fetched: /kv/kv_pkey/3/d -> 8 fetched: /kv/kv_pkey/3/s -> 'a' -fetched: /kv/kv_pkey/5 -> NULL +fetched: /kv/kv_pkey/5/w/f/b -> /5/9.9/false fetched: /kv/kv_pkey/5/d -> -321 -fetched: /kv/kv_pkey/6/v -> /2 +fetched: /kv/kv_pkey/6/v/w/f/b -> /2/3/4.4/true fetched: /kv/kv_pkey/6/d -> 4.4 fetched: /kv/kv_pkey/6/s -> 'b' -fetched: /kv/kv_pkey/7/v -> /2 +fetched: /kv/kv_pkey/7/v/w/f/b -> /2/2/6.0/true fetched: /kv/kv_pkey/7/d -> 7.9 fetched: /kv/kv_pkey/7/s -> 'b' -fetched: /kv/kv_pkey/8/v -> /4 +fetched: /kv/kv_pkey/8/v/w/f/b -> /4/2/3.0/false fetched: /kv/kv_pkey/8/d -> 3 fetched: /kv/kv_pkey/8/s -> 'A' output row: [5 NULL] diff --git a/pkg/sql/physicalplan/aggregator_funcs_test.go b/pkg/sql/physicalplan/aggregator_funcs_test.go index ebd1f7168c22..affb7371f80b 100644 --- a/pkg/sql/physicalplan/aggregator_funcs_test.go +++ b/pkg/sql/physicalplan/aggregator_funcs_test.go @@ -8,7 +8,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -package physicalplan +package physicalplan_test import ( "context" @@ -25,6 +25,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/distsql" "github.com/cockroachdb/cockroach/pkg/sql/execinfra" "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" + "github.com/cockroachdb/cockroach/pkg/sql/physicalplan" "github.com/cockroachdb/cockroach/pkg/sql/randgen" "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" @@ -119,7 +120,7 @@ func checkDistAggregationInfo( colIdx int, numRows int, fn execinfrapb.AggregatorSpec_Func, - info DistAggregationInfo, + info physicalplan.DistAggregationInfo, ) { colType := tableDesc.PublicColumns()[colIdx].GetType() @@ -211,7 +212,7 @@ func checkDistAggregationInfo( t.Fatal(err) } var expr execinfrapb.Expression - expr, err = MakeExpression(renderExpr, nil, nil) + expr, err = physicalplan.MakeExpression(renderExpr, nil, nil) if err != nil { t.Fatal(err) } @@ -314,7 +315,7 @@ func checkDistAggregationInfo( t.Fatal(err) } var expr execinfrapb.Expression - expr, err = MakeExpression(renderExpr, nil, nil) + expr, err = physicalplan.MakeExpression(renderExpr, nil, nil) if err != nil { t.Fatal(err) } @@ -462,7 +463,7 @@ func TestDistAggregationTable(t *testing.T) { kvDB := tc.Server(0).DB() desc := catalogkv.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") - for fn, info := range DistAggregationTable { + for fn, info := range physicalplan.DistAggregationTable { if fn == execinfrapb.AnyNotNull { // ANY_NOT_NULL only has a definite result if all rows have the same value // on the relevant column; skip testing this trivial case. diff --git a/pkg/sql/physicalplan/expression.go b/pkg/sql/physicalplan/expression.go index 5a55b7bf99cf..08bcf71759f6 100644 --- a/pkg/sql/physicalplan/expression.go +++ b/pkg/sql/physicalplan/expression.go @@ -78,7 +78,7 @@ func MakeExpression( if indexVarMap != nil { // Remap our indexed vars. - expr = remapIVarsInTypedExpr(expr, indexVarMap) + expr = RemapIVarsInTypedExpr(expr, indexVarMap) } expression := execinfrapb.Expression{LocalExpr: expr} if ctx.IsLocal() { @@ -123,9 +123,9 @@ func (e *evalAndReplaceSubqueryVisitor) VisitPre(expr tree.Expr) (bool, tree.Exp func (evalAndReplaceSubqueryVisitor) VisitPost(expr tree.Expr) tree.Expr { return expr } -// remapIVarsInTypedExpr remaps tree.IndexedVars in expr using indexVarMap. +// RemapIVarsInTypedExpr remaps tree.IndexedVars in expr using indexVarMap. // Note that a new expression is returned. -func remapIVarsInTypedExpr(expr tree.TypedExpr, indexVarMap []int) tree.TypedExpr { +func RemapIVarsInTypedExpr(expr tree.TypedExpr, indexVarMap []int) tree.TypedExpr { v := &ivarRemapper{indexVarMap: indexVarMap} newExpr, _ := tree.WalkExpr(v, expr) return newExpr.(tree.TypedExpr) From 2a044b8e753402b8fc4c88783e9b7d492649f5d1 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 27 Oct 2021 15:56:52 +1100 Subject: [PATCH 062/205] sql: fix bug where validate constraint checked inaccessible columns Release note (bug fix): Fixed a bug where certain schema changes (e.g. SET NULL) did not work if there was a expression-based index on the table. --- pkg/sql/check.go | 4 +-- .../testdata/logic_test/expression_index | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/pkg/sql/check.go b/pkg/sql/check.go index f89093109182..48022f45c76d 100644 --- a/pkg/sql/check.go +++ b/pkg/sql/check.go @@ -48,7 +48,7 @@ func validateCheckExpr( if err != nil { return err } - colSelectors := tabledesc.ColumnsSelectors(tableDesc.PublicColumns()) + colSelectors := tabledesc.ColumnsSelectors(tableDesc.AccessibleColumns()) columns := tree.AsStringWithFlags(&colSelectors, tree.FmtSerializable) queryStr := fmt.Sprintf(`SELECT %s FROM [%d AS t] WHERE NOT (%s) LIMIT 1`, columns, tableDesc.GetID(), exprStr) log.Infof(ctx, "validating check constraint %q with query %q", expr, queryStr) @@ -60,7 +60,7 @@ func validateCheckExpr( if rows.Len() > 0 { return pgerror.Newf(pgcode.CheckViolation, "validation of CHECK %q failed on row: %s", - expr, labeledRowValues(tableDesc.PublicColumns(), rows)) + expr, labeledRowValues(tableDesc.AccessibleColumns(), rows)) } return nil } diff --git a/pkg/sql/logictest/testdata/logic_test/expression_index b/pkg/sql/logictest/testdata/logic_test/expression_index index 813fc13fbe57..70a8e92ba7c8 100644 --- a/pkg/sql/logictest/testdata/logic_test/expression_index +++ b/pkg/sql/logictest/testdata/logic_test/expression_index @@ -883,3 +883,28 @@ INSERT INTO uniq VALUES (3, 1, 109) ON CONFLICT ((a + b)) DO NOTHING # See https://github.com/cockroachdb/cockroach/issues/67893. statement error syntax error INSERT INTO uniq VALUES (4, 1, 219) ON CONFLICT ((a + b)) DO UPDATE SET b = 90 + +# Regression test for #72012. CHECK CONSTRAINT should not check inaccessible columns. + +statement ok +CREATE TABLE t72012 (col integer); +CREATE INDEX t72012_idx ON t72012 ((abs(col))); +ALTER TABLE t72012 ALTER COLUMN col SET NOT NULL + +query TT +SHOW CREATE TABLE t72012 +---- +t72012 CREATE TABLE public.t72012 ( + col INT8 NOT NULL, + rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(), + CONSTRAINT t72012_pkey PRIMARY KEY (rowid ASC), + INDEX t72012_idx (abs(col) ASC), + FAMILY "primary" (col, rowid) +) + +statement ok +ALTER TABLE t72012 ALTER COLUMN col DROP NOT NULL; +INSERT INTO t72012 VALUES (NULL) + +statement error validation of NOT NULL constraint failed: validation of CHECK "col IS NOT NULL" failed on row: col=NULL, rowid=\d+ +ALTER TABLE t72012 ALTER COLUMN col SET NOT NULL From 75f788155bf7a359ab616165b64ed9562badd4da Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 27 Oct 2021 18:30:51 +1100 Subject: [PATCH 063/205] types: correct atttypmod for DECIMAL with precision but no width Release note (bug fix): Previously, atttypmod in pg_catalog.pg_attributes for DECIMAL types with precision but no width was incorrectly -1. This is now populated correctly. --- pkg/sql/logictest/testdata/logic_test/decimal | 10 ++++++++ pkg/sql/types/types.go | 25 +++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/decimal b/pkg/sql/logictest/testdata/logic_test/decimal index 6c9c0a8b6cfd..be6c597eb1fb 100644 --- a/pkg/sql/logictest/testdata/logic_test/decimal +++ b/pkg/sql/logictest/testdata/logic_test/decimal @@ -574,3 +574,13 @@ Infinity Infinity Infinity -Infinity + +statement ok +CREATE TABLE t71926(no_typmod decimal, precision decimal(5), precision_and_width decimal(5,3)) + +query TI +SELECT attname, atttypmod FROM pg_attribute WHERE attrelid = 't71926'::regclass::oid AND atttypid = 'decimal'::regtype::oid +---- +no_typmod -1 +precision 327684 +precision_and_width 327687 diff --git a/pkg/sql/types/types.go b/pkg/sql/types/types.go index 92942a69e8d6..dc34bf92c9bd 100644 --- a/pkg/sql/types/types.go +++ b/pkg/sql/types/types.go @@ -1191,20 +1191,25 @@ func (t *T) TypeModifier() int32 { if t.Oid() == oid.T_char { return int32(-1) } - if width := t.Width(); width != 0 { - switch t.Family() { - case StringFamily, CollatedStringFamily: + + switch t.Family() { + case StringFamily, CollatedStringFamily: + if width := t.Width(); width != 0 { // Postgres adds 4 to the attypmod for bounded string types, the // var header size. return width + 4 - case BitFamily: + } + case BitFamily: + if width := t.Width(); width != 0 { return width - case DecimalFamily: - // attTypMod is calculated by putting the precision in the upper - // bits and the scale in the lower bits of a 32-bit int, and adding - // 4 (the var header size). We mock this for clients' sake. See - // numeric.c. - return ((t.Precision() << 16) | width) + 4 + } + case DecimalFamily: + // attTypMod is calculated by putting the precision in the upper + // bits and the scale in the lower bits of a 32-bit int, and adding + // 4 (the var header size). We mock this for clients' sake. See + // https://github.com/postgres/postgres/blob/5a2832465fd8984d089e8c44c094e6900d987fcd/src/backend/utils/adt/numeric.c#L1242. + if width, precision := t.Width(), t.Precision(); precision != 0 || width != 0 { + return ((precision << 16) | width) + 4 } } return int32(-1) From d6fa4c82f307bdeb75f8b8d2d8657f626ae867f6 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Wed, 27 Oct 2021 09:58:40 +0100 Subject: [PATCH 064/205] sql: fix bad length test in tests `len` always returns >= 0. Release note: None --- pkg/sql/pgwire/auth_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sql/pgwire/auth_test.go b/pkg/sql/pgwire/auth_test.go index 0366b59af673..bb06c5d36d08 100644 --- a/pkg/sql/pgwire/auth_test.go +++ b/pkg/sql/pgwire/auth_test.go @@ -275,7 +275,7 @@ func hbaRunTest(t *testing.T, insecure bool) { return "ok", err case "authlog": - if len(td.CmdArgs) < 0 { + if len(td.CmdArgs) < 1 { t.Fatal("not enough arguments") } numEntries, err := strconv.Atoi(td.CmdArgs[0].Key) From b23cc0b772e17f7c044692092ae640af7982a301 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Wed, 27 Oct 2021 14:26:35 +0100 Subject: [PATCH 065/205] sql: remove usused err constants in schema changer I couldn't find any use of these when reading through the schema changer code. Release note: None --- pkg/sql/schema_changer.go | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/pkg/sql/schema_changer.go b/pkg/sql/schema_changer.go index 56514a6769e7..49169c4e6b64 100644 --- a/pkg/sql/schema_changer.go +++ b/pkg/sql/schema_changer.go @@ -183,10 +183,6 @@ func IsPermanentSchemaChangeError(err error) bool { if errors.IsAny(err, context.Canceled, context.DeadlineExceeded, - errExistingSchemaChangeLease, - errExpiredSchemaChangeLease, - errNotHitGCTTLDeadline, - errSchemaChangeDuringDrain, errSchemaChangeNotFirstInLine, errTableVersionMismatchSentinel, ) { @@ -210,13 +206,7 @@ func IsPermanentSchemaChangeError(err error) bool { return true } -var ( - errExistingSchemaChangeLease = errors.Newf("an outstanding schema change lease exists") - errExpiredSchemaChangeLease = errors.Newf("the schema change lease has expired") - errSchemaChangeNotFirstInLine = errors.Newf("schema change not first in line") - errNotHitGCTTLDeadline = errors.Newf("not hit gc ttl deadline") - errSchemaChangeDuringDrain = errors.Newf("a schema change ran during the drain phase, re-increment") -) +var errSchemaChangeNotFirstInLine = errors.Newf("schema change not first in line") type errTableVersionMismatch struct { version descpb.DescriptorVersion From 805b6070e942ee07c1188ae16615b6ac50e3b67d Mon Sep 17 00:00:00 2001 From: Nathan VanBenschoten Date: Wed, 27 Oct 2021 01:11:18 -0400 Subject: [PATCH 066/205] kv: prevent intent timestamp regression during partial txn rollback This commit fixes a potential scenario that could lead to a regression in an intent's timestamp. This could previously occur if an intent was pushed to a higher timestamp and then issued a ResolveIntent request with its original timestamp and a list of ignored sequence numbers. The intent resolution request would attempt to revert part of the intent's sequence history, and in doing so, would unintentionally regress its timestamp. The commit first adds a data-driven test that revealed the issue. It then addresses the bug and adds assertions to help catch similar violations in the future. --- pkg/storage/mvcc.go | 20 ++++++- .../testdata/mvcc_histories/ignored_seq_nums | 57 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 9f2783d42c7d..6e4183942df5 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -3201,6 +3201,7 @@ func mvccResolveWriteIntent( // remains, the rolledBackVal is set to a non-nil value. var rolledBackVal []byte if len(intent.IgnoredSeqNums) > 0 { + // NOTE: mvccMaybeRewriteIntentHistory mutates its meta argument. var removeIntent bool removeIntent, rolledBackVal, err = mvccMaybeRewriteIntentHistory(ctx, rw, intent.IgnoredSeqNums, meta, latestKey) if err != nil { @@ -3210,10 +3211,19 @@ func mvccResolveWriteIntent( if removeIntent { // This intent should be cleared. Set commit, pushed, and inProgress to // false so that this intent isn't updated, gets cleared, and committed - // values are left untouched. + // values are left untouched. Also ensure that rolledBackVal is set to nil + // or we could end up trying to update the intent instead of removing it. commit = false pushed = false inProgress = false + rolledBackVal = nil + } + + if rolledBackVal != nil { + // If we need to update the intent to roll back part of its intent + // history, make sure that we don't regress its timestamp, even if the + // caller provided an outdated timestamp. + intent.Txn.WriteTimestamp.Forward(metaTimestamp) } } @@ -3240,6 +3250,14 @@ func mvccResolveWriteIntent( // getting pushed. newTimestamp := intent.Txn.WriteTimestamp + // Assert that the intent timestamp never regresses. The logic above should + // not allow this, regardless of the input to this function. + if newTimestamp.Less(metaTimestamp) { + return false, errors.AssertionFailedf("timestamp regression (%s -> %s) "+ + "during intent resolution, commit=%t pushed=%t rolledBackVal=%t", + metaTimestamp, newTimestamp, commit, pushed, rolledBackVal != nil) + } + buf.newMeta = *meta // Set the timestamp for upcoming write (or at least the stats update). buf.newMeta.Timestamp = newTimestamp.ToLegacyTimestamp() diff --git a/pkg/storage/testdata/mvcc_histories/ignored_seq_nums b/pkg/storage/testdata/mvcc_histories/ignored_seq_nums index 2826746e20ff..00173382f477 100644 --- a/pkg/storage/testdata/mvcc_histories/ignored_seq_nums +++ b/pkg/storage/testdata/mvcc_histories/ignored_seq_nums @@ -572,3 +572,60 @@ with t=E check_intent k=o ---- error: (*withstack.withStack:) meta: "o" -> expected intent, found none + + +run ok +with t=F k=o + txn_begin ts=40 + txn_advance ts=50 + txn_step seq=10 + put v=a + txn_step seq=20 + put v=b + check_intent + get +---- +meta: "o" -> txn={id=00000000 key="o" pri=0.00000000 epo=0 ts=50.000000000,0 min=0,0 seq=20} ts=50.000000000,0 del=false klen=12 vlen=6 ih={{10 /BYTES/a}} mergeTs= txnDidNotUpdateMeta=false +get: "o" -> /BYTES/b @50.000000000,0 +>> at end: +txn: "F" meta={id=00000000 key="o" pri=0.00000000 epo=0 ts=50.000000000,0 min=0,0 seq=20} lock=true stat=PENDING rts=40.000000000,0 wto=false gul=0,0 +data: "k"/14.000000000,0 -> /BYTES/b +meta: "k/10"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=10} ts=11.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "k/10"/11.000000000,0 -> /BYTES/10 +meta: "k/20"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=20} ts=11.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "k/20"/11.000000000,0 -> /BYTES/20 +meta: "k/30"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=30} ts=11.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "k/30"/11.000000000,0 -> /BYTES/30 +data: "m"/30.000000000,0 -> /BYTES/a +data: "n"/45.000000000,0 -> /BYTES/c +meta: "o"/0,0 -> txn={id=00000000 key="o" pri=0.00000000 epo=0 ts=50.000000000,0 min=0,0 seq=20} ts=50.000000000,0 del=false klen=12 vlen=6 ih={{10 /BYTES/a}} mergeTs= txnDidNotUpdateMeta=false +data: "o"/50.000000000,0 -> /BYTES/b + + +# Do a push with a lower timestamp and an ignored seqnum. This should delete the +# value at seqnum 20 but should *not* regress the intent's timestamp. + +run ok +with t=F k=o + txn_ignore_seqs seqs=(15-25) + # the following operation is really a txn_backwards. + txn_advance ts=45 + resolve_intent status=PENDING + check_intent + get +---- +meta: "o" -> txn={id=00000000 key="o" pri=0.00000000 epo=0 ts=50.000000000,0 min=0,0 seq=10} ts=50.000000000,0 del=false klen=12 vlen=6 mergeTs= txnDidNotUpdateMeta=false +get: "o" -> /BYTES/a @50.000000000,0 +>> at end: +txn: "F" meta={id=00000000 key="o" pri=0.00000000 epo=0 ts=45.000000000,0 min=0,0 seq=20} lock=true stat=PENDING rts=40.000000000,0 wto=false gul=0,0 isn=1 +data: "k"/14.000000000,0 -> /BYTES/b +meta: "k/10"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=10} ts=11.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "k/10"/11.000000000,0 -> /BYTES/10 +meta: "k/20"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=20} ts=11.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "k/20"/11.000000000,0 -> /BYTES/20 +meta: "k/30"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=30} ts=11.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "k/30"/11.000000000,0 -> /BYTES/30 +data: "m"/30.000000000,0 -> /BYTES/a +data: "n"/45.000000000,0 -> /BYTES/c +meta: "o"/0,0 -> txn={id=00000000 key="o" pri=0.00000000 epo=0 ts=50.000000000,0 min=0,0 seq=10} ts=50.000000000,0 del=false klen=12 vlen=6 mergeTs= txnDidNotUpdateMeta=false +data: "o"/50.000000000,0 -> /BYTES/a From 86e1c4714485cd8556cbfc3fee7e63be33fca7c8 Mon Sep 17 00:00:00 2001 From: Faizan Qazi Date: Tue, 19 Oct 2021 00:13:09 -0400 Subject: [PATCH 067/205] sql: new schema changer does not correctly sort operations Previously, the new schema changer had bugs in the sort order which would lead to dependency edges not sorting things correctly. Additionally, revertability should also be inherited from these edges. This was inadequate because operations did note execute in the correct order. To address this, this patch fixes various sort related bugs and adds support for inheriting revertability based on dependencies. Release note: None --- pkg/sql/schemachanger/scgraph/graph.go | 27 ++--- pkg/sql/schemachanger/scplan/opgen/op_gen.go | 1 - .../scplan/opgen/opgen_column.go | 7 +- .../scplan/opgen/opgen_default_expression.go | 1 + .../scplan/opgen/opgen_inbound_foreign_key.go | 4 +- .../opgen/opgen_outbound_foreign_key.go | 4 +- .../scplan/opgen/opgen_primary_index.go | 1 + .../opgen/opgen_relation_depended_on_by.go | 2 +- .../scplan/opgen/opgen_sequence_owned_by.go | 2 +- .../scplan/opgen/opgen_type_reference.go | 2 +- pkg/sql/schemachanger/scplan/opgen/target.go | 2 +- pkg/sql/schemachanger/scplan/plan.go | 95 ++++++++++++---- .../schemachanger/scplan/testdata/alter_table | 105 +++++++++--------- .../scplan/testdata/drop_database | 46 ++++---- .../schemachanger/scplan/testdata/drop_schema | 42 +++---- .../schemachanger/scplan/testdata/drop_table | 4 +- .../schemachanger/scplan/testdata/drop_view | 34 +++--- 17 files changed, 215 insertions(+), 164 deletions(-) diff --git a/pkg/sql/schemachanger/scgraph/graph.go b/pkg/sql/schemachanger/scgraph/graph.go index 046afbb96c71..9bae7247f66d 100644 --- a/pkg/sql/schemachanger/scgraph/graph.go +++ b/pkg/sql/schemachanger/scgraph/graph.go @@ -33,9 +33,9 @@ type Graph struct { // Maps a target to its index in targetNodes. targetIdxMap map[*scpb.Target]int - // nodeOpEdges maps a Node to an opEdge that proceeds + // nodeOpEdgesFrom maps a Node to an opEdge that proceeds // from it. A Node may have at most one opEdge from it. - nodeOpEdges map[*scpb.Node]*OpEdge + nodeOpEdgesFrom map[*scpb.Node]*OpEdge // nodeDepEdges maps a Node to its dependencies. // A Node dependency is another target node which must be @@ -70,11 +70,11 @@ func New(initial scpb.State) (*Graph, error) { return nil, err } g := Graph{ - targetIdxMap: map[*scpb.Target]int{}, - nodeOpEdges: map[*scpb.Node]*OpEdge{}, - nodeDepEdges: map[*scpb.Node][]*DepEdge{}, - opToNode: map[scop.Op]*scpb.Node{}, - entities: db, + targetIdxMap: map[*scpb.Target]int{}, + nodeOpEdgesFrom: map[*scpb.Node]*OpEdge{}, + nodeDepEdges: map[*scpb.Node][]*DepEdge{}, + opToNode: map[scop.Op]*scpb.Node{}, + entities: db, } for _, n := range initial { if existing, ok := g.targetIdxMap[n.Target]; ok { @@ -93,14 +93,15 @@ func New(initial scpb.State) (*Graph, error) { return &g, nil } -func (g *Graph) getNode(t *scpb.Target, s scpb.Status) (*scpb.Node, bool) { +// GetNode returns the cached node for a given target and status. +func (g *Graph) GetNode(t *scpb.Target, s scpb.Status) (*scpb.Node, bool) { targetStatuses := g.getTargetStatusMap(t) ts, ok := targetStatuses[s] return ts, ok } // Suppress the linter. -var _ = (*Graph)(nil).getNode +var _ = (*Graph)(nil).GetNode func (g *Graph) getOrCreateNode(t *scpb.Target, s scpb.Status) (*scpb.Node, error) { targetStatuses := g.getTargetStatusMap(t) @@ -137,7 +138,7 @@ var _ = (*Graph)(nil).containsTarget // GetOpEdgeFrom returns the unique outgoing op edge from the specified node, // if one exists. func (g *Graph) GetOpEdgeFrom(n *scpb.Node) (*OpEdge, bool) { - oe, ok := g.nodeOpEdges[n] + oe, ok := g.nodeOpEdgesFrom[n] return oe, ok } @@ -162,15 +163,15 @@ func (g *Graph) AddOpEdges( if oe.to, err = g.getOrCreateNode(t, to); err != nil { return err } - if existing, exists := g.nodeOpEdges[oe.from]; exists { + if existing, exists := g.nodeOpEdgesFrom[oe.from]; exists { return errors.Errorf("duplicate outbound op edge %v and %v", oe, existing) } g.edges = append(g.edges, oe) - g.nodeOpEdges[oe.from] = oe + g.nodeOpEdgesFrom[oe.from] = oe // Store mapping from op to Edge for _, op := range ops { - g.opToNode[op] = oe.From() + g.opToNode[op] = oe.To() } return nil } diff --git a/pkg/sql/schemachanger/scplan/opgen/op_gen.go b/pkg/sql/schemachanger/scplan/opgen/op_gen.go index 68d8e59175e0..97bc3bdfc843 100644 --- a/pkg/sql/schemachanger/scplan/opgen/op_gen.go +++ b/pkg/sql/schemachanger/scplan/opgen/op_gen.go @@ -75,7 +75,6 @@ func (r *registry) buildGraph(currentPhase scop.Phase, initial scpb.State) (*scg op.from = op.to op.ops = func(element scpb.Element) []scop.Op { return nil } } - edgesToAdd = append(edgesToAdd, toAdd{ transition: op, n: n, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_column.go b/pkg/sql/schemachanger/scplan/opgen/opgen_column.go index 62f32aaf9a26..2604ce1fcd83 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_column.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_column.go @@ -32,9 +32,6 @@ func init() { })), to(scpb.Status_DELETE_AND_WRITE_ONLY, minPhase(scop.PostCommitPhase), - // TODO(ajwerner): This non-revertible label is not - // correct here but we're porting rules. - revertible(false), emit(func(this *scpb.Column) scop.Op { return &scop.MakeAddedColumnDeleteAndWriteOnly{ TableID: this.TableID, @@ -61,8 +58,8 @@ func init() { ColumnID: this.Column.ID, } })), - to(scpb.Status_DELETE_AND_WRITE_ONLY, - // TODO(ajwerner): This should probably be marked as non-revertible. + to(scpb.Status_DELETE_ONLY, + revertible(false), minPhase(scop.PostCommitPhase), emit(func(this *scpb.Column) scop.Op { return &scop.MakeDroppedColumnDeleteOnly{ diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_default_expression.go b/pkg/sql/schemachanger/scplan/opgen/opgen_default_expression.go index e9efa69c5e22..e39c98caa021 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_default_expression.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_default_expression.go @@ -22,6 +22,7 @@ func init() { scpb.Status_PUBLIC, to(scpb.Status_ABSENT, minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.DefaultExpression) scop.Op { return &scop.RemoveColumnDefaultExpression{ TableID: this.TableID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_inbound_foreign_key.go b/pkg/sql/schemachanger/scplan/opgen/opgen_inbound_foreign_key.go index 3ab58d553b8c..02342910ee25 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_inbound_foreign_key.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_inbound_foreign_key.go @@ -21,9 +21,9 @@ func init() { scpb.Target_DROP, scpb.Status_PUBLIC, to(scpb.Status_ABSENT, + // TODO(ajwerner): This probably cannot happen until post-commit. minPhase(scop.PreCommitPhase), - // TODO(ajwerner): This should not be revertible and probably - // cannot happen until post-commit. + revertible(false), emit(func(this *scpb.InboundForeignKey) scop.Op { return &scop.DropForeignKeyRef{ TableID: this.OriginID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_outbound_foreign_key.go b/pkg/sql/schemachanger/scplan/opgen/opgen_outbound_foreign_key.go index 4415270fb4dd..d6573636a27a 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_outbound_foreign_key.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_outbound_foreign_key.go @@ -21,9 +21,9 @@ func init() { scpb.Target_DROP, scpb.Status_PUBLIC, to(scpb.Status_ABSENT, + // TODO(ajwerner): This probably cannot happen until post-commit. minPhase(scop.PreCommitPhase), - // TODO(ajwerner): This should not be revertible and probably - // cannot happen until post-commit. + revertible(false), emit(func(this *scpb.OutboundForeignKey) scop.Op { return &scop.DropForeignKeyRef{ TableID: this.OriginID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_primary_index.go b/pkg/sql/schemachanger/scplan/opgen/opgen_primary_index.go index 25271c79e042..7e87c1c15078 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_primary_index.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_primary_index.go @@ -80,6 +80,7 @@ func init() { })), to(scpb.Status_DELETE_ONLY, minPhase(scop.PostCommitPhase), + revertible(false), // TODO(ajwerner): This should be marked as not revertible. emit(func(this *scpb.PrimaryIndex) scop.Op { return &scop.MakeDroppedIndexDeleteOnly{ diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_relation_depended_on_by.go b/pkg/sql/schemachanger/scplan/opgen/opgen_relation_depended_on_by.go index 7369bac5d320..d35d14d1ddc0 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_relation_depended_on_by.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_relation_depended_on_by.go @@ -22,7 +22,7 @@ func init() { scpb.Status_PUBLIC, to(scpb.Status_ABSENT, minPhase(scop.PreCommitPhase), - // TODO(ajwerner): This should be marked as non-revertible. + revertible(false), emit(func(this *scpb.RelationDependedOnBy) scop.Op { return &scop.RemoveRelationDependedOnBy{ TableID: this.TableID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_sequence_owned_by.go b/pkg/sql/schemachanger/scplan/opgen/opgen_sequence_owned_by.go index b9bf338da68c..7a820c0b74d0 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_sequence_owned_by.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_sequence_owned_by.go @@ -21,8 +21,8 @@ func init() { scpb.Target_DROP, scpb.Status_PUBLIC, to(scpb.Status_ABSENT, - // TODO(ajwerner): This probably should be marked as non-revertible. minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.SequenceOwnedBy) scop.Op { return &scop.RemoveSequenceOwnedBy{ TableID: this.SequenceID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_type_reference.go b/pkg/sql/schemachanger/scplan/opgen/opgen_type_reference.go index 78e9eeca301e..1c64878d87b3 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_type_reference.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_type_reference.go @@ -22,7 +22,7 @@ func init() { scpb.Status_PUBLIC, to(scpb.Status_ABSENT, minPhase(scop.PreCommitPhase), - // TODO(ajwerner): This feels like it should be marked non-revertible. + revertible(false), emit(func(this *scpb.TypeReference) scop.Op { return &scop.RemoveTypeBackRef{ TypeID: this.TypeID, diff --git a/pkg/sql/schemachanger/scplan/opgen/target.go b/pkg/sql/schemachanger/scplan/opgen/target.go index 85f4ee8cffe1..d2bccd5f6513 100644 --- a/pkg/sql/schemachanger/scplan/opgen/target.go +++ b/pkg/sql/schemachanger/scplan/opgen/target.go @@ -54,7 +54,7 @@ func populateSpecs(status scpb.Status, specs []transitionSpec) { continue } specs[i].from = specs[i-1].to - + specs[i].revertible = specs[i].revertible && specs[i-1].revertible prevMinPhase := specs[i-1].minPhase switch curMinPhase := specs[i].minPhase; curMinPhase { case 0: diff --git a/pkg/sql/schemachanger/scplan/plan.go b/pkg/sql/schemachanger/scplan/plan.go index ed75382b85a4..8a7adca00a3f 100644 --- a/pkg/sql/schemachanger/scplan/plan.go +++ b/pkg/sql/schemachanger/scplan/plan.go @@ -85,6 +85,21 @@ func MakePlan(initial scpb.State, params Params) (_ Plan, err error) { }, nil } +// validateStages sanity checks stages to ensure no +// invalid execution plans are made. +func validateStages(stages []Stage) { + revertibleAllowed := true + for idx, stage := range stages { + if !stage.Revertible { + revertibleAllowed = false + } + if stage.Revertible && !revertibleAllowed { + panic(errors.AssertionFailedf( + "invalid execution plan revertability flipped at stage (%d): %v", idx, stage)) + } + } +} + func buildStages(init scpb.State, g *scgraph.Graph, params Params) []Stage { // TODO(ajwerner): deal with the case where the target status was // fulfilled by something that preceded the initial state. @@ -212,6 +227,7 @@ func buildStages(init scpb.State, g *scgraph.Graph, params Params) []Stage { stages = append(stages, s) cur = s.After } + validateStages(stages) return stages } @@ -244,24 +260,47 @@ func doesPathExistToNode(graph *scgraph.Graph, start *scpb.Node, target *scpb.No // sortOps sorts the operations into order based on // graph dependencies func sortOps(graph *scgraph.Graph, ops []scop.Op) { - for i := 1; i < len(ops); i++ { - for j := i; j > 0; j-- { - if compareOps(graph, ops[j], ops[j-1]) { - tmp := ops[j] - ops[j] = ops[j-1] - ops[j-1] = tmp + // Original implicit order of the ops will + // be kept to keep ordering of equal values. + implicitOrder := make([]int, 0, len(ops)) + for i := range ops { + implicitOrder = append(implicitOrder, i) + } + // Unfortunately, we are forced to do an inefficient + // bubble sort, since with unrelated dependencies will + // be equal to each other. But, still have a relative order + // across the entire set of nodes. + for i := 0; i < len(ops); i++ { + for j := i + 1; j < len(ops); j++ { + if i == j { + continue + } + if !compareOps(graph, ops[i], ops[j], implicitOrder[i], implicitOrder[j]) && + compareOps(graph, ops[j], ops[i], implicitOrder[j], implicitOrder[i]) { + tmpOrder := implicitOrder[i] + tmp := ops[i] + ops[i] = ops[j] + ops[j] = tmp + implicitOrder[i] = implicitOrder[j] + implicitOrder[j] = tmpOrder } } } - // Sanity: Graph order is sane across all of - // the ops. + // Sanity: Graph order is sane across all the ops. for i := 0; i < len(ops); i++ { for j := i + 1; j < len(ops); j++ { - if !compareOps(graph, ops[i], ops[j]) && // Greater, but not equal (if equal opposite comparison would match). - compareOps(graph, ops[j], ops[i]) { - panic(errors.AssertionFailedf("Operators are not completely sorted %d %d", i, j)) - } else if compareOps(graph, ops[j], ops[i]) { - compareOps(graph, ops[j], ops[i]) + // Validate that the list is sorted by checking + // 1) i is always less than j. + // 1a) If it is not less than its equal based on the opposite comparison. + // 2) j should always be greater or equal to i, so our comparison + // should never be true. + // Note: We will intentionally ignore the implicit order for the + // validation phase because we will have non-comparable items. + if !compareOps(graph, ops[i], ops[j], 1, 0) && + compareOps(graph, ops[j], ops[i], 1, 0) { + panic(errors.AssertionFailedf("Operators are not completely sorted %d %d, "+ + "not strictly increasing", i, j)) + } else if compareOps(graph, ops[j], ops[i], 1, 0) { panic(errors.AssertionFailedf("Operators are not completely sorted %d %d", i, j)) } } @@ -270,20 +309,27 @@ func sortOps(graph *scgraph.Graph, ops []scop.Op) { // compareOps compares operations and orders them based on // followed by the graph dependencies. -func compareOps(graph *scgraph.Graph, firstOp scop.Op, secondOp scop.Op) (less bool) { +func compareOps( + graph *scgraph.Graph, firstOp, + secondOp scop.Op, firstImplicitOrder, secondImplicitOrder int, +) (less bool) { // Otherwise, lets compare attributes firstNode := graph.GetNodeFromOp(firstOp) secondNode := graph.GetNodeFromOp(secondOp) if firstNode == secondNode { - return false // Equal + // Equal, only implicit order determines which is first. + return firstImplicitOrder < secondImplicitOrder } firstExists := doesPathExistToNode(graph, firstNode, secondNode) secondExists := doesPathExistToNode(graph, secondNode, firstNode) + + // If both paths exist, then we care about the direction of nodes, + // otherwise we have a cycle, and we can't sort. if firstExists && secondExists { if firstNode.Target.Direction == scpb.Target_DROP { - return true - } else if secondNode.Target.Direction == scpb.Target_DROP { return false + } else if secondNode.Target.Direction == scpb.Target_DROP { + return true } else { panic(errors.AssertionFailedf("A potential cycle exists in plan the graph, without any"+ "nodes transitioning in opposite directions\n %s\n%s\n", @@ -291,7 +337,18 @@ func compareOps(graph *scgraph.Graph, firstOp scop.Op, secondOp scop.Op) (less b secondNode)) } } + // If a path exists from first to second, then the first node depends + // on the second. + if firstExists { + return false + } + // If a path exists to the second to the first node, then + // the second node depends on the first. + if secondExists { + return true + } - // Path exists from first to second, so we depend on second. - return firstExists + // Otherwise, no paths exists and the two are equal. + // Only the implicit order determines the first one. + return firstImplicitOrder < secondImplicitOrder } diff --git a/pkg/sql/schemachanger/scplan/testdata/alter_table b/pkg/sql/schemachanger/scplan/testdata/alter_table index a9ac65a07933..ee91b255d2ae 100644 --- a/pkg/sql/schemachanger/scplan/testdata/alter_table +++ b/pkg/sql/schemachanger/scplan/testdata/alter_table @@ -35,27 +35,23 @@ Stage 0 Unique: true Version: 4 TableID: 52 -Stage 1 +Stage 1 (non-revertible) *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 -Stage 2 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 -Stage 3 +Stage 2 (non-revertible) *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 4 +Stage 3 (non-revertible) *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 5 - *scop.MakeColumnPublic - ColumnID: 2 - TableID: 52 +Stage 4 (non-revertible) *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -88,11 +84,14 @@ Stage 5 Unique: true Version: 4 TableID: 52 -Stage 6 + *scop.MakeColumnPublic + ColumnID: 2 + TableID: 52 +Stage 5 (non-revertible) *scop.MakeDroppedIndexDeleteOnly IndexID: 1 TableID: 52 -Stage 7 +Stage 6 (non-revertible) *scop.MakeIndexAbsent IndexID: 1 TableID: 52 @@ -131,27 +130,23 @@ Stage 0 Unique: true Version: 4 TableID: 52 -Stage 1 +Stage 1 (non-revertible) *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 -Stage 2 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 -Stage 3 +Stage 2 (non-revertible) *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 4 +Stage 3 (non-revertible) *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 5 - *scop.MakeColumnPublic - ColumnID: 2 - TableID: 52 +Stage 4 (non-revertible) *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -184,11 +179,14 @@ Stage 5 Unique: true Version: 4 TableID: 52 -Stage 6 + *scop.MakeColumnPublic + ColumnID: 2 + TableID: 52 +Stage 5 (non-revertible) *scop.MakeDroppedIndexDeleteOnly IndexID: 1 TableID: 52 -Stage 7 +Stage 6 (non-revertible) *scop.MakeIndexAbsent IndexID: 1 TableID: 52 @@ -242,30 +240,26 @@ Stage 0 width: 64 FamilyName: primary TableID: 52 -Stage 1 +Stage 1 (non-revertible) *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 -Stage 2 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 3 TableID: 52 -Stage 3 +Stage 2 (non-revertible) *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 4 +Stage 3 (non-revertible) *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 5 - *scop.MakeColumnPublic - ColumnID: 2 - TableID: 52 +Stage 4 (non-revertible) *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -300,14 +294,17 @@ Stage 5 Unique: true Version: 4 TableID: 52 + *scop.MakeColumnPublic + ColumnID: 2 + TableID: 52 *scop.MakeColumnPublic ColumnID: 3 TableID: 52 -Stage 6 +Stage 5 (non-revertible) *scop.MakeDroppedIndexDeleteOnly IndexID: 1 TableID: 52 -Stage 7 +Stage 6 (non-revertible) *scop.MakeIndexAbsent IndexID: 1 TableID: 52 @@ -346,27 +343,23 @@ Stage 0 Unique: true Version: 4 TableID: 52 -Stage 1 +Stage 1 (non-revertible) *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 -Stage 2 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 -Stage 3 +Stage 2 (non-revertible) *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 4 +Stage 3 (non-revertible) *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 5 - *scop.MakeColumnPublic - ColumnID: 2 - TableID: 52 +Stage 4 (non-revertible) *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -399,11 +392,14 @@ Stage 5 Unique: true Version: 4 TableID: 52 -Stage 6 + *scop.MakeColumnPublic + ColumnID: 2 + TableID: 52 +Stage 5 (non-revertible) *scop.MakeDroppedIndexDeleteOnly IndexID: 1 TableID: 52 -Stage 7 +Stage 6 (non-revertible) *scop.MakeIndexAbsent IndexID: 1 TableID: 52 @@ -478,28 +474,27 @@ Stage 0 Unique: true Version: 4 TableID: 53 -Stage 1 +Stage 1 (non-revertible) *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 - *scop.MakeAddedIndexDeleteAndWriteOnly - IndexID: 2 - TableID: 53 -Stage 2 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 + *scop.MakeAddedIndexDeleteAndWriteOnly + IndexID: 2 + TableID: 53 *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 3 TableID: 53 -Stage 3 +Stage 2 (non-revertible) *scop.BackfillIndex IndexID: 2 TableID: 52 *scop.BackfillIndex IndexID: 2 TableID: 53 -Stage 4 +Stage 3 (non-revertible) *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 @@ -508,10 +503,7 @@ Stage 4 IndexID: 2 PrimaryIndexID: 1 TableID: 53 -Stage 5 - *scop.MakeColumnPublic - ColumnID: 2 - TableID: 52 +Stage 4 (non-revertible) *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -545,8 +537,8 @@ Stage 5 Version: 4 TableID: 52 *scop.MakeColumnPublic - ColumnID: 3 - TableID: 53 + ColumnID: 2 + TableID: 52 *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -585,14 +577,17 @@ Stage 5 Unique: true Version: 4 TableID: 53 -Stage 6 + *scop.MakeColumnPublic + ColumnID: 3 + TableID: 53 +Stage 5 (non-revertible) *scop.MakeDroppedIndexDeleteOnly IndexID: 1 TableID: 52 *scop.MakeDroppedIndexDeleteOnly IndexID: 1 TableID: 53 -Stage 7 +Stage 6 (non-revertible) *scop.MakeIndexAbsent IndexID: 1 TableID: 52 diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_database b/pkg/sql/schemachanger/scplan/testdata/drop_database index 0fab7ec2881c..efc903e0f45b 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_database +++ b/pkg/sql/schemachanger/scplan/testdata/drop_database @@ -92,12 +92,6 @@ Stage 0 *scop.RemoveRelationDependedOnBy DependedOnBy: 64 TableID: 61 - *scop.RemoveTypeBackRef - DescID: 64 - TypeID: 62 - *scop.RemoveTypeBackRef - DescID: 64 - TypeID: 63 *scop.RemoveColumnDefaultExpression ColumnID: 1 TableID: 56 @@ -115,12 +109,7 @@ Stage 0 TableID: 62 *scop.MarkDescriptorAsDropped TableID: 63 - *scop.MarkDescriptorAsDropped - TableID: 52 -Stage 1 - *scop.MarkDescriptorAsDropped - TableID: 53 -Stage 2 (non-revertible) +Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped TableID: 54 *scop.DrainDescriptorName @@ -140,11 +129,11 @@ Stage 2 (non-revertible) *scop.CreateGcJobForDescriptor DescID: 55 *scop.MarkDescriptorAsDropped - TableID: 58 + TableID: 60 *scop.DrainDescriptorName - TableID: 58 + TableID: 60 *scop.CreateGcJobForDescriptor - DescID: 58 + DescID: 60 *scop.MarkDescriptorAsDropped TableID: 59 *scop.DrainDescriptorName @@ -152,23 +141,29 @@ Stage 2 (non-revertible) *scop.CreateGcJobForDescriptor DescID: 59 *scop.MarkDescriptorAsDropped - TableID: 60 + TableID: 58 *scop.DrainDescriptorName - TableID: 60 + TableID: 58 *scop.CreateGcJobForDescriptor - DescID: 60 + DescID: 58 + *scop.RemoveTypeBackRef + DescID: 64 + TypeID: 62 *scop.MarkDescriptorAsDropped - TableID: 61 + TableID: 64 *scop.DrainDescriptorName - TableID: 61 + TableID: 64 *scop.CreateGcJobForDescriptor - DescID: 61 + DescID: 64 *scop.MarkDescriptorAsDropped - TableID: 64 + TableID: 61 *scop.DrainDescriptorName - TableID: 64 + TableID: 61 *scop.CreateGcJobForDescriptor + DescID: 61 + *scop.RemoveTypeBackRef DescID: 64 + TypeID: 63 *scop.MarkDescriptorAsDropped TableID: 56 *scop.DrainDescriptorName @@ -179,6 +174,11 @@ Stage 2 (non-revertible) TableID: 62 *scop.DrainDescriptorName TableID: 63 + *scop.MarkDescriptorAsDropped + TableID: 53 + *scop.MarkDescriptorAsDropped + TableID: 52 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 53 *scop.DrainDescriptorName diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_schema b/pkg/sql/schemachanger/scplan/testdata/drop_schema index 41db6896a2dc..92ff58c28f99 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_schema +++ b/pkg/sql/schemachanger/scplan/testdata/drop_schema @@ -113,12 +113,6 @@ Stage 0 *scop.RemoveRelationDependedOnBy DependedOnBy: 61 TableID: 58 - *scop.RemoveTypeBackRef - DescID: 61 - TypeID: 59 - *scop.RemoveTypeBackRef - DescID: 61 - TypeID: 60 *scop.RemoveColumnDefaultExpression ColumnID: 1 TableID: 54 @@ -136,10 +130,7 @@ Stage 0 TableID: 59 *scop.MarkDescriptorAsDropped TableID: 60 -Stage 1 - *scop.MarkDescriptorAsDropped - TableID: 52 -Stage 2 (non-revertible) +Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped TableID: 53 *scop.DrainDescriptorName @@ -147,11 +138,11 @@ Stage 2 (non-revertible) *scop.CreateGcJobForDescriptor DescID: 53 *scop.MarkDescriptorAsDropped - TableID: 55 + TableID: 57 *scop.DrainDescriptorName - TableID: 55 + TableID: 57 *scop.CreateGcJobForDescriptor - DescID: 55 + DescID: 57 *scop.MarkDescriptorAsDropped TableID: 56 *scop.DrainDescriptorName @@ -159,23 +150,29 @@ Stage 2 (non-revertible) *scop.CreateGcJobForDescriptor DescID: 56 *scop.MarkDescriptorAsDropped - TableID: 57 + TableID: 55 *scop.DrainDescriptorName - TableID: 57 + TableID: 55 *scop.CreateGcJobForDescriptor - DescID: 57 + DescID: 55 + *scop.RemoveTypeBackRef + DescID: 61 + TypeID: 59 *scop.MarkDescriptorAsDropped - TableID: 58 + TableID: 61 *scop.DrainDescriptorName - TableID: 58 + TableID: 61 *scop.CreateGcJobForDescriptor - DescID: 58 + DescID: 61 *scop.MarkDescriptorAsDropped - TableID: 61 + TableID: 58 *scop.DrainDescriptorName - TableID: 61 + TableID: 58 *scop.CreateGcJobForDescriptor + DescID: 58 + *scop.RemoveTypeBackRef DescID: 61 + TypeID: 60 *scop.MarkDescriptorAsDropped TableID: 54 *scop.DrainDescriptorName @@ -186,5 +183,8 @@ Stage 2 (non-revertible) TableID: 59 *scop.DrainDescriptorName TableID: 60 + *scop.MarkDescriptorAsDropped + TableID: 52 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 52 diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_table b/pkg/sql/schemachanger/scplan/testdata/drop_table index 6c256e968833..f098e99104fd 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_table +++ b/pkg/sql/schemachanger/scplan/testdata/drop_table @@ -61,8 +61,6 @@ Stage 0 TableID: 55 *scop.UpdateRelationDeps TableID: 55 - *scop.RemoveSequenceOwnedBy - TableID: 56 *scop.RemoveColumnDefaultExpression ColumnID: 2 TableID: 55 @@ -99,6 +97,8 @@ Stage 1 (non-revertible) TableID: 56 *scop.CreateGcJobForDescriptor DescID: 56 + *scop.RemoveSequenceOwnedBy + TableID: 56 *scop.MarkDescriptorAsDropped TableID: 55 *scop.DrainDescriptorName diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_view b/pkg/sql/schemachanger/scplan/testdata/drop_view index 4ef9ed7f3589..46df9bb07b6d 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_view +++ b/pkg/sql/schemachanger/scplan/testdata/drop_view @@ -67,19 +67,13 @@ Stage 0 *scop.RemoveRelationDependedOnBy DependedOnBy: 59 TableID: 56 - *scop.RemoveTypeBackRef - DescID: 59 - TypeID: 57 - *scop.RemoveTypeBackRef - DescID: 59 - TypeID: 58 Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped - TableID: 53 + TableID: 55 *scop.DrainDescriptorName - TableID: 53 + TableID: 55 *scop.CreateGcJobForDescriptor - DescID: 53 + DescID: 55 *scop.MarkDescriptorAsDropped TableID: 54 *scop.DrainDescriptorName @@ -87,23 +81,29 @@ Stage 1 (non-revertible) *scop.CreateGcJobForDescriptor DescID: 54 *scop.MarkDescriptorAsDropped - TableID: 55 + TableID: 53 *scop.DrainDescriptorName - TableID: 55 + TableID: 53 *scop.CreateGcJobForDescriptor - DescID: 55 + DescID: 53 + *scop.RemoveTypeBackRef + DescID: 59 + TypeID: 57 *scop.MarkDescriptorAsDropped - TableID: 56 + TableID: 59 *scop.DrainDescriptorName - TableID: 56 + TableID: 59 *scop.CreateGcJobForDescriptor - DescID: 56 + DescID: 59 *scop.MarkDescriptorAsDropped - TableID: 59 + TableID: 56 *scop.DrainDescriptorName - TableID: 59 + TableID: 56 *scop.CreateGcJobForDescriptor + DescID: 56 + *scop.RemoveTypeBackRef DescID: 59 + TypeID: 58 deps DROP VIEW defaultdb.v1 CASCADE From 4fd3e1d02ad8a27ad084d467959e4286acddedef Mon Sep 17 00:00:00 2001 From: Faizan Qazi Date: Tue, 19 Oct 2021 00:12:03 -0400 Subject: [PATCH 068/205] sql: new schema changer does not properly use phases for drops Fixes: #66613 Previously, when drop operations were added into the new schema changer we added all operations into the statement phase. This was inadequate because if an explicit transaction dropped a table and then most of the work would be applied before the transaction was committed potentially influencing other transactions. To address this, this patch splits the drop into between statement and pre-commit phases properly. In the pre-commit phase, a synthetic descriptor marks the object as dropped, so that only the transaction sees the drop. The pre-commit phase which executes before committing then marks the descriptor as dropped. Release note: None --- pkg/sql/catalog/descs/collection.go | 13 + pkg/sql/catalog/descs/descriptor.go | 2 +- .../catalog/descs/synthetic_descriptors.go | 19 +- .../testdata/logic_test/new_schema_changer | 33 +++ pkg/sql/schema_change_plan_node.go | 3 + pkg/sql/schemachanger/scexec/BUILD.bazel | 1 + pkg/sql/schemachanger/scexec/executor.go | 7 +- .../scexec/mutation_desc_getter.go | 19 ++ .../scexec/scmutationexec/scmutationexec.go | 26 +- pkg/sql/schemachanger/scop/mutation.go | 9 +- .../scop/mutation_visitor_generated.go | 6 + pkg/sql/schemachanger/scop/phase.go | 2 + pkg/sql/schemachanger/scpb/scpb.pb.go | 250 +++++++++--------- pkg/sql/schemachanger/scpb/scpb.proto | 12 +- .../schemachanger/scplan/deprules/rules.go | 235 +++++----------- .../scplan/deprules/testdata/rules | 233 ++++++---------- .../scplan/opgen/opgen_database.go | 18 +- .../scplan/opgen/opgen_schema.go | 17 +- .../scplan/opgen/opgen_sequence.go | 15 +- .../schemachanger/scplan/opgen/opgen_table.go | 15 +- .../schemachanger/scplan/opgen/opgen_type.go | 20 +- .../schemachanger/scplan/opgen/opgen_view.go | 15 +- .../schemachanger/scplan/testdata/alter_table | 40 +-- .../scplan/testdata/drop_database | 186 ++++++++----- .../schemachanger/scplan/testdata/drop_schema | 146 ++++++---- .../scplan/testdata/drop_sequence | 19 +- .../schemachanger/scplan/testdata/drop_table | 71 +++-- .../schemachanger/scplan/testdata/drop_type | 11 +- .../schemachanger/scplan/testdata/drop_view | 70 +++-- pkg/sql/sem/tree/name_resolution.go | 2 + 30 files changed, 854 insertions(+), 661 deletions(-) diff --git a/pkg/sql/catalog/descs/collection.go b/pkg/sql/catalog/descs/collection.go index 8f60a335de87..dc5e5ab9a914 100644 --- a/pkg/sql/catalog/descs/collection.go +++ b/pkg/sql/catalog/descs/collection.go @@ -383,6 +383,19 @@ func (tc *Collection) SetSyntheticDescriptors(descs []catalog.Descriptor) { tc.synthetic.set(descs) } +// AddSyntheticDescriptor replaces a descriptor with a synthetic +// one temporarily for a given transaction. This synthetic descriptor +// will be immutable. +func (tc *Collection) AddSyntheticDescriptor(desc catalog.Descriptor) { + tc.synthetic.add(desc) +} + +// RemoveSyntheticDescriptor removes a synthetic descriptor +// override that temporarily exists for a given transaction. +func (tc *Collection) RemoveSyntheticDescriptor(id descpb.ID) { + tc.synthetic.remove(id) +} + func (tc *Collection) codec() keys.SQLCodec { return tc.kv.codec } diff --git a/pkg/sql/catalog/descs/descriptor.go b/pkg/sql/catalog/descs/descriptor.go index bdf3fe3ba386..8675faa64e6f 100644 --- a/pkg/sql/catalog/descs/descriptor.go +++ b/pkg/sql/catalog/descs/descriptor.go @@ -83,7 +83,7 @@ func (tc *Collection) getDescriptorByIDMaybeSetTxnDeadline( return vd, err } - if found, sd := tc.synthetic.getByID(id); found { + if found, sd := tc.synthetic.getByID(id); found && !flags.AvoidSynthetic { if flags.RequireMutable { return nil, newMutableSyntheticDescriptorAssertionError(sd.GetID()) } diff --git a/pkg/sql/catalog/descs/synthetic_descriptors.go b/pkg/sql/catalog/descs/synthetic_descriptors.go index 9d17f8b9100b..23efaa24d066 100644 --- a/pkg/sql/catalog/descs/synthetic_descriptors.go +++ b/pkg/sql/catalog/descs/synthetic_descriptors.go @@ -20,13 +20,24 @@ type syntheticDescriptors struct { descs nstree.Map } +func (sd *syntheticDescriptors) add(desc catalog.Descriptor) { + if mut, ok := desc.(catalog.MutableDescriptor); ok { + desc = mut.ImmutableCopy() + sd.descs.Upsert(desc) + } else { + // Already an immutable object. + sd.descs.Upsert(desc) + } +} + +func (sd *syntheticDescriptors) remove(id descpb.ID) { + sd.descs.Remove(id) +} + func (sd *syntheticDescriptors) set(descs []catalog.Descriptor) { sd.descs.Clear() for _, desc := range descs { - if mut, ok := desc.(catalog.MutableDescriptor); ok { - desc = mut.ImmutableCopy() - } - sd.descs.Upsert(desc) + sd.add(desc) } } diff --git a/pkg/sql/logictest/testdata/logic_test/new_schema_changer b/pkg/sql/logictest/testdata/logic_test/new_schema_changer index 9f3d0763870b..449d8ec19214 100644 --- a/pkg/sql/logictest/testdata/logic_test/new_schema_changer +++ b/pkg/sql/logictest/testdata/logic_test/new_schema_changer @@ -620,6 +620,39 @@ DROP SCHEMA db1.sc1 statement error database "db1" is not empty and CASCADE was not specified DROP DATABASE db1 +statement ok +SET experimental_use_new_schema_changer = 'unsafe_always' + +# Sanity check: Things are properly executed in post rollback. We should +# be able to select from tables/views/sequences still +statement ok +BEGIN + +statement ok +DROP DATABASE db1 CASCADE + +statement error database "db1" does not exist +SELECT * from db1.sc1.v1 + +statement ok +ROLLBACK + +statement ok +SELECT * from db1.sc1.v1 + +statement ok +SELECT * from db1.sc1.t1 + +statement ok +SELECT * from db1.sc1.sq1 + +statement ok +SELECT 'a'::db1.sc1.typ::string + +statement ok +SET experimental_use_new_schema_changer = 'on' + +# Actually drop the database now. statement ok DROP DATABASE db1 CASCADE diff --git a/pkg/sql/schema_change_plan_node.go b/pkg/sql/schema_change_plan_node.go index 924d1523d76a..6ae6e60f47c7 100644 --- a/pkg/sql/schema_change_plan_node.go +++ b/pkg/sql/schema_change_plan_node.go @@ -33,6 +33,9 @@ import ( func (p *planner) SchemaChange(ctx context.Context, stmt tree.Statement) (planNode, bool, error) { // TODO(ajwerner): Call featureflag.CheckEnabled appropriately. mode := p.extendedEvalCtx.SchemaChangerState.mode + // When new schema changer is on we will not support it for explicit + // transaction, since we don't know if subsequent statements don't + // support it. if mode == sessiondatapb.UseNewSchemaChangerOff || (mode == sessiondatapb.UseNewSchemaChangerOn && !p.extendedEvalCtx.TxnImplicit) { return nil, false, nil diff --git a/pkg/sql/schemachanger/scexec/BUILD.bazel b/pkg/sql/schemachanger/scexec/BUILD.bazel index 4c71cd24288d..1c88bb9914cf 100644 --- a/pkg/sql/schemachanger/scexec/BUILD.bazel +++ b/pkg/sql/schemachanger/scexec/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//pkg/security", "//pkg/sql/catalog", "//pkg/sql/catalog/catalogkeys", + "//pkg/sql/catalog/catalogkv", "//pkg/sql/catalog/descpb", "//pkg/sql/catalog/descs", "//pkg/sql/catalog/tabledesc", diff --git a/pkg/sql/schemachanger/scexec/executor.go b/pkg/sql/schemachanger/scexec/executor.go index fc281468d130..8fa1d02c3684 100644 --- a/pkg/sql/schemachanger/scexec/executor.go +++ b/pkg/sql/schemachanger/scexec/executor.go @@ -280,12 +280,15 @@ func UpdateDescriptorJobIDs( return err } // Currently all "locking" schema changes are on tables. This will probably - // need to be expanded at least to types. + // need to be expanded at least to types. Synthetic descriptors are intentionally + // ignore, since we may inject these in the statement phase to mark things as + // dropped for example. table, err := descriptors.GetMutableTableByID(ctx, txn, id, tree.ObjectLookupFlags{ CommonLookupFlags: tree.CommonLookupFlags{ Required: true, - IncludeDropped: true}, + IncludeDropped: true, + AvoidSynthetic: true}, }) if err != nil { return err diff --git a/pkg/sql/schemachanger/scexec/mutation_desc_getter.go b/pkg/sql/schemachanger/scexec/mutation_desc_getter.go index 21b814f8154f..8fa364663bd2 100644 --- a/pkg/sql/schemachanger/scexec/mutation_desc_getter.go +++ b/pkg/sql/schemachanger/scexec/mutation_desc_getter.go @@ -18,6 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/security" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkeys" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" @@ -109,6 +110,24 @@ func (m *mutationDescGetter) SubmitDrainedNames( return nil } +func (m *mutationDescGetter) MarkDescriptorAsSyntheticDropped( + ctx context.Context, id descpb.ID, +) error { + table, err := m.descs.GetImmutableDescriptorByID(ctx, m.txn, id, tree.CommonLookupFlags{Required: true}) + if err != nil { + return err + } + droppedDesc := catalogkv.NewBuilderWithMVCCTimestamp(table.DescriptorProto(), m.txn.ReadTimestamp()).BuildExistingMutable() + droppedDesc.SetDropped() + m.descs.AddSyntheticDescriptor(droppedDesc) + return nil +} + +func (m *mutationDescGetter) RemoveSyntheticDescFromTxn(_ context.Context, id descpb.ID) error { + m.descs.RemoveSyntheticDescriptor(id) + return nil +} + func (m *mutationDescGetter) RemoveObjectComments(ctx context.Context, id descpb.ID) error { _, err := m.executor.ExecEx( ctx, diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go b/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go index 0031d5ce0d0d..d54ad1a2695c 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go +++ b/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go @@ -36,6 +36,14 @@ type DescriptorReader interface { AddDrainedName(id descpb.ID, nameInfo descpb.NameInfo) } +// SyntheticDescriptorWriter encapsulates logic for +// injecting synthetic descriptors only in the current +// transaction. +type SyntheticDescriptorWriter interface { + MarkDescriptorAsSyntheticDropped(ctx context.Context, id descpb.ID) error + RemoveSyntheticDescFromTxn(ctx context.Context, id descpb.ID) error +} + // NamespaceWriter encapsulates operations used to manipulate // namespaces. type NamespaceWriter interface { @@ -52,6 +60,7 @@ type CommentWriter interface { // namespaces, comments to help support schema changer mutations. type Catalog interface { DescriptorReader + SyntheticDescriptorWriter NamespaceWriter CommentWriter } @@ -224,15 +233,28 @@ func (m *visitor) CreateGcJobForDescriptor( func (m *visitor) MarkDescriptorAsDropped( ctx context.Context, op scop.MarkDescriptorAsDropped, ) error { - descriptor, err := m.catalog.GetAnyDescriptorByID(ctx, op.TableID) + // Before we can mutate the descriptor, drop any synthetic + // one that inserted. + err := m.catalog.RemoveSyntheticDescFromTxn(ctx, op.DescID) + if err != nil { + return err + } + // Mark the descriptor as dropped. + descriptor, err := m.catalog.GetAnyDescriptorByID(ctx, op.DescID) if err != nil { return err } - // Mark table as dropped descriptor.SetDropped() return nil } +func (m *visitor) MarkDescriptorAsDroppedSynthetically( + ctx context.Context, op scop.MarkDescriptorAsDroppedSynthetically, +) error { + // Mark table as dropped inside the current txn. + return m.catalog.MarkDescriptorAsSyntheticDropped(ctx, op.DescID) +} + func (m *visitor) DrainDescriptorName(ctx context.Context, op scop.DrainDescriptorName) error { descriptor, err := m.catalog.GetAnyDescriptorByID(ctx, op.TableID) if err != nil { diff --git a/pkg/sql/schemachanger/scop/mutation.go b/pkg/sql/schemachanger/scop/mutation.go index 66c9b398f762..af341189be6b 100644 --- a/pkg/sql/schemachanger/scop/mutation.go +++ b/pkg/sql/schemachanger/scop/mutation.go @@ -66,10 +66,17 @@ type CreateGcJobForDescriptor struct { DescID descpb.ID } +// MarkDescriptorAsDroppedSynthetically marks a descriptor as dropped within +// a transaction by injecting a synthetic descriptor. +type MarkDescriptorAsDroppedSynthetically struct { + mutationOp + DescID descpb.ID +} + // MarkDescriptorAsDropped marks a descriptor as dropped. type MarkDescriptorAsDropped struct { mutationOp - TableID descpb.ID + DescID descpb.ID } // DrainDescriptorName marks a descriptor as dropped. diff --git a/pkg/sql/schemachanger/scop/mutation_visitor_generated.go b/pkg/sql/schemachanger/scop/mutation_visitor_generated.go index 365ce133ecad..d7f7d831f43c 100644 --- a/pkg/sql/schemachanger/scop/mutation_visitor_generated.go +++ b/pkg/sql/schemachanger/scop/mutation_visitor_generated.go @@ -27,6 +27,7 @@ type MutationVisitor interface { MakeAddedPrimaryIndexPublic(context.Context, MakeAddedPrimaryIndexPublic) error MakeDroppedPrimaryIndexDeleteAndWriteOnly(context.Context, MakeDroppedPrimaryIndexDeleteAndWriteOnly) error CreateGcJobForDescriptor(context.Context, CreateGcJobForDescriptor) error + MarkDescriptorAsDroppedSynthetically(context.Context, MarkDescriptorAsDroppedSynthetically) error MarkDescriptorAsDropped(context.Context, MarkDescriptorAsDropped) error DrainDescriptorName(context.Context, DrainDescriptorName) error UpdateRelationDeps(context.Context, UpdateRelationDeps) error @@ -74,6 +75,11 @@ func (op CreateGcJobForDescriptor) Visit(ctx context.Context, v MutationVisitor) return v.CreateGcJobForDescriptor(ctx, op) } +// Visit is part of the MutationOp interface. +func (op MarkDescriptorAsDroppedSynthetically) Visit(ctx context.Context, v MutationVisitor) error { + return v.MarkDescriptorAsDroppedSynthetically(ctx, op) +} + // Visit is part of the MutationOp interface. func (op MarkDescriptorAsDropped) Visit(ctx context.Context, v MutationVisitor) error { return v.MarkDescriptorAsDropped(ctx, op) diff --git a/pkg/sql/schemachanger/scop/phase.go b/pkg/sql/schemachanger/scop/phase.go index 5cd99be592e2..c8a196aca87a 100644 --- a/pkg/sql/schemachanger/scop/phase.go +++ b/pkg/sql/schemachanger/scop/phase.go @@ -31,5 +31,7 @@ const ( PreCommitPhase // PostCommitPhase refers to execution of ops occurring after the user // transaction has committed (i.e., in the async schema change job). + // Note: Planning rules cannot ever be in this phase, since all those operations + // should be executed in pre-commit. PostCommitPhase ) diff --git a/pkg/sql/schemachanger/scpb/scpb.pb.go b/pkg/sql/schemachanger/scpb/scpb.pb.go index fe54d3600d4b..385c358ce306 100644 --- a/pkg/sql/schemachanger/scpb/scpb.pb.go +++ b/pkg/sql/schemachanger/scpb/scpb.pb.go @@ -30,31 +30,37 @@ type Status int32 const ( Status_UNKNOWN Status = 0 Status_ABSENT Status = 1 - Status_DELETE_ONLY Status = 2 - Status_DELETE_AND_WRITE_ONLY Status = 3 - Status_BACKFILLED Status = 4 - Status_VALIDATED Status = 5 - Status_PUBLIC Status = 6 + Status_DROPPED Status = 2 + Status_DELETE_ONLY Status = 3 + Status_DELETE_AND_WRITE_ONLY Status = 4 + Status_BACKFILLED Status = 5 + Status_VALIDATED Status = 6 + Status_TXN_DROPPED Status = 7 + Status_PUBLIC Status = 8 ) var Status_name = map[int32]string{ 0: "UNKNOWN", 1: "ABSENT", - 2: "DELETE_ONLY", - 3: "DELETE_AND_WRITE_ONLY", - 4: "BACKFILLED", - 5: "VALIDATED", - 6: "PUBLIC", + 2: "DROPPED", + 3: "DELETE_ONLY", + 4: "DELETE_AND_WRITE_ONLY", + 5: "BACKFILLED", + 6: "VALIDATED", + 7: "TXN_DROPPED", + 8: "PUBLIC", } var Status_value = map[string]int32{ "UNKNOWN": 0, "ABSENT": 1, - "DELETE_ONLY": 2, - "DELETE_AND_WRITE_ONLY": 3, - "BACKFILLED": 4, - "VALIDATED": 5, - "PUBLIC": 6, + "DROPPED": 2, + "DELETE_ONLY": 3, + "DELETE_AND_WRITE_ONLY": 4, + "BACKFILLED": 5, + "VALIDATED": 6, + "TXN_DROPPED": 7, + "PUBLIC": 8, } func (x Status) String() string { @@ -865,112 +871,114 @@ func init() { func init() { proto.RegisterFile("sql/schemachanger/scpb/scpb.proto", fileDescriptor_5413c88842564e28) } var fileDescriptor_5413c88842564e28 = []byte{ - // 1680 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0x4f, 0x6f, 0xdb, 0x46, - 0x16, 0x37, 0x25, 0x5a, 0x96, 0x9e, 0x24, 0x8b, 0x9e, 0x64, 0x17, 0xdc, 0x60, 0x21, 0x25, 0x5a, - 0x20, 0x09, 0xb2, 0x80, 0xb4, 0x9b, 0x2c, 0x16, 0x58, 0x03, 0x8b, 0x8d, 0x65, 0xca, 0x58, 0xd6, - 0x8e, 0xe4, 0x52, 0x76, 0xdc, 0x04, 0x2d, 0x04, 0x8a, 0x9c, 0xc8, 0x4c, 0x24, 0x52, 0x26, 0xa9, - 0x38, 0x46, 0x8b, 0xf6, 0xd4, 0x6b, 0xd3, 0x8f, 0xd0, 0x73, 0xef, 0xfd, 0x0e, 0x06, 0x7a, 0x71, - 0x2f, 0x45, 0x80, 0x02, 0x42, 0xab, 0x00, 0x6d, 0xaf, 0xed, 0xa1, 0x40, 0x7b, 0x2a, 0x66, 0x86, - 0xff, 0x24, 0xb9, 0x90, 0x10, 0x39, 0x2e, 0x9a, 0xf6, 0x62, 0x50, 0x43, 0xbe, 0xdf, 0x6f, 0xde, - 0xff, 0x37, 0x63, 0xb8, 0xe2, 0x1c, 0x74, 0xca, 0x8e, 0xb6, 0x8f, 0xbb, 0xaa, 0xb6, 0xaf, 0x9a, - 0x6d, 0x6c, 0x97, 0x1d, 0xad, 0xd7, 0xa2, 0x7f, 0x4a, 0x3d, 0xdb, 0x72, 0x2d, 0x74, 0x59, 0xb3, - 0xb4, 0x47, 0xb6, 0xa5, 0x6a, 0xfb, 0x25, 0xe7, 0xa0, 0x53, 0x1a, 0xf9, 0xb8, 0x44, 0xbe, 0xbb, - 0xf4, 0x37, 0x02, 0xa2, 0xa9, 0xae, 0xda, 0xb1, 0xda, 0x65, 0x1d, 0x33, 0x00, 0xd7, 0xee, 0x6b, - 0x6e, 0xdf, 0xc6, 0x3a, 0x83, 0xb9, 0x74, 0xb1, 0x6d, 0xb5, 0x2d, 0xfa, 0x58, 0x26, 0x4f, 0x6c, - 0xb5, 0x38, 0x48, 0x43, 0xa6, 0xda, 0xc1, 0x5d, 0x6c, 0xba, 0xdb, 0x94, 0xed, 0x36, 0x24, 0x34, - 0xab, 0xd3, 0xef, 0x9a, 0x22, 0x77, 0x99, 0xbb, 0x9e, 0xbe, 0x79, 0xbd, 0x34, 0x8d, 0xbe, 0xb4, - 0x4e, 0xbf, 0x57, 0x3c, 0x39, 0xd4, 0x80, 0x6c, 0xcf, 0x36, 0xba, 0xaa, 0x7d, 0xd4, 0x34, 0x4c, - 0x1d, 0x3f, 0x11, 0x63, 0x14, 0xa8, 0x34, 0x1d, 0x68, 0x9b, 0x89, 0xc9, 0x44, 0x4a, 0xc9, 0xf4, - 0x22, 0xbf, 0xd0, 0x3d, 0xc8, 0x39, 0x58, 0xb3, 0x4c, 0x3d, 0x84, 0x8d, 0x53, 0xd8, 0x7f, 0x4c, - 0x87, 0x6d, 0xf8, 0x82, 0x0c, 0x78, 0xd9, 0x19, 0xf9, 0x8d, 0x30, 0x5c, 0x70, 0xf0, 0x41, 0x1f, - 0x9b, 0x1a, 0x6e, 0xea, 0xb8, 0x87, 0x4d, 0x1d, 0x9b, 0xda, 0x91, 0xc8, 0x53, 0xf8, 0x7f, 0xcd, - 0x02, 0xcf, 0x84, 0xa5, 0x40, 0x56, 0x41, 0xce, 0xc4, 0x1a, 0x6a, 0xc2, 0x4a, 0xdf, 0x34, 0x0e, - 0xfa, 0xb8, 0xa9, 0x59, 0xa6, 0xe3, 0xda, 0xaa, 0x61, 0xba, 0xe2, 0x22, 0x25, 0xb9, 0x39, 0x9d, - 0x64, 0x97, 0x8a, 0xae, 0x07, 0x92, 0x8a, 0xd0, 0x1f, 0x5b, 0x41, 0x6f, 0x82, 0xa0, 0xed, 0x63, - 0xed, 0x51, 0x14, 0x3f, 0x41, 0xf1, 0xff, 0x39, 0x83, 0x0f, 0x89, 0x64, 0x04, 0x3e, 0xa7, 0x8d, - 0x2e, 0xa0, 0x0d, 0x48, 0xfa, 0x4a, 0x89, 0x4b, 0x14, 0xf5, 0xc6, 0xec, 0xa6, 0x51, 0x02, 0x59, - 0xd4, 0x02, 0xa4, 0xe3, 0x07, 0x6a, 0xbf, 0xe3, 0x36, 0xf1, 0x93, 0x9e, 0x8d, 0x1d, 0xc7, 0xb0, - 0x4c, 0x31, 0x49, 0x11, 0x6f, 0x4d, 0x47, 0x94, 0x98, 0x6c, 0x35, 0x10, 0x55, 0x56, 0xf4, 0xf1, - 0x25, 0xb4, 0x0a, 0xfc, 0x63, 0x03, 0x1f, 0x8a, 0x29, 0x8a, 0x7a, 0x75, 0x3a, 0xea, 0x5d, 0x03, - 0x1f, 0x2a, 0x54, 0x06, 0xc9, 0xb0, 0xe4, 0x1e, 0xf5, 0xb0, 0x82, 0x1f, 0x88, 0x40, 0xc5, 0xcb, - 0xd3, 0xc5, 0x77, 0x98, 0x00, 0xb6, 0xa9, 0xae, 0xbe, 0x3c, 0xfa, 0x2f, 0x2c, 0xba, 0x6a, 0xab, - 0x83, 0xc5, 0x34, 0x05, 0xba, 0x36, 0x03, 0x10, 0xf9, 0x5c, 0x61, 0x52, 0xe8, 0x3e, 0x64, 0xad, - 0xbe, 0xbb, 0x61, 0xd9, 0xd8, 0x68, 0x9b, 0x9b, 0xf8, 0x48, 0xcc, 0xcc, 0x1a, 0x91, 0xf5, 0xbe, - 0xdb, 0xb2, 0xfa, 0xa6, 0x1e, 0xca, 0x2a, 0xa3, 0x50, 0x68, 0x0f, 0x32, 0x86, 0x19, 0x81, 0xce, - 0xce, 0x6a, 0x7f, 0xd9, 0x1c, 0x47, 0x1e, 0x01, 0x42, 0x0f, 0xe1, 0xa2, 0x8d, 0x3b, 0xaa, 0x6b, - 0x58, 0xa6, 0x17, 0xfb, 0x7a, 0xdd, 0xac, 0x1c, 0x89, 0xcb, 0x94, 0xe0, 0xdf, 0xd3, 0x09, 0x94, - 0x53, 0xa4, 0x95, 0x53, 0x31, 0xd1, 0x1e, 0x64, 0xfd, 0xb0, 0xaa, 0x1f, 0x9a, 0xd8, 0x16, 0x73, - 0xb3, 0x46, 0x7b, 0x23, 0x22, 0xa6, 0x57, 0x8e, 0x94, 0x51, 0x1c, 0x12, 0x3f, 0xc4, 0x87, 0xa2, - 0x30, 0x6b, 0xfc, 0xd0, 0x00, 0xa0, 0x32, 0xa4, 0x7e, 0xb2, 0x0f, 0xc4, 0x95, 0x59, 0xeb, 0x67, - 0x83, 0x2e, 0x29, 0x9e, 0x1c, 0xc9, 0x34, 0x5d, 0x75, 0xd5, 0x96, 0xea, 0x60, 0x11, 0xcd, 0x9a, - 0x69, 0x92, 0x27, 0xa1, 0x04, 0xb2, 0xab, 0xfc, 0xf1, 0x47, 0x05, 0xae, 0xf8, 0x35, 0x07, 0x89, - 0x1d, 0xd5, 0x6e, 0x63, 0x17, 0xbd, 0x05, 0x59, 0xcc, 0x4a, 0x7d, 0x93, 0x16, 0x7f, 0xaf, 0xc2, - 0xcf, 0x50, 0x98, 0xa3, 0x1d, 0xa2, 0x92, 0x3c, 0x1e, 0x14, 0x16, 0x4e, 0x06, 0x05, 0x4e, 0xc9, - 0xe0, 0x68, 0xe7, 0xd8, 0x86, 0x94, 0x6e, 0xd8, 0x58, 0x23, 0x7e, 0xa2, 0x35, 0x7f, 0x79, 0x96, - 0xc2, 0xc6, 0xf6, 0x56, 0x92, 0x7c, 0x49, 0x25, 0x04, 0x29, 0xfe, 0x1d, 0x52, 0xc1, 0x3a, 0x4a, - 0xc3, 0xd2, 0x6e, 0x6d, 0xb3, 0x56, 0xdf, 0xab, 0x09, 0x0b, 0x68, 0x09, 0xe2, 0x6b, 0x92, 0x24, - 0x70, 0x28, 0x09, 0xbc, 0xa4, 0xd4, 0xb7, 0x85, 0x58, 0xf1, 0xd3, 0x18, 0x24, 0x58, 0x27, 0x42, - 0x3a, 0x24, 0x69, 0x0a, 0x35, 0x0d, 0x9d, 0xea, 0x98, 0xad, 0xc8, 0xc3, 0x41, 0x61, 0x89, 0x66, - 0x97, 0x2c, 0xfd, 0x34, 0x28, 0xac, 0xb6, 0x0d, 0x77, 0xbf, 0xdf, 0x2a, 0x69, 0x56, 0xb7, 0x1c, - 0xec, 0x50, 0x6f, 0x85, 0xcf, 0xe5, 0xde, 0xa3, 0x76, 0x79, 0xb2, 0xab, 0x96, 0x64, 0x49, 0x59, - 0xa2, 0xd0, 0xb2, 0x8e, 0xba, 0x90, 0x7a, 0xa0, 0x76, 0x8d, 0xce, 0x11, 0xa1, 0x89, 0x51, 0x9a, - 0xed, 0xe1, 0xa0, 0x90, 0xdc, 0xa0, 0x8b, 0x94, 0xe7, 0xf6, 0x8b, 0xf2, 0xf8, 0x18, 0x4a, 0x92, - 0x51, 0xc8, 0x3a, 0x2a, 0x40, 0xda, 0xa3, 0x33, 0xd5, 0x2e, 0xa6, 0xdd, 0x2f, 0xa5, 0x00, 0x5b, - 0xaa, 0xa9, 0x5d, 0x8c, 0xaa, 0x41, 0xe7, 0xe6, 0x4f, 0xaf, 0x37, 0x07, 0x1d, 0x12, 0x17, 0x5e, - 0xbb, 0x96, 0xb0, 0xa3, 0xd9, 0x46, 0xcf, 0xb5, 0xec, 0x0a, 0x4f, 0x1c, 0xea, 0xb7, 0xef, 0x55, - 0xfe, 0x5b, 0x12, 0x36, 0x27, 0x31, 0xc8, 0x44, 0xdb, 0xf1, 0x39, 0xd9, 0xb4, 0x02, 0x8b, 0xd1, - 0x99, 0xe1, 0xea, 0x2f, 0xa8, 0x40, 0xb7, 0x34, 0xa1, 0x01, 0x13, 0x45, 0x4f, 0x39, 0xf8, 0xb3, - 0xe5, 0xee, 0x63, 0xbb, 0x39, 0x32, 0x86, 0x90, 0x8d, 0xc7, 0xe9, 0xc6, 0xef, 0x0f, 0x07, 0x85, - 0x0b, 0x75, 0xf2, 0x45, 0x54, 0x43, 0xaa, 0xc4, 0xff, 0x5e, 0x58, 0x09, 0x06, 0xa1, 0x5c, 0xb0, - 0x26, 0x70, 0x75, 0xcf, 0xa4, 0x1f, 0xc7, 0x60, 0x79, 0x74, 0x14, 0xf9, 0x0d, 0x19, 0x75, 0x7f, - 0x7c, 0xa8, 0x63, 0xa6, 0x5c, 0x3f, 0x0b, 0x9b, 0x8d, 0x4c, 0x7a, 0x9e, 0xb1, 0x3e, 0x8f, 0x03, - 0x9a, 0x1c, 0xac, 0xce, 0x2f, 0xb3, 0x59, 0x32, 0x8c, 0x65, 0x36, 0xcb, 0x9c, 0xf9, 0x32, 0xdb, - 0xc7, 0x50, 0x92, 0x8c, 0x82, 0xd2, 0xa5, 0x83, 0x01, 0x34, 0x08, 0xd2, 0xad, 0xe1, 0xa0, 0x00, - 0xbe, 0x05, 0xe6, 0x56, 0x0d, 0x7c, 0x02, 0x59, 0x47, 0x77, 0xbc, 0xee, 0xc6, 0xd3, 0x12, 0xfd, - 0x9f, 0x17, 0x19, 0x70, 0x23, 0x0d, 0xaf, 0x78, 0x0d, 0x78, 0xf2, 0x6b, 0xb4, 0x3e, 0x27, 0x81, - 0xdf, 0x6d, 0x54, 0x1b, 0xac, 0x40, 0xd7, 0xf7, 0x6a, 0x0d, 0x21, 0xe6, 0x39, 0xf6, 0x8b, 0x18, - 0x08, 0xe3, 0xc3, 0xec, 0x39, 0xb9, 0xd5, 0x80, 0x64, 0x50, 0x09, 0x98, 0x57, 0x6b, 0x84, 0xe5, - 0x0c, 0xb3, 0x7f, 0xc9, 0x60, 0x19, 0x8f, 0x7a, 0x00, 0x41, 0x04, 0x39, 0x62, 0xfc, 0x72, 0xfc, - 0x7a, 0xb6, 0xf2, 0xfa, 0x70, 0x50, 0x48, 0xf9, 0xee, 0x77, 0xce, 0x24, 0x86, 0x52, 0x7e, 0x0c, - 0x39, 0x9e, 0x75, 0x3f, 0x89, 0x41, 0x6e, 0x6c, 0x94, 0x3f, 0x27, 0xe3, 0x22, 0xe0, 0x69, 0x5f, - 0x8a, 0xd1, 0xbe, 0x44, 0x9f, 0xc9, 0x1a, 0x99, 0xf1, 0xbd, 0x5e, 0x45, 0x9f, 0xc7, 0x2c, 0xc3, - 0xbf, 0x7c, 0xcb, 0xa0, 0xbf, 0x42, 0xea, 0xb1, 0xda, 0x31, 0x74, 0xd5, 0xc5, 0x3a, 0x3d, 0x70, - 0x25, 0x95, 0x70, 0xc1, 0xb3, 0xdb, 0x7b, 0x90, 0xf4, 0xa3, 0x7c, 0x3c, 0x1d, 0xb9, 0x97, 0x9b, - 0x8e, 0xde, 0x06, 0x3e, 0x88, 0xc3, 0xca, 0xc4, 0xd9, 0xe6, 0xd5, 0x2c, 0x77, 0x6f, 0x43, 0xae, - 0xef, 0x60, 0x27, 0x34, 0x66, 0x34, 0x41, 0x72, 0xbb, 0xa3, 0xaf, 0xe6, 0xd4, 0x71, 0x9c, 0x09, - 0x5d, 0x81, 0x4c, 0xf4, 0xf8, 0x49, 0x8b, 0x60, 0x4a, 0x49, 0x47, 0xce, 0x90, 0x9e, 0x43, 0x3e, - 0x8b, 0x01, 0x4f, 0x8e, 0x85, 0xe7, 0xe4, 0x03, 0x9b, 0xec, 0x2b, 0x72, 0x5e, 0x8a, 0x51, 0x8b, - 0x90, 0xfa, 0x94, 0x89, 0x9e, 0x79, 0xe6, 0xa4, 0x1b, 0xe1, 0x40, 0x06, 0xa4, 0xd8, 0x6f, 0xa7, - 0x6e, 0x7a, 0x2e, 0xd8, 0x24, 0x99, 0x28, 0xf9, 0x8b, 0x73, 0xb2, 0x85, 0xe8, 0x9e, 0x4d, 0xbf, - 0xe1, 0x60, 0x91, 0xda, 0xed, 0xd5, 0x35, 0x6a, 0xa8, 0x69, 0x76, 0xe4, 0x56, 0x00, 0x75, 0x21, - 0xab, 0x07, 0xb3, 0x55, 0x98, 0x68, 0xff, 0x1f, 0x0e, 0x0a, 0x09, 0x32, 0x74, 0xcd, 0xad, 0x75, - 0x26, 0x84, 0x97, 0x75, 0xa4, 0xb2, 0x6b, 0x8c, 0xd0, 0xbe, 0x94, 0x88, 0x6c, 0x69, 0x6e, 0xa2, - 0x04, 0x01, 0x0e, 0x0a, 0xd7, 0x53, 0x1e, 0xd0, 0xe4, 0x7d, 0x03, 0x6a, 0x43, 0xca, 0xb2, 0x8d, - 0xb6, 0x61, 0x86, 0x3b, 0x78, 0x8d, 0xd4, 0x94, 0x3a, 0x5d, 0x9c, 0x7b, 0x0f, 0x49, 0x06, 0x2e, - 0xeb, 0xe8, 0x1d, 0x58, 0xf6, 0x88, 0x58, 0x81, 0xf1, 0x8b, 0xc9, 0xee, 0x70, 0x50, 0xc8, 0x32, - 0x36, 0x56, 0x83, 0xce, 0xa6, 0xaf, 0x64, 0xad, 0x28, 0x24, 0xea, 0x41, 0xc6, 0xf6, 0x5d, 0x4c, - 0x34, 0xe5, 0xa9, 0xa6, 0x77, 0x86, 0x83, 0x42, 0x3a, 0x70, 0xfd, 0xdc, 0xca, 0xa6, 0x03, 0x0a, - 0x59, 0x47, 0xef, 0x73, 0xb0, 0x12, 0x52, 0xfa, 0x3a, 0x2f, 0x52, 0x9d, 0xdf, 0x18, 0x0e, 0x0a, - 0x42, 0xc0, 0x7b, 0x96, 0x6a, 0x0b, 0xf6, 0x18, 0x6a, 0xd0, 0xef, 0x13, 0x61, 0xbf, 0xf7, 0x5b, - 0x19, 0x0f, 0x2b, 0x13, 0xd7, 0x44, 0x7f, 0x04, 0xc4, 0xef, 0x37, 0x20, 0x7e, 0xe0, 0x20, 0x37, - 0x76, 0xe3, 0x76, 0xce, 0x43, 0x16, 0x72, 0x61, 0xd9, 0x3a, 0x34, 0xb1, 0xdd, 0x0c, 0xba, 0x4e, - 0x78, 0x00, 0xc8, 0xd0, 0x4b, 0xbf, 0xb3, 0x69, 0x3d, 0x19, 0x2b, 0xc4, 0xd2, 0x8b, 0xdf, 0x73, - 0x70, 0xf1, 0xb4, 0xfb, 0xcc, 0x73, 0x6a, 0x7f, 0x26, 0x40, 0xd8, 0x9a, 0xa2, 0x0a, 0x9f, 0x61, - 0xf3, 0x8b, 0x30, 0x78, 0xde, 0x36, 0xbc, 0xf3, 0xe0, 0xcb, 0xef, 0x40, 0xc5, 0xef, 0x38, 0x48, - 0xb0, 0xcb, 0x53, 0x52, 0x5e, 0xd8, 0xc9, 0x75, 0xac, 0xbc, 0xb0, 0xd7, 0xf3, 0x97, 0x17, 0x06, - 0x2e, 0xeb, 0xe8, 0x5d, 0x10, 0xfc, 0x7f, 0x12, 0xb9, 0xf5, 0xd6, 0x43, 0xac, 0xb9, 0x8e, 0x37, - 0x57, 0x28, 0x24, 0xd9, 0xa4, 0xb1, 0x77, 0x73, 0xf2, 0x4e, 0x70, 0x15, 0x7f, 0xe4, 0x20, 0xe9, - 0x5f, 0xf6, 0x92, 0x2c, 0xf2, 0xaf, 0x7b, 0xc7, 0xb2, 0xc8, 0xff, 0x64, 0xfe, 0x2c, 0xf2, 0x09, - 0x7e, 0x7d, 0xdd, 0x6f, 0x3c, 0x81, 0x44, 0xc3, 0x55, 0xdd, 0xbe, 0x33, 0x7a, 0xd9, 0x00, 0x90, - 0x58, 0xab, 0x34, 0xaa, 0xb5, 0x1d, 0x81, 0x43, 0x39, 0x48, 0x4b, 0xd5, 0xad, 0xea, 0x4e, 0xb5, - 0x59, 0xaf, 0x6d, 0xdd, 0x13, 0x62, 0xe8, 0x2f, 0xf0, 0x27, 0x6f, 0x61, 0xad, 0x26, 0x35, 0xf7, - 0x14, 0xd9, 0x7f, 0x15, 0x47, 0xcb, 0x00, 0x95, 0xb5, 0xf5, 0xcd, 0x0d, 0x79, 0x6b, 0xab, 0x2a, - 0x09, 0x3c, 0xca, 0x42, 0xea, 0xee, 0xda, 0x96, 0x2c, 0xad, 0xed, 0x54, 0x25, 0x61, 0x91, 0xc0, - 0x6e, 0xef, 0x56, 0xb6, 0xe4, 0x75, 0x21, 0x51, 0xb9, 0x7a, 0xfc, 0x55, 0x7e, 0xe1, 0x78, 0x98, - 0xe7, 0x4e, 0x86, 0x79, 0xee, 0xd9, 0x30, 0xcf, 0x7d, 0x39, 0xcc, 0x73, 0x1f, 0x3e, 0xcf, 0x2f, - 0x9c, 0x3c, 0xcf, 0x2f, 0x3c, 0x7b, 0x9e, 0x5f, 0xb8, 0xcf, 0x93, 0x6d, 0xb7, 0x12, 0xf4, 0x62, - 0xfd, 0xd6, 0xcf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x47, 0xea, 0xd4, 0x4b, 0xd7, 0x1d, 0x00, 0x00, + // 1698 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0xcd, 0x6f, 0xdb, 0xc8, + 0x15, 0x37, 0x25, 0x5a, 0x1f, 0x4f, 0x92, 0x45, 0x4f, 0xd2, 0x82, 0x0d, 0x0a, 0x29, 0x51, 0x81, + 0x24, 0x48, 0x01, 0xa9, 0x4d, 0x8a, 0x02, 0x35, 0x50, 0x34, 0x96, 0x29, 0xa3, 0xac, 0x1d, 0x49, + 0xa5, 0xec, 0x38, 0x09, 0x5a, 0x08, 0x14, 0x39, 0x91, 0x99, 0x48, 0xa4, 0x4c, 0x52, 0x71, 0x8c, + 0x16, 0xed, 0xa9, 0xd7, 0xa6, 0xa7, 0x9e, 0x7b, 0xee, 0xbd, 0xff, 0x83, 0x81, 0x5e, 0xdc, 0x4b, + 0x11, 0xa0, 0x80, 0xd0, 0x55, 0x80, 0xdd, 0xbd, 0xee, 0x1e, 0x16, 0xd8, 0x3d, 0x2d, 0x66, 0x86, + 0x5f, 0x92, 0xbc, 0x90, 0x10, 0x39, 0x5e, 0x6c, 0x76, 0x2f, 0x06, 0x35, 0xe4, 0xfb, 0xfd, 0xe6, + 0x7d, 0xbf, 0x19, 0xc3, 0x0d, 0xe7, 0xa8, 0x57, 0x71, 0xb4, 0x43, 0xdc, 0x57, 0xb5, 0x43, 0xd5, + 0xec, 0x62, 0xbb, 0xe2, 0x68, 0x83, 0x0e, 0xfd, 0x53, 0x1e, 0xd8, 0x96, 0x6b, 0xa1, 0xeb, 0x9a, + 0xa5, 0x3d, 0xb7, 0x2d, 0x55, 0x3b, 0x2c, 0x3b, 0x47, 0xbd, 0xf2, 0xc4, 0xc7, 0x65, 0xf2, 0xdd, + 0xb5, 0x1f, 0x11, 0x10, 0x4d, 0x75, 0xd5, 0x9e, 0xd5, 0xad, 0xe8, 0x98, 0x01, 0xb8, 0xf6, 0x50, + 0x73, 0x87, 0x36, 0xd6, 0x19, 0xcc, 0xb5, 0xab, 0x5d, 0xab, 0x6b, 0xd1, 0xc7, 0x0a, 0x79, 0x62, + 0xab, 0xa5, 0x51, 0x06, 0xb2, 0xb5, 0x1e, 0xee, 0x63, 0xd3, 0x6d, 0x52, 0xb6, 0xfb, 0x90, 0xd0, + 0xac, 0xde, 0xb0, 0x6f, 0x8a, 0xdc, 0x75, 0xee, 0x76, 0xe6, 0xee, 0xed, 0xf2, 0x3c, 0xfa, 0xf2, + 0x16, 0xfd, 0x5e, 0xf1, 0xe4, 0x50, 0x0b, 0x72, 0x03, 0xdb, 0xe8, 0xab, 0xf6, 0x49, 0xdb, 0x30, + 0x75, 0xfc, 0x52, 0x8c, 0x51, 0xa0, 0xf2, 0x7c, 0xa0, 0x26, 0x13, 0x93, 0x89, 0x94, 0x92, 0x1d, + 0x44, 0x7e, 0xa1, 0xc7, 0x90, 0x77, 0xb0, 0x66, 0x99, 0x7a, 0x08, 0x1b, 0xa7, 0xb0, 0x3f, 0x99, + 0x0f, 0xdb, 0xf2, 0x05, 0x19, 0xf0, 0x9a, 0x33, 0xf1, 0x1b, 0x61, 0xb8, 0xe2, 0xe0, 0xa3, 0x21, + 0x36, 0x35, 0xdc, 0xd6, 0xf1, 0x00, 0x9b, 0x3a, 0x36, 0xb5, 0x13, 0x91, 0xa7, 0xf0, 0x3f, 0x5b, + 0x04, 0x9e, 0x09, 0x4b, 0x81, 0xac, 0x82, 0x9c, 0x99, 0x35, 0xd4, 0x86, 0xf5, 0xa1, 0x69, 0x1c, + 0x0d, 0x71, 0x5b, 0xb3, 0x4c, 0xc7, 0xb5, 0x55, 0xc3, 0x74, 0xc5, 0x55, 0x4a, 0x72, 0x77, 0x3e, + 0xc9, 0x3e, 0x15, 0xdd, 0x0a, 0x24, 0x15, 0x61, 0x38, 0xb5, 0x82, 0x7e, 0x07, 0x82, 0x76, 0x88, + 0xb5, 0xe7, 0x51, 0xfc, 0x04, 0xc5, 0xff, 0xe9, 0x02, 0x3e, 0x24, 0x92, 0x11, 0xf8, 0xbc, 0x36, + 0xb9, 0x80, 0xb6, 0x21, 0xe5, 0x2b, 0x25, 0x26, 0x29, 0xea, 0x9d, 0xc5, 0x4d, 0xa3, 0x04, 0xb2, + 0xa8, 0x03, 0x48, 0xc7, 0x4f, 0xd5, 0x61, 0xcf, 0x6d, 0xe3, 0x97, 0x03, 0x1b, 0x3b, 0x8e, 0x61, + 0x99, 0x62, 0x8a, 0x22, 0xde, 0x9b, 0x8f, 0x28, 0x31, 0xd9, 0x5a, 0x20, 0xaa, 0xac, 0xeb, 0xd3, + 0x4b, 0x68, 0x03, 0xf8, 0x17, 0x06, 0x3e, 0x16, 0xd3, 0x14, 0xf5, 0xe6, 0x7c, 0xd4, 0x87, 0x06, + 0x3e, 0x56, 0xa8, 0x0c, 0x92, 0x21, 0xe9, 0x9e, 0x0c, 0xb0, 0x82, 0x9f, 0x8a, 0x40, 0xc5, 0x2b, + 0xf3, 0xc5, 0xf7, 0x98, 0x00, 0xb6, 0xa9, 0xae, 0xbe, 0x3c, 0xfa, 0x25, 0xac, 0xba, 0x6a, 0xa7, + 0x87, 0xc5, 0x0c, 0x05, 0xba, 0xb5, 0x00, 0x10, 0xf9, 0x5c, 0x61, 0x52, 0xe8, 0x09, 0xe4, 0xac, + 0xa1, 0xbb, 0x6d, 0xd9, 0xd8, 0xe8, 0x9a, 0x3b, 0xf8, 0x44, 0xcc, 0x2e, 0x1a, 0x91, 0x8d, 0xa1, + 0xdb, 0xb1, 0x86, 0xa6, 0x1e, 0xca, 0x2a, 0x93, 0x50, 0xe8, 0x00, 0xb2, 0x86, 0x19, 0x81, 0xce, + 0x2d, 0x6a, 0x7f, 0xd9, 0x9c, 0x46, 0x9e, 0x00, 0x42, 0xcf, 0xe0, 0xaa, 0x8d, 0x7b, 0xaa, 0x6b, + 0x58, 0xa6, 0x17, 0xfb, 0x7a, 0xc3, 0xac, 0x9e, 0x88, 0x6b, 0x94, 0xe0, 0xe7, 0xf3, 0x09, 0x94, + 0x73, 0xa4, 0x95, 0x73, 0x31, 0xd1, 0x01, 0xe4, 0xfc, 0xb0, 0x6a, 0x1c, 0x9b, 0xd8, 0x16, 0xf3, + 0x8b, 0x46, 0x7b, 0x2b, 0x22, 0xa6, 0x57, 0x4f, 0x94, 0x49, 0x1c, 0x12, 0x3f, 0xc4, 0x87, 0xa2, + 0xb0, 0x68, 0xfc, 0xd0, 0x00, 0xa0, 0x32, 0xa4, 0x7e, 0xb2, 0x0f, 0xc4, 0xf5, 0x45, 0xeb, 0x67, + 0x8b, 0x2e, 0x29, 0x9e, 0x1c, 0xc9, 0x34, 0x5d, 0x75, 0xd5, 0x8e, 0xea, 0x60, 0x11, 0x2d, 0x9a, + 0x69, 0x92, 0x27, 0xa1, 0x04, 0xb2, 0x1b, 0xfc, 0xe9, 0x3f, 0x8a, 0x5c, 0xe9, 0x43, 0x0e, 0x12, + 0x7b, 0xaa, 0xdd, 0xc5, 0x2e, 0xfa, 0x3d, 0xe4, 0x30, 0x2b, 0xf5, 0x6d, 0x5a, 0xfc, 0xbd, 0x0a, + 0xbf, 0x40, 0x61, 0x8e, 0x76, 0x88, 0x6a, 0xea, 0x74, 0x54, 0x5c, 0x39, 0x1b, 0x15, 0x39, 0x25, + 0x8b, 0xa3, 0x9d, 0xa3, 0x09, 0x69, 0xdd, 0xb0, 0xb1, 0x46, 0xfc, 0x44, 0x6b, 0xfe, 0xda, 0x22, + 0x85, 0x8d, 0xed, 0xad, 0x2c, 0xf9, 0x92, 0x4a, 0x08, 0x52, 0xfa, 0x31, 0xa4, 0x83, 0x75, 0x94, + 0x81, 0xe4, 0x7e, 0x7d, 0xa7, 0xde, 0x38, 0xa8, 0x0b, 0x2b, 0x28, 0x09, 0xf1, 0x4d, 0x49, 0x12, + 0x38, 0x94, 0x02, 0x5e, 0x52, 0x1a, 0x4d, 0x21, 0x56, 0xfa, 0x77, 0x0c, 0x12, 0xac, 0x13, 0x21, + 0x1d, 0x52, 0x34, 0x85, 0xda, 0x86, 0x4e, 0x75, 0xcc, 0x55, 0xe5, 0xf1, 0xa8, 0x98, 0xa4, 0xd9, + 0x25, 0x4b, 0x5f, 0x8c, 0x8a, 0x1b, 0x5d, 0xc3, 0x3d, 0x1c, 0x76, 0xca, 0x9a, 0xd5, 0xaf, 0x04, + 0x3b, 0xd4, 0x3b, 0xe1, 0x73, 0x65, 0xf0, 0xbc, 0x5b, 0x99, 0xed, 0xaa, 0x65, 0x59, 0x52, 0x92, + 0x14, 0x5a, 0xd6, 0x51, 0x1f, 0xd2, 0x4f, 0xd5, 0xbe, 0xd1, 0x3b, 0x21, 0x34, 0x31, 0x4a, 0xd3, + 0x1c, 0x8f, 0x8a, 0xa9, 0x6d, 0xba, 0x48, 0x79, 0xee, 0xbf, 0x2d, 0x8f, 0x8f, 0xa1, 0xa4, 0x18, + 0x85, 0xac, 0xa3, 0x22, 0x64, 0x3c, 0x3a, 0x53, 0xed, 0x63, 0xda, 0xfd, 0xd2, 0x0a, 0xb0, 0xa5, + 0xba, 0xda, 0xc7, 0xa8, 0x16, 0x74, 0x6e, 0xfe, 0xfc, 0x7a, 0x73, 0xd4, 0x23, 0x71, 0xe1, 0xb5, + 0x6b, 0x09, 0x3b, 0x9a, 0x6d, 0x0c, 0x5c, 0xcb, 0xae, 0xf2, 0xc4, 0xa1, 0x7e, 0xfb, 0xde, 0xe0, + 0x3f, 0x26, 0x61, 0x73, 0x16, 0x83, 0x6c, 0xb4, 0x1d, 0x5f, 0x92, 0x4d, 0xab, 0xb0, 0x1a, 0x9d, + 0x19, 0x6e, 0x7e, 0x85, 0x0a, 0x74, 0x4b, 0x33, 0x1a, 0x30, 0x51, 0xf4, 0x8a, 0x83, 0xef, 0x5b, + 0xee, 0x21, 0xb6, 0xdb, 0x13, 0x63, 0x08, 0xd9, 0x78, 0x9c, 0x6e, 0xfc, 0xc9, 0x78, 0x54, 0xbc, + 0xd2, 0x20, 0x5f, 0x44, 0x35, 0xa4, 0x4a, 0xfc, 0xea, 0xad, 0x95, 0x60, 0x10, 0xca, 0x15, 0x6b, + 0x06, 0x57, 0xf7, 0x4c, 0xfa, 0xcf, 0x18, 0xac, 0x4d, 0x8e, 0x22, 0xdf, 0x20, 0xa3, 0x1e, 0x4e, + 0x0f, 0x75, 0xcc, 0x94, 0x5b, 0x17, 0x61, 0xb3, 0x89, 0x49, 0xcf, 0x33, 0xd6, 0x7f, 0xe3, 0x80, + 0x66, 0x07, 0xab, 0xcb, 0xcb, 0x6c, 0x96, 0x0c, 0x53, 0x99, 0xcd, 0x32, 0x67, 0xb9, 0xcc, 0xf6, + 0x31, 0x94, 0x14, 0xa3, 0xa0, 0x74, 0x99, 0x60, 0x00, 0x0d, 0x82, 0x74, 0x77, 0x3c, 0x2a, 0x82, + 0x6f, 0x81, 0xa5, 0x55, 0x03, 0x9f, 0x40, 0xd6, 0xd1, 0x03, 0xaf, 0xbb, 0xf1, 0xb4, 0x44, 0xff, + 0xe2, 0x6d, 0x06, 0xdc, 0x48, 0xc3, 0x2b, 0xdd, 0x02, 0x9e, 0xfc, 0x9a, 0xac, 0xcf, 0x29, 0xe0, + 0xf7, 0x5b, 0xb5, 0x16, 0x2b, 0xd0, 0x8d, 0x83, 0x7a, 0x4b, 0x88, 0x79, 0x8e, 0xfd, 0x5f, 0x0c, + 0x84, 0xe9, 0x61, 0xf6, 0x92, 0xdc, 0x6a, 0x40, 0x2a, 0xa8, 0x04, 0xcc, 0xab, 0x75, 0xc2, 0x72, + 0x81, 0xd9, 0x9f, 0x34, 0x58, 0xc6, 0xa3, 0x01, 0x40, 0x10, 0x41, 0x8e, 0x18, 0xbf, 0x1e, 0xbf, + 0x9d, 0xab, 0xfe, 0x76, 0x3c, 0x2a, 0xa6, 0x7d, 0xf7, 0x3b, 0x17, 0x12, 0x43, 0x69, 0x3f, 0x86, + 0x1c, 0xcf, 0xba, 0xff, 0x8a, 0x41, 0x7e, 0x6a, 0x94, 0xbf, 0x24, 0xe3, 0x22, 0xe0, 0x69, 0x5f, + 0x8a, 0xd1, 0xbe, 0x44, 0x9f, 0xc9, 0x1a, 0x99, 0xf1, 0xbd, 0x5e, 0x45, 0x9f, 0xa7, 0x2c, 0xc3, + 0xbf, 0x7b, 0xcb, 0xa0, 0x1f, 0x42, 0xfa, 0x85, 0xda, 0x33, 0x74, 0xd5, 0xc5, 0x3a, 0x3d, 0x70, + 0xa5, 0x94, 0x70, 0xc1, 0xb3, 0xdb, 0x9f, 0x21, 0xe5, 0x47, 0xf9, 0x74, 0x3a, 0x72, 0xef, 0x36, + 0x1d, 0xbd, 0x0d, 0xfc, 0x35, 0x0e, 0xeb, 0x33, 0x67, 0x9b, 0xf7, 0xb3, 0xdc, 0xfd, 0x01, 0xf2, + 0x43, 0x07, 0x3b, 0xa1, 0x31, 0xa3, 0x09, 0x92, 0xdf, 0x9f, 0x7c, 0xb5, 0xa4, 0x8e, 0xd3, 0x4c, + 0xe8, 0x06, 0x64, 0xa3, 0xc7, 0x4f, 0x5a, 0x04, 0xd3, 0x4a, 0x26, 0x72, 0x86, 0xf4, 0x1c, 0xf2, + 0x9f, 0x18, 0xf0, 0xe4, 0x58, 0x78, 0x49, 0x3e, 0xb0, 0xc9, 0xbe, 0x22, 0xe7, 0xa5, 0x18, 0xb5, + 0x08, 0xa9, 0x4f, 0xd9, 0xe8, 0x99, 0x67, 0x49, 0xba, 0x09, 0x0e, 0x64, 0x40, 0x9a, 0xfd, 0x76, + 0x1a, 0xa6, 0xe7, 0x82, 0x1d, 0x92, 0x89, 0x92, 0xbf, 0xb8, 0x24, 0x5b, 0x88, 0xee, 0xd9, 0xf4, + 0x23, 0x0e, 0x56, 0xa9, 0xdd, 0xde, 0x5f, 0xa3, 0x86, 0x9a, 0xe6, 0x26, 0x6e, 0x05, 0x50, 0x1f, + 0x72, 0x7a, 0x30, 0x5b, 0x85, 0x89, 0xf6, 0xeb, 0xf1, 0xa8, 0x98, 0x20, 0x43, 0xd7, 0xd2, 0x5a, + 0x67, 0x43, 0x78, 0x59, 0x47, 0x2a, 0xbb, 0xc6, 0x08, 0xed, 0x4b, 0x89, 0xc8, 0x96, 0x96, 0x26, + 0x4a, 0x10, 0xe0, 0xa0, 0x70, 0xbd, 0xe2, 0x01, 0xcd, 0xde, 0x37, 0xa0, 0x2e, 0xa4, 0x2d, 0xdb, + 0xe8, 0x1a, 0x66, 0xb8, 0x83, 0xdf, 0x90, 0x9a, 0xd2, 0xa0, 0x8b, 0x4b, 0xef, 0x21, 0xc5, 0xc0, + 0x65, 0x1d, 0xfd, 0x11, 0xd6, 0x3c, 0x22, 0x56, 0x60, 0xfc, 0x62, 0xb2, 0x3f, 0x1e, 0x15, 0x73, + 0x8c, 0x8d, 0xd5, 0xa0, 0x8b, 0xe9, 0x2b, 0x39, 0x2b, 0x0a, 0x89, 0x06, 0x90, 0xb5, 0x7d, 0x17, + 0x13, 0x4d, 0x79, 0xaa, 0xe9, 0x83, 0xf1, 0xa8, 0x98, 0x09, 0x5c, 0xbf, 0xb4, 0xb2, 0x99, 0x80, + 0x42, 0xd6, 0xd1, 0x5f, 0x38, 0x58, 0x0f, 0x29, 0x7d, 0x9d, 0x57, 0xa9, 0xce, 0x8f, 0xc6, 0xa3, + 0xa2, 0x10, 0xf0, 0x5e, 0xa4, 0xda, 0x82, 0x3d, 0x85, 0x1a, 0xf4, 0xfb, 0x44, 0xd8, 0xef, 0xfd, + 0x56, 0xc6, 0xc3, 0xfa, 0xcc, 0x35, 0xd1, 0x77, 0x01, 0xf1, 0xed, 0x0d, 0x88, 0xcf, 0x38, 0xc8, + 0x4f, 0xdd, 0xb8, 0x5d, 0xf2, 0x90, 0x85, 0x5c, 0x58, 0xb3, 0x8e, 0x4d, 0x6c, 0xb7, 0x83, 0xae, + 0x13, 0x1e, 0x00, 0xb2, 0xf4, 0xd2, 0xef, 0x62, 0x5a, 0x4f, 0xd6, 0x0a, 0xb1, 0xf4, 0xd2, 0xa7, + 0x1c, 0x5c, 0x3d, 0xef, 0x3e, 0xf3, 0x92, 0xda, 0x9f, 0x09, 0x10, 0xb6, 0xa6, 0xa8, 0xc2, 0x17, + 0xd8, 0xfc, 0x22, 0x0c, 0x9e, 0xb7, 0x0d, 0xef, 0x3c, 0xf8, 0xee, 0x3b, 0x50, 0xe9, 0x13, 0x0e, + 0x12, 0xec, 0xf2, 0x94, 0x94, 0x17, 0x76, 0x72, 0x9d, 0x2a, 0x2f, 0xec, 0xf5, 0xf2, 0xe5, 0x85, + 0x81, 0xcb, 0x3a, 0xfa, 0x13, 0x08, 0xfe, 0x3f, 0x89, 0xdc, 0x46, 0xe7, 0x19, 0xd6, 0x5c, 0xc7, + 0x9b, 0x2b, 0x14, 0x92, 0x6c, 0xd2, 0xd4, 0xbb, 0x25, 0x79, 0x67, 0xb8, 0x4a, 0x9f, 0x73, 0x90, + 0xf2, 0x2f, 0x7b, 0x49, 0x16, 0xf9, 0xd7, 0xbd, 0x53, 0x59, 0xe4, 0x7f, 0xb2, 0x7c, 0x16, 0xf9, + 0x04, 0x5f, 0xbf, 0xee, 0x77, 0xfe, 0x4e, 0xfc, 0xed, 0xaa, 0xee, 0xd0, 0x99, 0xbc, 0x6d, 0x00, + 0x48, 0x6c, 0x56, 0x5b, 0xb5, 0xfa, 0x9e, 0xc0, 0x91, 0x17, 0x92, 0xd2, 0x68, 0x36, 0x6b, 0x92, + 0x10, 0x43, 0x79, 0xc8, 0x48, 0xb5, 0xdd, 0xda, 0x5e, 0xad, 0xdd, 0xa8, 0xef, 0x3e, 0x16, 0xe2, + 0xe8, 0x07, 0xf0, 0x3d, 0x6f, 0x61, 0xb3, 0x2e, 0xb5, 0x0f, 0x14, 0xd9, 0x7f, 0xc5, 0xa3, 0x35, + 0x80, 0xea, 0xe6, 0xd6, 0xce, 0xb6, 0xbc, 0xbb, 0x5b, 0x93, 0x84, 0x55, 0x94, 0x83, 0xf4, 0xc3, + 0xcd, 0x5d, 0x59, 0xda, 0xdc, 0xab, 0x49, 0x42, 0x82, 0x40, 0xed, 0x3d, 0xaa, 0xb7, 0x7d, 0xec, + 0x24, 0x21, 0x6d, 0xee, 0x57, 0x77, 0xe5, 0x2d, 0x21, 0x55, 0xbd, 0x79, 0xfa, 0x41, 0x61, 0xe5, + 0x74, 0x5c, 0xe0, 0xce, 0xc6, 0x05, 0xee, 0xf5, 0xb8, 0xc0, 0xfd, 0x7f, 0x5c, 0xe0, 0xfe, 0xf6, + 0xa6, 0xb0, 0x72, 0xf6, 0xa6, 0xb0, 0xf2, 0xfa, 0x4d, 0x61, 0xe5, 0x09, 0x4f, 0xb4, 0xea, 0x24, + 0xe8, 0xbd, 0xfb, 0xbd, 0x2f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xc8, 0xdd, 0x27, 0xd1, 0xf6, 0x1d, + 0x00, 0x00, } func (this *Column) Equal(that interface{}) bool { diff --git a/pkg/sql/schemachanger/scpb/scpb.proto b/pkg/sql/schemachanger/scpb/scpb.proto index 89ed803be225..1706c0bc70ba 100644 --- a/pkg/sql/schemachanger/scpb/scpb.proto +++ b/pkg/sql/schemachanger/scpb/scpb.proto @@ -51,11 +51,13 @@ message Target { enum Status { UNKNOWN = 0; ABSENT = 1; - DELETE_ONLY = 2; - DELETE_AND_WRITE_ONLY = 3; - BACKFILLED = 4; - VALIDATED = 5; - PUBLIC = 6; + DROPPED = 2; + DELETE_ONLY = 3; + DELETE_AND_WRITE_ONLY = 4; + BACKFILLED = 5; + VALIDATED = 6; + TXN_DROPPED = 7; + PUBLIC = 8; } message Column { diff --git a/pkg/sql/schemachanger/scplan/deprules/rules.go b/pkg/sql/schemachanger/scplan/deprules/rules.go index 194a850b7401..d8f05c034649 100644 --- a/pkg/sql/schemachanger/scplan/deprules/rules.go +++ b/pkg/sql/schemachanger/scplan/deprules/rules.go @@ -35,8 +35,12 @@ func joinTargetNode( const ( add, drop = scpb.Target_ADD, scpb.Target_DROP - public, absent, deleteOnly, deleteAndWriteOnly = scpb.Status_PUBLIC, - scpb.Status_ABSENT, scpb.Status_DELETE_ONLY, scpb.Status_DELETE_AND_WRITE_ONLY + public, txnDropped, dropped, absent, deleteOnly, deleteAndWriteOnly = scpb.Status_PUBLIC, + scpb.Status_TXN_DROPPED, scpb.Status_DROPPED, scpb.Status_ABSENT, scpb.Status_DELETE_ONLY, + scpb.Status_DELETE_AND_WRITE_ONLY + // Make the linter happy + _ = txnDropped + _ = deleteOnly ) func init() { @@ -44,7 +48,8 @@ func init() { parent, parentTarget, parentNode = targetNodeVars("parent") other, otherTarget, otherNode = targetNodeVars("other") ) - // TODO(ajwerner): These rules are suspect. + // Ensures that child objects under a database or schema + // are dropped before any children are dealt with. register( "parent dependencies", parentNode, otherNode, @@ -76,76 +81,16 @@ func init() { screl.JoinTargetNode(parent, parentTarget, parentNode), parentTarget.AttrEq(screl.Direction, drop), - parentNode.AttrIn(screl.Status, deleteOnly, deleteAndWriteOnly), + parentNode.AttrIn(screl.Status, absent), joinTargetNode(other, otherTarget, otherNode, drop, absent), ), ) } -func init() { - ownedBy, ownedByTarget, ownedByNode := targetNodeVars("owned-by") - seq, seqTarget, seqNode := targetNodeVars("seq") - var id rel.Var = "id" - register( - "sequence owned by being dropped relies on sequence entering delete only", - ownedByNode, seqNode, - screl.MustQuery( - ownedBy.Type((*scpb.SequenceOwnedBy)(nil)), - seq.Type((*scpb.Sequence)(nil)), - - id.Entities(screl.DescID, ownedBy, seq), - - joinTargetNode(ownedBy, ownedByTarget, ownedByNode, drop, absent), - joinTargetNode(seq, seqTarget, seqNode, drop, absent), - )) -} - -func init() { - ownedBy, ownedByTarget, ownedByNode := targetNodeVars("owned-by") - id := rel.Var("id") - // TODO(ajwerner): What is this about? It seems, if anything, just wrong. - tab, tabTarget, tabNode := targetNodeVars("tab") - register( - "table drop in public depends on absent sequence owned by?", - tabNode, ownedByNode, - screl.MustQuery( - tab.Type((*scpb.Table)(nil)), - ownedBy.Type((*scpb.SequenceOwnedBy)(nil)), - - tab.AttrEqVar(screl.DescID, id), - ownedBy.AttrEqVar(screl.ReferencedDescID, id), - - joinTargetNode(tab, tabTarget, tabNode, drop, public), - joinTargetNode(ownedBy, ownedByTarget, ownedByNode, drop, absent), - )) -} - -func init() { - - typ, typTarget, typNode := targetNodeVars("type") - typRef, typRefTarget, typRefNode := targetNodeVars("type-ref") - var id rel.Var = "id" - register( - "type reference something", - typNode, typRefNode, - screl.MustQuery( - typ.Type((*scpb.Type)(nil)), - typRef.Type((*scpb.TypeReference)(nil)), - - typ.AttrEqVar(screl.DescID, id), - typRef.AttrEqVar(screl.ReferencedDescID, id), - - joinTargetNode(typ, typTarget, typNode, drop, public), - joinTargetNode(typRef, typRefTarget, typRefNode, drop, absent), - ), - ) -} - func init() { from, fromTarget, fromNode := targetNodeVars("from") to, toTarget, toNode := targetNodeVars("to") - var id rel.Var = "id" register( "view depends on view", fromNode, toNode, @@ -179,20 +124,6 @@ func init() { return idInIDs(to.DependsOn, from.TableID) }), - joinTargetNode(from, fromTarget, fromNode, drop, public), - joinTargetNode(to, toTarget, toNode, drop, absent), - ), - ) - - register( - "view depends on type", - fromNode, toNode, - screl.MustQuery( - - from.Type((*scpb.View)(nil)), - to.Type((*scpb.TypeReference)(nil)), - id.Entities(screl.DescID, from, to), - joinTargetNode(from, fromTarget, fromNode, drop, absent), joinTargetNode(to, toTarget, toNode, drop, absent), ), @@ -275,98 +206,66 @@ func init() { ) } -// TODO(ajwerner): What does this even mean? The sequence starts in -// public. - func init() { - seq, seqTarget, seqNode := targetNodeVars("seq") - defExpr, defExprTarget, defExprNode := targetNodeVars("def-expr") - register( - "sequence default expr", - seqNode, defExprNode, - screl.MustQuery( - seq.Type((*scpb.Sequence)(nil)), - defExpr.Type((*scpb.DefaultExpression)(nil)), - - rel.Filter("defaultExpressionUsesSequence", seq, defExpr)(func( - this *scpb.Sequence, that *scpb.DefaultExpression, - ) bool { - return idInIDs(that.UsesSequenceIDs, this.SequenceID) - }), - - joinTargetNode(seq, seqTarget, seqNode, drop, public), - joinTargetNode(defExpr, defExprTarget, defExprNode, drop, absent), - ), - ) -} - -func init() { - - // TODO(ajwerner): What does this even mean? The table starts in - // public. - tab, tabTarget, tabNode := targetNodeVars("tab") - defExpr, defExprTarget, defExprNode := targetNodeVars("def-expr") - id := rel.Var("id") - register( - "table default expr", - tabNode, defExprNode, - screl.MustQuery( - - tab.Type((*scpb.Table)(nil)), - defExpr.Type((*scpb.DefaultExpression)(nil)), - - id.Entities(screl.DescID, tab, defExpr), - - joinTargetNode(tab, tabTarget, tabNode, drop, public), - joinTargetNode(defExpr, defExprTarget, defExprNode, drop, absent), - ), - ) -} - -func init() { - - // TODO(ajwerner): What does this even mean? The table starts in - // public. - tab, tabTarget, tabNode := targetNodeVars("tab") - relDepOnBy, relDepOnByTarget, relDepOnByNode := targetNodeVars("rel-dep") - tabID := rel.Var("dep-id") - register( - "table drop relation depended on", - tabNode, relDepOnByNode, - screl.MustQuery( - - tab.Type((*scpb.Table)(nil)), - relDepOnBy.Type((*scpb.RelationDependedOnBy)(nil)), - - tab.AttrEqVar(screl.DescID, tabID), - relDepOnBy.AttrEqVar(screl.ReferencedDescID, tabID), - - joinTargetNode(tab, tabTarget, tabNode, drop, public), - joinTargetNode(relDepOnBy, relDepOnByTarget, relDepOnByNode, drop, absent), - ), - ) + depNeedsRelationToExitSynthDrop := func(ruleName string, depTypes []interface{}, depDescIDMatch rel.Attr) { + // Before any parts of a relation/type can be dropped, the relation + // should exit the synthetic drop state. + relation, relationTarget, relationNode := targetNodeVars("relation") + dep, depTarget, depNode := targetNodeVars("dep") + var id rel.Var = "id" + register( + "dependency needs relation/type as non-synthetically dropped", + depNode, relationNode, + screl.MustQuery( + + relation.Type((*scpb.Table)(nil), (*scpb.View)(nil), (*scpb.Sequence)(nil), (*scpb.Type)(nil)), + dep.Type(depTypes[0], depTypes[1:]...), + + relation.AttrEqVar(screl.DescID, id), + dep.AttrEqVar(depDescIDMatch, id), + joinTargetNode(relation, relationTarget, relationNode, drop, dropped), + joinTargetNode(dep, depTarget, depNode, drop, absent), + ), + ) + } + depNeedsRelationToExitSynthDrop("dependency needs relation/type as non-synthetically dropped", + []interface{}{(*scpb.DefaultExpression)(nil), (*scpb.RelationDependedOnBy)(nil), + (*scpb.SequenceOwnedBy)(nil), (*scpb.OutboundForeignKey)(nil)}, + screl.DescID) + + depNeedsRelationToExitSynthDrop("dependency (ref desc) needs relation/type as non-synthetically dropped", + []interface{}{(*scpb.InboundForeignKey)(nil), (*scpb.TypeReference)(nil)}, + screl.ReferencedDescID) } func init() { - - // These are more weird rules where the public drop table - // depends on something else. - tab, tabTarget, tabNode := targetNodeVars("tab") - fkBy, fkByTarget, fkByNode := targetNodeVars("fk") - tabID := rel.Var("dep-id") - register( - "table drop depends on outbound fk", - tabNode, fkByNode, - screl.MustQuery( - - tab.Type((*scpb.Table)(nil)), - fkBy.Type((*scpb.OutboundForeignKey)(nil)), - - tab.AttrEqVar(screl.DescID, tabID), - fkBy.AttrEqVar(screl.DescID, tabID), - - joinTargetNode(tab, tabTarget, tabNode, drop, public), - joinTargetNode(fkBy, fkByTarget, fkByNode, drop, absent), - ), - ) + relationNeedsDepToBeRemoved := func(ruleName string, depTypes []interface{}, depDescIDMatch rel.Attr) { + // Before any parts of a relation can be dropped, the relation + // should exit the synthetic drop state. + relation, relationTarget, relationNode := targetNodeVars("relation") + dep, depTarget, depNode := targetNodeVars("dep") + var id rel.Var = "id" + register( + "relation/type needs dependency as dropped", + relationNode, depNode, + screl.MustQuery( + + relation.Type((*scpb.Table)(nil), (*scpb.View)(nil), (*scpb.Sequence)(nil), (*scpb.Type)(nil)), + dep.Type(depTypes[0], depTypes[1:]...), + + id.Entities(screl.DescID, relation, dep), + + joinTargetNode(relation, relationTarget, relationNode, drop, absent), + joinTargetNode(dep, depTarget, depNode, drop, absent), + ), + ) + } + relationNeedsDepToBeRemoved("relation/type needs dependency as dropped", + []interface{}{(*scpb.DefaultExpression)(nil), (*scpb.RelationDependedOnBy)(nil), + (*scpb.SequenceOwnedBy)(nil), (*scpb.OutboundForeignKey)(nil)}, + screl.DescID) + + relationNeedsDepToBeRemoved("relation/type (ref desc) needs dependency as dropped", + []interface{}{(*scpb.InboundForeignKey)(nil), (*scpb.TypeReference)(nil)}, + screl.ReferencedDescID) } diff --git a/pkg/sql/schemachanger/scplan/deprules/testdata/rules b/pkg/sql/schemachanger/scplan/deprules/testdata/rules index db54e39e6a31..0a4caa06477b 100644 --- a/pkg/sql/schemachanger/scplan/deprules/testdata/rules +++ b/pkg/sql/schemachanger/scplan/deprules/testdata/rules @@ -12,73 +12,13 @@ rules - $parent-node[Type] = '*scpb.Node' - $parent-node[Target] = $parent-target - $parent-target[Direction] = DROP - - $parent-node[Status] IN [DELETE_ONLY, DELETE_AND_WRITE_ONLY] + - $parent-node[Status] IN [ABSENT] - $other-target[Type] = '*scpb.Target' - $other-target[Element] = $other - $other-node[Type] = '*scpb.Node' - $other-node[Target] = $other-target - $other-target[Direction] = DROP - $other-node[Status] = ABSENT -- name: sequence owned by being dropped relies on sequence entering delete only - from: owned-by-node - to: seq-node - query: - - $owned-by[Type] = '*scpb.SequenceOwnedBy' - - $seq[Type] = '*scpb.Sequence' - - $owned-by[DescID] = $id - - $seq[DescID] = $id - - $owned-by-target[Type] = '*scpb.Target' - - $owned-by-target[Element] = $owned-by - - $owned-by-node[Type] = '*scpb.Node' - - $owned-by-node[Target] = $owned-by-target - - $owned-by-target[Direction] = DROP - - $owned-by-node[Status] = ABSENT - - $seq-target[Type] = '*scpb.Target' - - $seq-target[Element] = $seq - - $seq-node[Type] = '*scpb.Node' - - $seq-node[Target] = $seq-target - - $seq-target[Direction] = DROP - - $seq-node[Status] = ABSENT -- name: table drop in public depends on absent sequence owned by? - from: tab-node - to: owned-by-node - query: - - $tab[Type] = '*scpb.Table' - - $owned-by[Type] = '*scpb.SequenceOwnedBy' - - $tab[DescID] = $id - - $owned-by[ReferencedDescID] = $id - - $tab-target[Type] = '*scpb.Target' - - $tab-target[Element] = $tab - - $tab-node[Type] = '*scpb.Node' - - $tab-node[Target] = $tab-target - - $tab-target[Direction] = DROP - - $tab-node[Status] = PUBLIC - - $owned-by-target[Type] = '*scpb.Target' - - $owned-by-target[Element] = $owned-by - - $owned-by-node[Type] = '*scpb.Node' - - $owned-by-node[Target] = $owned-by-target - - $owned-by-target[Direction] = DROP - - $owned-by-node[Status] = ABSENT -- name: type reference something - from: type-node - to: type-ref-node - query: - - $type[Type] = '*scpb.Type' - - $type-ref[Type] = '*scpb.TypeReference' - - $type[DescID] = $id - - $type-ref[ReferencedDescID] = $id - - $type-target[Type] = '*scpb.Target' - - $type-target[Element] = $type - - $type-node[Type] = '*scpb.Node' - - $type-node[Target] = $type-target - - $type-target[Direction] = DROP - - $type-node[Status] = PUBLIC - - $type-ref-target[Type] = '*scpb.Target' - - $type-ref-target[Element] = $type-ref - - $type-ref-node[Type] = '*scpb.Node' - - $type-ref-node[Target] = $type-ref-target - - $type-ref-target[Direction] = DROP - - $type-ref-node[Status] = ABSENT - name: view depends on view from: from-node to: to-node @@ -110,26 +50,6 @@ rules - $from-node[Type] = '*scpb.Node' - $from-node[Target] = $from-target - $from-target[Direction] = DROP - - $from-node[Status] = PUBLIC - - $to-target[Type] = '*scpb.Target' - - $to-target[Element] = $to - - $to-node[Type] = '*scpb.Node' - - $to-node[Target] = $to-target - - $to-target[Direction] = DROP - - $to-node[Status] = ABSENT -- name: view depends on type - from: from-node - to: to-node - query: - - $from[Type] = '*scpb.View' - - $to[Type] = '*scpb.TypeReference' - - $from[DescID] = $id - - $to[DescID] = $id - - $from-target[Type] = '*scpb.Target' - - $from-target[Element] = $from - - $from-node[Type] = '*scpb.Node' - - $from-node[Target] = $from-target - - $from-target[Direction] = DROP - $from-node[Status] = ABSENT - $to-target[Type] = '*scpb.Target' - $to-target[Element] = $to @@ -202,82 +122,83 @@ rules - $drop-idx-node[Target] = $drop-idx-target - $drop-idx-target[Direction] = DROP - $drop-idx-node[Status] = DELETE_AND_WRITE_ONLY -- name: sequence default expr - from: seq-node - to: def-expr-node +- name: dependency needs relation/type as non-synthetically dropped + from: dep-node + to: relation-node query: - - $seq[Type] = '*scpb.Sequence' - - $def-expr[Type] = '*scpb.DefaultExpression' - - defaultExpressionUsesSequence(*scpb.Sequence, *scpb.DefaultExpression)($seq, $def-expr) - - $seq-target[Type] = '*scpb.Target' - - $seq-target[Element] = $seq - - $seq-node[Type] = '*scpb.Node' - - $seq-node[Target] = $seq-target - - $seq-target[Direction] = DROP - - $seq-node[Status] = PUBLIC - - $def-expr-target[Type] = '*scpb.Target' - - $def-expr-target[Element] = $def-expr - - $def-expr-node[Type] = '*scpb.Node' - - $def-expr-node[Target] = $def-expr-target - - $def-expr-target[Direction] = DROP - - $def-expr-node[Status] = ABSENT -- name: table default expr - from: tab-node - to: def-expr-node + - $relation[Type] IN ['*scpb.Table', '*scpb.View', '*scpb.Sequence', '*scpb.Type'] + - $dep[Type] IN ['*scpb.DefaultExpression', '*scpb.RelationDependedOnBy', '*scpb.SequenceOwnedBy', '*scpb.OutboundForeignKey'] + - $relation[DescID] = $id + - $dep[DescID] = $id + - $relation-target[Type] = '*scpb.Target' + - $relation-target[Element] = $relation + - $relation-node[Type] = '*scpb.Node' + - $relation-node[Target] = $relation-target + - $relation-target[Direction] = DROP + - $relation-node[Status] = DROPPED + - $dep-target[Type] = '*scpb.Target' + - $dep-target[Element] = $dep + - $dep-node[Type] = '*scpb.Node' + - $dep-node[Target] = $dep-target + - $dep-target[Direction] = DROP + - $dep-node[Status] = ABSENT +- name: dependency needs relation/type as non-synthetically dropped + from: dep-node + to: relation-node query: - - $tab[Type] = '*scpb.Table' - - $def-expr[Type] = '*scpb.DefaultExpression' - - $tab[DescID] = $id - - $def-expr[DescID] = $id - - $tab-target[Type] = '*scpb.Target' - - $tab-target[Element] = $tab - - $tab-node[Type] = '*scpb.Node' - - $tab-node[Target] = $tab-target - - $tab-target[Direction] = DROP - - $tab-node[Status] = PUBLIC - - $def-expr-target[Type] = '*scpb.Target' - - $def-expr-target[Element] = $def-expr - - $def-expr-node[Type] = '*scpb.Node' - - $def-expr-node[Target] = $def-expr-target - - $def-expr-target[Direction] = DROP - - $def-expr-node[Status] = ABSENT -- name: table drop relation depended on - from: tab-node - to: rel-dep-node + - $relation[Type] IN ['*scpb.Table', '*scpb.View', '*scpb.Sequence', '*scpb.Type'] + - $dep[Type] IN ['*scpb.InboundForeignKey', '*scpb.TypeReference'] + - $relation[DescID] = $id + - $dep[ReferencedDescID] = $id + - $relation-target[Type] = '*scpb.Target' + - $relation-target[Element] = $relation + - $relation-node[Type] = '*scpb.Node' + - $relation-node[Target] = $relation-target + - $relation-target[Direction] = DROP + - $relation-node[Status] = DROPPED + - $dep-target[Type] = '*scpb.Target' + - $dep-target[Element] = $dep + - $dep-node[Type] = '*scpb.Node' + - $dep-node[Target] = $dep-target + - $dep-target[Direction] = DROP + - $dep-node[Status] = ABSENT +- name: relation/type needs dependency as dropped + from: relation-node + to: dep-node query: - - $tab[Type] = '*scpb.Table' - - $rel-dep[Type] = '*scpb.RelationDependedOnBy' - - $tab[DescID] = $dep-id - - $rel-dep[ReferencedDescID] = $dep-id - - $tab-target[Type] = '*scpb.Target' - - $tab-target[Element] = $tab - - $tab-node[Type] = '*scpb.Node' - - $tab-node[Target] = $tab-target - - $tab-target[Direction] = DROP - - $tab-node[Status] = PUBLIC - - $rel-dep-target[Type] = '*scpb.Target' - - $rel-dep-target[Element] = $rel-dep - - $rel-dep-node[Type] = '*scpb.Node' - - $rel-dep-node[Target] = $rel-dep-target - - $rel-dep-target[Direction] = DROP - - $rel-dep-node[Status] = ABSENT -- name: table drop depends on outbound fk - from: tab-node - to: fk-node + - $relation[Type] IN ['*scpb.Table', '*scpb.View', '*scpb.Sequence', '*scpb.Type'] + - $dep[Type] IN ['*scpb.DefaultExpression', '*scpb.RelationDependedOnBy', '*scpb.SequenceOwnedBy', '*scpb.OutboundForeignKey'] + - $relation[DescID] = $id + - $dep[DescID] = $id + - $relation-target[Type] = '*scpb.Target' + - $relation-target[Element] = $relation + - $relation-node[Type] = '*scpb.Node' + - $relation-node[Target] = $relation-target + - $relation-target[Direction] = DROP + - $relation-node[Status] = ABSENT + - $dep-target[Type] = '*scpb.Target' + - $dep-target[Element] = $dep + - $dep-node[Type] = '*scpb.Node' + - $dep-node[Target] = $dep-target + - $dep-target[Direction] = DROP + - $dep-node[Status] = ABSENT +- name: relation/type needs dependency as dropped + from: relation-node + to: dep-node query: - - $tab[Type] = '*scpb.Table' - - $fk[Type] = '*scpb.OutboundForeignKey' - - $tab[DescID] = $dep-id - - $fk[DescID] = $dep-id - - $tab-target[Type] = '*scpb.Target' - - $tab-target[Element] = $tab - - $tab-node[Type] = '*scpb.Node' - - $tab-node[Target] = $tab-target - - $tab-target[Direction] = DROP - - $tab-node[Status] = PUBLIC - - $fk-target[Type] = '*scpb.Target' - - $fk-target[Element] = $fk - - $fk-node[Type] = '*scpb.Node' - - $fk-node[Target] = $fk-target - - $fk-target[Direction] = DROP - - $fk-node[Status] = ABSENT + - $relation[Type] IN ['*scpb.Table', '*scpb.View', '*scpb.Sequence', '*scpb.Type'] + - $dep[Type] IN ['*scpb.InboundForeignKey', '*scpb.TypeReference'] + - $relation[DescID] = $id + - $dep[DescID] = $id + - $relation-target[Type] = '*scpb.Target' + - $relation-target[Element] = $relation + - $relation-node[Type] = '*scpb.Node' + - $relation-node[Target] = $relation-target + - $relation-target[Direction] = DROP + - $relation-node[Status] = ABSENT + - $dep-target[Type] = '*scpb.Target' + - $dep-target[Element] = $dep + - $dep-node[Type] = '*scpb.Node' + - $dep-node[Target] = $dep-target + - $dep-target[Direction] = DROP + - $dep-node[Status] = ABSENT diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_database.go b/pkg/sql/schemachanger/scplan/opgen/opgen_database.go index 8d2c5aa59d56..984d3de26f05 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_database.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_database.go @@ -20,17 +20,27 @@ func init() { (*scpb.Database)(nil), scpb.Target_DROP, scpb.Status_PUBLIC, - to(scpb.Status_DELETE_ONLY, + to(scpb.Status_TXN_DROPPED, + minPhase(scop.StatementPhase), + emit(func(this *scpb.Database) scop.Op { + return &scop.MarkDescriptorAsDroppedSynthetically{ + DescID: this.DatabaseID, + } + })), + to(scpb.Status_DROPPED, minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.Database) scop.Op { return &scop.MarkDescriptorAsDropped{ - TableID: this.DatabaseID, + DescID: this.DatabaseID, } - })), + }), + ), to(scpb.Status_ABSENT, - minPhase(scop.PostCommitPhase), + minPhase(scop.PreCommitPhase), revertible(false), emit(func(this *scpb.Database) scop.Op { + return &scop.DrainDescriptorName{ TableID: this.DatabaseID, } diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_schema.go b/pkg/sql/schemachanger/scplan/opgen/opgen_schema.go index cb405d8a4abc..41fd976be881 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_schema.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_schema.go @@ -20,15 +20,22 @@ func init() { (*scpb.Schema)(nil), scpb.Target_DROP, scpb.Status_PUBLIC, - to(scpb.Status_DELETE_ONLY, - // TODO(ajwerner): Sort out these steps. In general, DELETE_ONLY is - // not revertible. + to(scpb.Status_TXN_DROPPED, + minPhase(scop.StatementPhase), + emit(func(this *scpb.Schema) scop.Op { + return &scop.MarkDescriptorAsDroppedSynthetically{ + DescID: this.SchemaID, + } + })), + to(scpb.Status_DROPPED, minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.Schema) scop.Op { return &scop.MarkDescriptorAsDropped{ - TableID: this.SchemaID, + DescID: this.SchemaID, } - })), + }), + ), to(scpb.Status_ABSENT, // TODO(ajwerner): The minPhase here feels like it should be PostCommit. // Also, this definitely is not revertible. Leaving to make this commit diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_sequence.go b/pkg/sql/schemachanger/scplan/opgen/opgen_sequence.go index d7be6de5806d..1e946a8f172f 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_sequence.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_sequence.go @@ -22,14 +22,25 @@ func init() { (*scpb.Sequence)(nil), scpb.Target_DROP, scpb.Status_PUBLIC, - to(scpb.Status_ABSENT, + to(scpb.Status_TXN_DROPPED, + minPhase(scop.StatementPhase), + emit(func(this *scpb.Sequence) scop.Op { + return &scop.MarkDescriptorAsDroppedSynthetically{ + DescID: this.SequenceID, + } + })), + to(scpb.Status_DROPPED, minPhase(scop.PreCommitPhase), revertible(false), emit(func(this *scpb.Sequence) scop.Op { return &scop.MarkDescriptorAsDropped{ - TableID: this.SequenceID, + DescID: this.SequenceID, } }), + ), + to(scpb.Status_ABSENT, + minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.Sequence) scop.Op { return &scop.DrainDescriptorName{ TableID: this.SequenceID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_table.go b/pkg/sql/schemachanger/scplan/opgen/opgen_table.go index 124eefc482af..214be01111de 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_table.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_table.go @@ -21,14 +21,25 @@ func init() { (*scpb.Table)(nil), scpb.Target_DROP, scpb.Status_PUBLIC, - to(scpb.Status_ABSENT, + to(scpb.Status_TXN_DROPPED, + minPhase(scop.StatementPhase), + emit(func(this *scpb.Table) scop.Op { + return &scop.MarkDescriptorAsDroppedSynthetically{ + DescID: this.TableID, + } + })), + to(scpb.Status_DROPPED, minPhase(scop.PreCommitPhase), revertible(false), emit(func(this *scpb.Table) scop.Op { return &scop.MarkDescriptorAsDropped{ - TableID: this.TableID, + DescID: this.TableID, } }), + ), + to(scpb.Status_ABSENT, + minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.Table) scop.Op { return &scop.DrainDescriptorName{ TableID: this.TableID, diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_type.go b/pkg/sql/schemachanger/scplan/opgen/opgen_type.go index 2720e87c1d7e..c09449452b25 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_type.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_type.go @@ -20,18 +20,28 @@ func init() { (*scpb.Type)(nil), scpb.Target_DROP, scpb.Status_PUBLIC, - to(scpb.Status_DELETE_ONLY, + to(scpb.Status_TXN_DROPPED, + minPhase(scop.StatementPhase), + emit(func(this *scpb.Type) scop.Op { + return &scop.MarkDescriptorAsDroppedSynthetically{ + DescID: this.TypeID, + } + })), + to(scpb.Status_DROPPED, + // TODO(ajwerner): The move to DELETE_ONLY should be marked + // non-revertible. minPhase(scop.PreCommitPhase), - // TODO(ajwerner): This should be marked as non-revertible. + revertible(false), emit(func(this *scpb.Type) scop.Op { return &scop.MarkDescriptorAsDropped{ - TableID: this.TypeID, + DescID: this.TypeID, } - })), + }), + ), to(scpb.Status_ABSENT, // TODO(ajwerner): The move to DELETE_ONLY should be marked // non-revertible. - minPhase(scop.PostCommitPhase), + minPhase(scop.PreCommitPhase), revertible(false), emit(func(this *scpb.Type) scop.Op { return &scop.DrainDescriptorName{ diff --git a/pkg/sql/schemachanger/scplan/opgen/opgen_view.go b/pkg/sql/schemachanger/scplan/opgen/opgen_view.go index 44c5e42475bc..9a879b3a5f7d 100644 --- a/pkg/sql/schemachanger/scplan/opgen/opgen_view.go +++ b/pkg/sql/schemachanger/scplan/opgen/opgen_view.go @@ -21,14 +21,25 @@ func init() { (*scpb.View)(nil), scpb.Target_DROP, scpb.Status_PUBLIC, - to(scpb.Status_ABSENT, + to(scpb.Status_TXN_DROPPED, + minPhase(scop.StatementPhase), + emit(func(this *scpb.View) scop.Op { + return &scop.MarkDescriptorAsDroppedSynthetically{ + DescID: this.TableID, + } + })), + to(scpb.Status_DROPPED, minPhase(scop.PreCommitPhase), revertible(false), emit(func(this *scpb.View) scop.Op { return &scop.MarkDescriptorAsDropped{ - TableID: this.TableID, + DescID: this.TableID, } }), + ), + to(scpb.Status_ABSENT, + minPhase(scop.PreCommitPhase), + revertible(false), emit(func(this *scpb.View) scop.Op { return &scop.DrainDescriptorName{ TableID: this.TableID, diff --git a/pkg/sql/schemachanger/scplan/testdata/alter_table b/pkg/sql/schemachanger/scplan/testdata/alter_table index ee91b255d2ae..c86e46fb4404 100644 --- a/pkg/sql/schemachanger/scplan/testdata/alter_table +++ b/pkg/sql/schemachanger/scplan/testdata/alter_table @@ -35,23 +35,23 @@ Stage 0 Unique: true Version: 4 TableID: 52 -Stage 1 (non-revertible) +Stage 1 *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 -Stage 2 (non-revertible) +Stage 2 *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 3 (non-revertible) +Stage 3 *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 4 (non-revertible) +Stage 4 *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -130,23 +130,23 @@ Stage 0 Unique: true Version: 4 TableID: 52 -Stage 1 (non-revertible) +Stage 1 *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 -Stage 2 (non-revertible) +Stage 2 *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 3 (non-revertible) +Stage 3 *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 4 (non-revertible) +Stage 4 *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -240,7 +240,7 @@ Stage 0 width: 64 FamilyName: primary TableID: 52 -Stage 1 (non-revertible) +Stage 1 *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 @@ -250,16 +250,16 @@ Stage 1 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 3 TableID: 52 -Stage 2 (non-revertible) +Stage 2 *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 3 (non-revertible) +Stage 3 *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 4 (non-revertible) +Stage 4 *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -343,23 +343,23 @@ Stage 0 Unique: true Version: 4 TableID: 52 -Stage 1 (non-revertible) +Stage 1 *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 2 TableID: 52 -Stage 2 (non-revertible) +Stage 2 *scop.BackfillIndex IndexID: 2 TableID: 52 -Stage 3 (non-revertible) +Stage 3 *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 TableID: 52 -Stage 4 (non-revertible) +Stage 4 *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 @@ -474,7 +474,7 @@ Stage 0 Unique: true Version: 4 TableID: 53 -Stage 1 (non-revertible) +Stage 1 *scop.MakeAddedIndexDeleteAndWriteOnly IndexID: 2 TableID: 52 @@ -487,14 +487,14 @@ Stage 1 (non-revertible) *scop.MakeAddedColumnDeleteAndWriteOnly ColumnID: 3 TableID: 53 -Stage 2 (non-revertible) +Stage 2 *scop.BackfillIndex IndexID: 2 TableID: 52 *scop.BackfillIndex IndexID: 2 TableID: 53 -Stage 3 (non-revertible) +Stage 3 *scop.ValidateUniqueIndex IndexID: 2 PrimaryIndexID: 1 @@ -503,7 +503,7 @@ Stage 3 (non-revertible) IndexID: 2 PrimaryIndexID: 1 TableID: 53 -Stage 4 (non-revertible) +Stage 4 *scop.MakeAddedPrimaryIndexPublic Index: EncodingType: 1 diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_database b/pkg/sql/schemachanger/scplan/testdata/drop_database index efc903e0f45b..01c6c881d169 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_database +++ b/pkg/sql/schemachanger/scplan/testdata/drop_database @@ -51,11 +51,42 @@ ops DROP DATABASE db1 CASCADE ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 54 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 57 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 55 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 58 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 59 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 60 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 61 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 64 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 56 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 62 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 63 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 53 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 52 +Stage 1 (non-revertible) + *scop.MarkDescriptorAsDropped + DescID: 57 *scop.RemoveColumnDefaultExpression ColumnID: 3 TableID: 57 *scop.UpdateRelationDeps TableID: 57 + *scop.MarkDescriptorAsDropped + DescID: 54 *scop.RemoveColumnDefaultExpression ColumnID: 1 TableID: 57 @@ -69,29 +100,53 @@ Stage 0 *scop.RemoveRelationDependedOnBy DependedOnBy: 57 TableID: 54 + *scop.MarkDescriptorAsDropped + DescID: 56 *scop.RemoveColumnDefaultExpression ColumnID: 3 TableID: 56 *scop.UpdateRelationDeps TableID: 56 + *scop.MarkDescriptorAsDropped + DescID: 55 *scop.RemoveRelationDependedOnBy DependedOnBy: 58 TableID: 56 + *scop.MarkDescriptorAsDropped + DescID: 58 *scop.RemoveRelationDependedOnBy DependedOnBy: 59 TableID: 58 + *scop.MarkDescriptorAsDropped + DescID: 59 *scop.RemoveRelationDependedOnBy DependedOnBy: 60 TableID: 58 *scop.RemoveRelationDependedOnBy DependedOnBy: 60 TableID: 59 + *scop.MarkDescriptorAsDropped + DescID: 60 *scop.RemoveRelationDependedOnBy DependedOnBy: 61 TableID: 59 + *scop.MarkDescriptorAsDropped + DescID: 61 *scop.RemoveRelationDependedOnBy DependedOnBy: 64 TableID: 61 + *scop.MarkDescriptorAsDropped + DescID: 64 + *scop.MarkDescriptorAsDropped + DescID: 62 + *scop.RemoveTypeBackRef + DescID: 64 + TypeID: 62 + *scop.MarkDescriptorAsDropped + DescID: 63 + *scop.RemoveTypeBackRef + DescID: 64 + TypeID: 63 *scop.RemoveColumnDefaultExpression ColumnID: 1 TableID: 56 @@ -106,66 +161,42 @@ Stage 0 DependedOnBy: 56 TableID: 55 *scop.MarkDescriptorAsDropped - TableID: 62 + DescID: 53 *scop.MarkDescriptorAsDropped - TableID: 63 -Stage 1 (non-revertible) - *scop.MarkDescriptorAsDropped - TableID: 54 + DescID: 52 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 54 *scop.CreateGcJobForDescriptor DescID: 54 - *scop.MarkDescriptorAsDropped - TableID: 57 *scop.DrainDescriptorName TableID: 57 *scop.CreateGcJobForDescriptor DescID: 57 - *scop.MarkDescriptorAsDropped - TableID: 55 *scop.DrainDescriptorName TableID: 55 *scop.CreateGcJobForDescriptor DescID: 55 - *scop.MarkDescriptorAsDropped - TableID: 60 *scop.DrainDescriptorName TableID: 60 *scop.CreateGcJobForDescriptor DescID: 60 - *scop.MarkDescriptorAsDropped - TableID: 59 *scop.DrainDescriptorName TableID: 59 *scop.CreateGcJobForDescriptor DescID: 59 - *scop.MarkDescriptorAsDropped - TableID: 58 *scop.DrainDescriptorName TableID: 58 *scop.CreateGcJobForDescriptor DescID: 58 - *scop.RemoveTypeBackRef - DescID: 64 - TypeID: 62 - *scop.MarkDescriptorAsDropped - TableID: 64 *scop.DrainDescriptorName TableID: 64 *scop.CreateGcJobForDescriptor DescID: 64 - *scop.MarkDescriptorAsDropped - TableID: 61 *scop.DrainDescriptorName TableID: 61 *scop.CreateGcJobForDescriptor DescID: 61 - *scop.RemoveTypeBackRef - DescID: 64 - TypeID: 63 - *scop.MarkDescriptorAsDropped - TableID: 56 *scop.DrainDescriptorName TableID: 56 *scop.CreateGcJobForDescriptor @@ -174,11 +205,6 @@ Stage 1 (non-revertible) TableID: 62 *scop.DrainDescriptorName TableID: 63 - *scop.MarkDescriptorAsDropped - TableID: 53 - *scop.MarkDescriptorAsDropped - TableID: 52 -Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 53 *scop.DrainDescriptorName @@ -187,62 +213,98 @@ Stage 2 (non-revertible) deps DROP DATABASE db1 CASCADE ---- -- from: [Database:{DescID: 52}, DELETE_ONLY] +- from: [Database:{DescID: 52}, ABSENT] to: [Sequence:{DescID: 54}, ABSENT] -- from: [Database:{DescID: 52}, DELETE_ONLY] +- from: [Database:{DescID: 52}, ABSENT] to: [Table:{DescID: 57}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [DefaultExpression:{DescID: 56, ColumnID: 1}, ABSENT] + to: [Table:{DescID: 56}, DROPPED] +- from: [DefaultExpression:{DescID: 56, ColumnID: 2}, ABSENT] + to: [Table:{DescID: 56}, DROPPED] +- from: [DefaultExpression:{DescID: 56, ColumnID: 3}, ABSENT] + to: [Table:{DescID: 56}, DROPPED] +- from: [DefaultExpression:{DescID: 57, ColumnID: 1}, ABSENT] + to: [Table:{DescID: 57}, DROPPED] +- from: [DefaultExpression:{DescID: 57, ColumnID: 2}, ABSENT] + to: [Table:{DescID: 57}, DROPPED] +- from: [DefaultExpression:{DescID: 57, ColumnID: 3}, ABSENT] + to: [Table:{DescID: 57}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 57}, ABSENT] + to: [Sequence:{DescID: 54}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 56}, ABSENT] + to: [Sequence:{DescID: 55}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 58}, ABSENT] + to: [Table:{DescID: 56}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 58, ReferencedDescID: 59}, ABSENT] + to: [View:{DescID: 58}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 58, ReferencedDescID: 60}, ABSENT] + to: [View:{DescID: 58}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 59, ReferencedDescID: 60}, ABSENT] + to: [View:{DescID: 59}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 59, ReferencedDescID: 61}, ABSENT] + to: [View:{DescID: 59}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 61, ReferencedDescID: 64}, ABSENT] + to: [View:{DescID: 61}, DROPPED] +- from: [Schema:{DescID: 53}, ABSENT] to: [Sequence:{DescID: 55}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [Table:{DescID: 56}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [Type:{DescID: 62}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [Type:{DescID: 63}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [View:{DescID: 58}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [View:{DescID: 59}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [View:{DescID: 60}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [View:{DescID: 61}, ABSENT] -- from: [Schema:{DescID: 53}, DELETE_ONLY] +- from: [Schema:{DescID: 53}, ABSENT] to: [View:{DescID: 64}, ABSENT] -- from: [Sequence:{DescID: 54}, PUBLIC] - to: [DefaultExpression:{DescID: 57, ColumnID: 3}, ABSENT] -- from: [Sequence:{DescID: 55}, PUBLIC] - to: [DefaultExpression:{DescID: 56, ColumnID: 3}, ABSENT] -- from: [Table:{DescID: 56}, PUBLIC] +- from: [Sequence:{DescID: 54}, ABSENT] + to: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 57}, ABSENT] +- from: [Sequence:{DescID: 55}, ABSENT] + to: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 56}, ABSENT] +- from: [Table:{DescID: 56}, ABSENT] to: [DefaultExpression:{DescID: 56, ColumnID: 1}, ABSENT] -- from: [Table:{DescID: 56}, PUBLIC] +- from: [Table:{DescID: 56}, ABSENT] to: [DefaultExpression:{DescID: 56, ColumnID: 2}, ABSENT] -- from: [Table:{DescID: 56}, PUBLIC] +- from: [Table:{DescID: 56}, ABSENT] to: [DefaultExpression:{DescID: 56, ColumnID: 3}, ABSENT] -- from: [Table:{DescID: 56}, PUBLIC] - to: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 56}, ABSENT] -- from: [Table:{DescID: 56}, PUBLIC] +- from: [Table:{DescID: 56}, ABSENT] + to: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 58}, ABSENT] +- from: [Table:{DescID: 56}, ABSENT] to: [View:{DescID: 58}, ABSENT] -- from: [Table:{DescID: 57}, PUBLIC] +- from: [Table:{DescID: 57}, ABSENT] to: [DefaultExpression:{DescID: 57, ColumnID: 1}, ABSENT] -- from: [Table:{DescID: 57}, PUBLIC] +- from: [Table:{DescID: 57}, ABSENT] to: [DefaultExpression:{DescID: 57, ColumnID: 2}, ABSENT] -- from: [Table:{DescID: 57}, PUBLIC] +- from: [Table:{DescID: 57}, ABSENT] to: [DefaultExpression:{DescID: 57, ColumnID: 3}, ABSENT] -- from: [Table:{DescID: 57}, PUBLIC] - to: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 57}, ABSENT] -- from: [Type:{DescID: 62}, PUBLIC] - to: [TypeReference:{DescID: 64, ReferencedDescID: 62}, ABSENT] -- from: [Type:{DescID: 63}, PUBLIC] - to: [TypeReference:{DescID: 64, ReferencedDescID: 63}, ABSENT] +- from: [TypeReference:{DescID: 64, ReferencedDescID: 62}, ABSENT] + to: [Type:{DescID: 62}, DROPPED] +- from: [TypeReference:{DescID: 64, ReferencedDescID: 63}, ABSENT] + to: [Type:{DescID: 63}, DROPPED] +- from: [View:{DescID: 58}, ABSENT] + to: [RelationDependedOnBy:{DescID: 58, ReferencedDescID: 59}, ABSENT] +- from: [View:{DescID: 58}, ABSENT] + to: [RelationDependedOnBy:{DescID: 58, ReferencedDescID: 60}, ABSENT] - from: [View:{DescID: 58}, ABSENT] to: [View:{DescID: 59}, ABSENT] - from: [View:{DescID: 58}, ABSENT] to: [View:{DescID: 60}, ABSENT] +- from: [View:{DescID: 59}, ABSENT] + to: [RelationDependedOnBy:{DescID: 59, ReferencedDescID: 60}, ABSENT] +- from: [View:{DescID: 59}, ABSENT] + to: [RelationDependedOnBy:{DescID: 59, ReferencedDescID: 61}, ABSENT] - from: [View:{DescID: 59}, ABSENT] to: [View:{DescID: 60}, ABSENT] - from: [View:{DescID: 59}, ABSENT] to: [View:{DescID: 61}, ABSENT] +- from: [View:{DescID: 61}, ABSENT] + to: [RelationDependedOnBy:{DescID: 61, ReferencedDescID: 64}, ABSENT] - from: [View:{DescID: 61}, ABSENT] to: [View:{DescID: 64}, ABSENT] - from: [View:{DescID: 64}, ABSENT] diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_schema b/pkg/sql/schemachanger/scplan/testdata/drop_schema index 92ff58c28f99..c4ce0839f5ea 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_schema +++ b/pkg/sql/schemachanger/scplan/testdata/drop_schema @@ -37,48 +37,78 @@ CREATE VIEW sc1.v5 AS (SELECT 'a'::sc1.typ::string AS k, n2, n1 from sc1.v4) deps DROP SCHEMA defaultdb.SC1 CASCADE ---- -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [DefaultExpression:{DescID: 54, ColumnID: 1}, ABSENT] + to: [Table:{DescID: 54}, DROPPED] +- from: [DefaultExpression:{DescID: 54, ColumnID: 2}, ABSENT] + to: [Table:{DescID: 54}, DROPPED] +- from: [DefaultExpression:{DescID: 54, ColumnID: 3}, ABSENT] + to: [Table:{DescID: 54}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 54}, ABSENT] + to: [Sequence:{DescID: 53}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 55}, ABSENT] + to: [Table:{DescID: 54}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 56}, ABSENT] + to: [View:{DescID: 55}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 57}, ABSENT] + to: [View:{DescID: 55}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 57}, ABSENT] + to: [View:{DescID: 56}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 58}, ABSENT] + to: [View:{DescID: 56}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 58, ReferencedDescID: 61}, ABSENT] + to: [View:{DescID: 58}, DROPPED] +- from: [Schema:{DescID: 52}, ABSENT] to: [Sequence:{DescID: 53}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [Table:{DescID: 54}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [Type:{DescID: 59}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [Type:{DescID: 60}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [View:{DescID: 55}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [View:{DescID: 56}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [View:{DescID: 57}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [View:{DescID: 58}, ABSENT] -- from: [Schema:{DescID: 52}, DELETE_ONLY] +- from: [Schema:{DescID: 52}, ABSENT] to: [View:{DescID: 61}, ABSENT] -- from: [Sequence:{DescID: 53}, PUBLIC] - to: [DefaultExpression:{DescID: 54, ColumnID: 3}, ABSENT] -- from: [Table:{DescID: 54}, PUBLIC] +- from: [Sequence:{DescID: 53}, ABSENT] + to: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 54}, ABSENT] +- from: [Table:{DescID: 54}, ABSENT] to: [DefaultExpression:{DescID: 54, ColumnID: 1}, ABSENT] -- from: [Table:{DescID: 54}, PUBLIC] +- from: [Table:{DescID: 54}, ABSENT] to: [DefaultExpression:{DescID: 54, ColumnID: 2}, ABSENT] -- from: [Table:{DescID: 54}, PUBLIC] +- from: [Table:{DescID: 54}, ABSENT] to: [DefaultExpression:{DescID: 54, ColumnID: 3}, ABSENT] -- from: [Table:{DescID: 54}, PUBLIC] - to: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 54}, ABSENT] -- from: [Table:{DescID: 54}, PUBLIC] +- from: [Table:{DescID: 54}, ABSENT] + to: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 55}, ABSENT] +- from: [Table:{DescID: 54}, ABSENT] to: [View:{DescID: 55}, ABSENT] -- from: [Type:{DescID: 59}, PUBLIC] - to: [TypeReference:{DescID: 61, ReferencedDescID: 59}, ABSENT] -- from: [Type:{DescID: 60}, PUBLIC] - to: [TypeReference:{DescID: 61, ReferencedDescID: 60}, ABSENT] +- from: [TypeReference:{DescID: 61, ReferencedDescID: 59}, ABSENT] + to: [Type:{DescID: 59}, DROPPED] +- from: [TypeReference:{DescID: 61, ReferencedDescID: 60}, ABSENT] + to: [Type:{DescID: 60}, DROPPED] +- from: [View:{DescID: 55}, ABSENT] + to: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 56}, ABSENT] +- from: [View:{DescID: 55}, ABSENT] + to: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 57}, ABSENT] - from: [View:{DescID: 55}, ABSENT] to: [View:{DescID: 56}, ABSENT] - from: [View:{DescID: 55}, ABSENT] to: [View:{DescID: 57}, ABSENT] +- from: [View:{DescID: 56}, ABSENT] + to: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 57}, ABSENT] +- from: [View:{DescID: 56}, ABSENT] + to: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 58}, ABSENT] - from: [View:{DescID: 56}, ABSENT] to: [View:{DescID: 57}, ABSENT] - from: [View:{DescID: 56}, ABSENT] to: [View:{DescID: 58}, ABSENT] +- from: [View:{DescID: 58}, ABSENT] + to: [RelationDependedOnBy:{DescID: 58, ReferencedDescID: 61}, ABSENT] - from: [View:{DescID: 58}, ABSENT] to: [View:{DescID: 61}, ABSENT] - from: [View:{DescID: 61}, ABSENT] @@ -90,29 +120,74 @@ ops DROP SCHEMA defaultdb.SC1 CASCADE ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 53 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 55 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 56 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 57 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 58 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 61 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 54 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 59 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 60 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 52 +Stage 1 (non-revertible) + *scop.MarkDescriptorAsDropped + DescID: 54 *scop.RemoveColumnDefaultExpression ColumnID: 3 TableID: 54 *scop.UpdateRelationDeps TableID: 54 + *scop.MarkDescriptorAsDropped + DescID: 53 *scop.RemoveRelationDependedOnBy DependedOnBy: 55 TableID: 54 + *scop.MarkDescriptorAsDropped + DescID: 55 *scop.RemoveRelationDependedOnBy DependedOnBy: 56 TableID: 55 + *scop.MarkDescriptorAsDropped + DescID: 56 *scop.RemoveRelationDependedOnBy DependedOnBy: 57 TableID: 55 *scop.RemoveRelationDependedOnBy DependedOnBy: 57 TableID: 56 + *scop.MarkDescriptorAsDropped + DescID: 57 *scop.RemoveRelationDependedOnBy DependedOnBy: 58 TableID: 56 + *scop.MarkDescriptorAsDropped + DescID: 58 *scop.RemoveRelationDependedOnBy DependedOnBy: 61 TableID: 58 + *scop.MarkDescriptorAsDropped + DescID: 61 + *scop.MarkDescriptorAsDropped + DescID: 59 + *scop.RemoveTypeBackRef + DescID: 61 + TypeID: 59 + *scop.MarkDescriptorAsDropped + DescID: 60 + *scop.RemoveTypeBackRef + DescID: 61 + TypeID: 60 *scop.RemoveColumnDefaultExpression ColumnID: 1 TableID: 54 @@ -127,54 +202,32 @@ Stage 0 DependedOnBy: 54 TableID: 53 *scop.MarkDescriptorAsDropped - TableID: 59 - *scop.MarkDescriptorAsDropped - TableID: 60 -Stage 1 (non-revertible) - *scop.MarkDescriptorAsDropped - TableID: 53 + DescID: 52 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 53 *scop.CreateGcJobForDescriptor DescID: 53 - *scop.MarkDescriptorAsDropped - TableID: 57 *scop.DrainDescriptorName TableID: 57 *scop.CreateGcJobForDescriptor DescID: 57 - *scop.MarkDescriptorAsDropped - TableID: 56 *scop.DrainDescriptorName TableID: 56 *scop.CreateGcJobForDescriptor DescID: 56 - *scop.MarkDescriptorAsDropped - TableID: 55 *scop.DrainDescriptorName TableID: 55 *scop.CreateGcJobForDescriptor DescID: 55 - *scop.RemoveTypeBackRef - DescID: 61 - TypeID: 59 - *scop.MarkDescriptorAsDropped - TableID: 61 *scop.DrainDescriptorName TableID: 61 *scop.CreateGcJobForDescriptor DescID: 61 - *scop.MarkDescriptorAsDropped - TableID: 58 *scop.DrainDescriptorName TableID: 58 *scop.CreateGcJobForDescriptor DescID: 58 - *scop.RemoveTypeBackRef - DescID: 61 - TypeID: 60 - *scop.MarkDescriptorAsDropped - TableID: 54 *scop.DrainDescriptorName TableID: 54 *scop.CreateGcJobForDescriptor @@ -183,8 +236,5 @@ Stage 1 (non-revertible) TableID: 59 *scop.DrainDescriptorName TableID: 60 - *scop.MarkDescriptorAsDropped - TableID: 52 -Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 52 diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_sequence b/pkg/sql/schemachanger/scplan/testdata/drop_sequence index cb043bff6890..3f912b763e60 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_sequence +++ b/pkg/sql/schemachanger/scplan/testdata/drop_sequence @@ -5,9 +5,13 @@ CREATE SEQUENCE defaultdb.SQ1 ops DROP SEQUENCE defaultdb.SQ1 CASCADE ---- -Stage 0 (non-revertible) +Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 52 +Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped - TableID: 52 + DescID: 52 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 52 *scop.CreateGcJobForDescriptor @@ -25,6 +29,9 @@ ops DROP SEQUENCE defaultdb.SQ1 CASCADE ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 52 +Stage 1 (non-revertible) *scop.RemoveColumnDefaultExpression ColumnID: 2 TableID: 53 @@ -35,9 +42,9 @@ Stage 0 TableID: 54 *scop.UpdateRelationDeps TableID: 54 -Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped - TableID: 52 + DescID: 52 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 52 *scop.CreateGcJobForDescriptor @@ -47,7 +54,3 @@ Stage 1 (non-revertible) deps DROP SEQUENCE defaultdb.SQ1 CASCADE ---- -- from: [Sequence:{DescID: 52}, PUBLIC] - to: [DefaultExpression:{DescID: 53, ColumnID: 2}, ABSENT] -- from: [Sequence:{DescID: 52}, PUBLIC] - to: [DefaultExpression:{DescID: 54, ColumnID: 2}, ABSENT] diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_table b/pkg/sql/schemachanger/scplan/testdata/drop_table index f098e99104fd..54d707fea9bf 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_table +++ b/pkg/sql/schemachanger/scplan/testdata/drop_table @@ -39,9 +39,20 @@ ops DROP TABLE defaultdb.shipments CASCADE; ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 57 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 56 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 55 +Stage 1 (non-revertible) + *scop.MarkDescriptorAsDropped + DescID: 55 *scop.RemoveRelationDependedOnBy DependedOnBy: 57 TableID: 55 + *scop.MarkDescriptorAsDropped + DescID: 57 *scop.DropForeignKeyRef Name: fk_customers Outbound: true @@ -61,6 +72,10 @@ Stage 0 TableID: 55 *scop.UpdateRelationDeps TableID: 55 + *scop.MarkDescriptorAsDropped + DescID: 56 + *scop.RemoveSequenceOwnedBy + TableID: 56 *scop.RemoveColumnDefaultExpression ColumnID: 2 TableID: 55 @@ -84,23 +99,15 @@ Stage 0 *scop.RemoveRelationDependedOnBy DependedOnBy: 55 TableID: 54 -Stage 1 (non-revertible) - *scop.MarkDescriptorAsDropped - TableID: 57 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 57 *scop.CreateGcJobForDescriptor DescID: 57 - *scop.MarkDescriptorAsDropped - TableID: 56 *scop.DrainDescriptorName TableID: 56 *scop.CreateGcJobForDescriptor DescID: 56 - *scop.RemoveSequenceOwnedBy - TableID: 56 - *scop.MarkDescriptorAsDropped - TableID: 55 *scop.DrainDescriptorName TableID: 55 *scop.CreateGcJobForDescriptor @@ -109,25 +116,45 @@ Stage 1 (non-revertible) deps DROP TABLE defaultdb.shipments CASCADE; ---- +- from: [DefaultExpression:{DescID: 55, ColumnID: 1}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [DefaultExpression:{DescID: 55, ColumnID: 2}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [DefaultExpression:{DescID: 55, ColumnID: 3}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [DefaultExpression:{DescID: 55, ColumnID: 4}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [DefaultExpression:{DescID: 55, ColumnID: 5}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [InboundForeignKey:{DescID: 52, Name: fk_customers, ReferencedDescID: 55}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [InboundForeignKey:{DescID: 53, Name: fk_orders, ReferencedDescID: 55}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [OutboundForeignKey:{DescID: 55, Name: fk_customers, ReferencedDescID: 52}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [OutboundForeignKey:{DescID: 55, Name: fk_orders, ReferencedDescID: 53}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 57}, ABSENT] + to: [Table:{DescID: 55}, DROPPED] +- from: [Sequence:{DescID: 56}, ABSENT] + to: [SequenceOwnedBy:{DescID: 56, ReferencedDescID: 55}, ABSENT] - from: [SequenceOwnedBy:{DescID: 56, ReferencedDescID: 55}, ABSENT] - to: [Sequence:{DescID: 56}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] + to: [Sequence:{DescID: 56}, DROPPED] +- from: [Table:{DescID: 55}, ABSENT] to: [DefaultExpression:{DescID: 55, ColumnID: 1}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] to: [DefaultExpression:{DescID: 55, ColumnID: 2}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] to: [DefaultExpression:{DescID: 55, ColumnID: 3}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] to: [DefaultExpression:{DescID: 55, ColumnID: 4}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] to: [DefaultExpression:{DescID: 55, ColumnID: 5}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] to: [OutboundForeignKey:{DescID: 55, Name: fk_customers, ReferencedDescID: 52}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] to: [OutboundForeignKey:{DescID: 55, Name: fk_orders, ReferencedDescID: 53}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] - to: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 55}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] - to: [SequenceOwnedBy:{DescID: 56, ReferencedDescID: 55}, ABSENT] -- from: [Table:{DescID: 55}, PUBLIC] +- from: [Table:{DescID: 55}, ABSENT] + to: [RelationDependedOnBy:{DescID: 55, ReferencedDescID: 57}, ABSENT] +- from: [Table:{DescID: 55}, ABSENT] to: [View:{DescID: 57}, ABSENT] diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_type b/pkg/sql/schemachanger/scplan/testdata/drop_type index aa528eeec30b..9225c7c4d335 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_type +++ b/pkg/sql/schemachanger/scplan/testdata/drop_type @@ -6,11 +6,16 @@ ops DROP TYPE defaultdb.typ ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 52 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 53 +Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped - TableID: 52 + DescID: 52 *scop.MarkDescriptorAsDropped - TableID: 53 -Stage 1 (non-revertible) + DescID: 53 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 52 *scop.DrainDescriptorName diff --git a/pkg/sql/schemachanger/scplan/testdata/drop_view b/pkg/sql/schemachanger/scplan/testdata/drop_view index 46df9bb07b6d..48205a5edd42 100644 --- a/pkg/sql/schemachanger/scplan/testdata/drop_view +++ b/pkg/sql/schemachanger/scplan/testdata/drop_view @@ -10,12 +10,15 @@ ops DROP VIEW defaultdb.v1 ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 53 +Stage 1 (non-revertible) *scop.RemoveRelationDependedOnBy DependedOnBy: 53 TableID: 52 -Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped - TableID: 53 + DescID: 53 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 53 *scop.CreateGcJobForDescriptor @@ -49,73 +52,104 @@ ops DROP VIEW defaultdb.v1 CASCADE ---- Stage 0 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 53 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 54 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 55 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 56 + *scop.MarkDescriptorAsDroppedSynthetically + DescID: 59 +Stage 1 (non-revertible) *scop.RemoveRelationDependedOnBy DependedOnBy: 53 TableID: 52 + *scop.MarkDescriptorAsDropped + DescID: 53 *scop.RemoveRelationDependedOnBy DependedOnBy: 54 TableID: 53 + *scop.MarkDescriptorAsDropped + DescID: 54 *scop.RemoveRelationDependedOnBy DependedOnBy: 55 TableID: 53 *scop.RemoveRelationDependedOnBy DependedOnBy: 55 TableID: 54 + *scop.MarkDescriptorAsDropped + DescID: 55 *scop.RemoveRelationDependedOnBy DependedOnBy: 56 TableID: 54 + *scop.MarkDescriptorAsDropped + DescID: 56 *scop.RemoveRelationDependedOnBy DependedOnBy: 59 TableID: 56 -Stage 1 (non-revertible) *scop.MarkDescriptorAsDropped - TableID: 55 + DescID: 59 + *scop.RemoveTypeBackRef + DescID: 59 + TypeID: 57 + *scop.RemoveTypeBackRef + DescID: 59 + TypeID: 58 +Stage 2 (non-revertible) *scop.DrainDescriptorName TableID: 55 *scop.CreateGcJobForDescriptor DescID: 55 - *scop.MarkDescriptorAsDropped - TableID: 54 *scop.DrainDescriptorName TableID: 54 *scop.CreateGcJobForDescriptor DescID: 54 - *scop.MarkDescriptorAsDropped - TableID: 53 *scop.DrainDescriptorName TableID: 53 *scop.CreateGcJobForDescriptor DescID: 53 - *scop.RemoveTypeBackRef - DescID: 59 - TypeID: 57 - *scop.MarkDescriptorAsDropped - TableID: 59 *scop.DrainDescriptorName TableID: 59 *scop.CreateGcJobForDescriptor DescID: 59 - *scop.MarkDescriptorAsDropped - TableID: 56 *scop.DrainDescriptorName TableID: 56 *scop.CreateGcJobForDescriptor DescID: 56 - *scop.RemoveTypeBackRef - DescID: 59 - TypeID: 58 deps DROP VIEW defaultdb.v1 CASCADE ---- +- from: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 54}, ABSENT] + to: [View:{DescID: 53}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 55}, ABSENT] + to: [View:{DescID: 53}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 55}, ABSENT] + to: [View:{DescID: 54}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 56}, ABSENT] + to: [View:{DescID: 54}, DROPPED] +- from: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 59}, ABSENT] + to: [View:{DescID: 56}, DROPPED] +- from: [View:{DescID: 53}, ABSENT] + to: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 54}, ABSENT] +- from: [View:{DescID: 53}, ABSENT] + to: [RelationDependedOnBy:{DescID: 53, ReferencedDescID: 55}, ABSENT] - from: [View:{DescID: 53}, ABSENT] to: [View:{DescID: 54}, ABSENT] - from: [View:{DescID: 53}, ABSENT] to: [View:{DescID: 55}, ABSENT] +- from: [View:{DescID: 54}, ABSENT] + to: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 55}, ABSENT] +- from: [View:{DescID: 54}, ABSENT] + to: [RelationDependedOnBy:{DescID: 54, ReferencedDescID: 56}, ABSENT] - from: [View:{DescID: 54}, ABSENT] to: [View:{DescID: 55}, ABSENT] - from: [View:{DescID: 54}, ABSENT] to: [View:{DescID: 56}, ABSENT] +- from: [View:{DescID: 56}, ABSENT] + to: [RelationDependedOnBy:{DescID: 56, ReferencedDescID: 59}, ABSENT] - from: [View:{DescID: 56}, ABSENT] to: [View:{DescID: 59}, ABSENT] - from: [View:{DescID: 59}, ABSENT] diff --git a/pkg/sql/sem/tree/name_resolution.go b/pkg/sql/sem/tree/name_resolution.go index 3b1ec37a7b6c..70c643c73c74 100644 --- a/pkg/sql/sem/tree/name_resolution.go +++ b/pkg/sql/sem/tree/name_resolution.go @@ -232,6 +232,8 @@ type CommonLookupFlags struct { IncludeOffline bool // IncludeOffline specifies if dropped descriptors should be visible. IncludeDropped bool + // AvoidSynthetic specifies if the any synthetic descriptors will be ignored. + AvoidSynthetic bool } // SchemaLookupFlags is the flag struct suitable for GetSchemaByName(). From 5542b9bd433c8590e2b659849b33a72dce50209b Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Wed, 27 Oct 2021 13:23:59 -0400 Subject: [PATCH 069/205] dev: override bazel timeout when run under stress When running under stress and no timeout is specified, we want to respect the timeout passed down to stress[^1]. Not doing so has bazel default to a timeout based on the test target's size[^2], which is not what we want. [^1]: Through --stress-arg=-maxtime or if nothing is specified, a -maxtime of 0 that's taken as "run forever") [^2]: https://docs.bazel.build/versions/main/test-encyclopedia.html#role-of-the-test-runner Release note: None --- pkg/cmd/dev/test.go | 33 ++++++++++++++++--------- pkg/cmd/dev/testdata/recording/test.txt | 2 +- pkg/cmd/dev/testdata/test.txt | 2 +- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/pkg/cmd/dev/test.go b/pkg/cmd/dev/test.go index 407be7285ca0..283d8b34093d 100644 --- a/pkg/cmd/dev/test.go +++ b/pkg/cmd/dev/test.go @@ -54,7 +54,7 @@ func makeTestCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Comm addCommonTestFlags(testCmd) testCmd.Flags().BoolP(vFlag, "v", false, "enable logging during test runs") testCmd.Flags().Bool(stressFlag, false, "run tests under stress") - testCmd.Flags().String(stressArgsFlag, "", "Additional arguments to pass to stress") + testCmd.Flags().String(stressArgsFlag, "", "additional arguments to pass to stress") testCmd.Flags().Bool(raceFlag, false, "run tests using race builds") testCmd.Flags().Bool(ignoreCacheFlag, false, "ignore cached test runs") testCmd.Flags().String(rewriteFlag, "", "argument to pass to underlying (only applicable for certain tests, e.g. logic and datadriven tests). If unspecified, -rewrite will be passed to the test binary.") @@ -83,9 +83,6 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { rewrite = "-rewrite" } - d.log.Printf("unit test args: stress=%t race=%t filter=%s timeout=%s ignore-cache=%t pkgs=%s", - stress, race, filter, timeout, ignoreCache, pkgs) - var args []string args = append(args, "test") args = append(args, mustGetRemoteCacheArgs(remoteCacheAddr)...) @@ -178,17 +175,29 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { args = append(args, fmt.Sprintf("--sandbox_writable_path=%s", filepath.Join(workspace, dir))) } } - if stress && timeout > 0 { - args = append(args, "--run_under", fmt.Sprintf("%s -maxtime=%s %s", stressTarget, timeout, stressArgs)) - // The timeout should be a bit higher than the stress duration. - // Bazel will probably think the timeout for this test isn't so - // long. - args = append(args, fmt.Sprintf("--test_timeout=%d", int((timeout+1*time.Second).Seconds()))) - } else if stress { - args = append(args, "--run_under", fmt.Sprintf("%s %s", stressTarget, stressArgs)) + if stress { + if timeout > 0 { + args = append(args, "--run_under", + fmt.Sprintf("%s -maxtime=%s %s", stressTarget, timeout, stressArgs)) + + // The bazel timeout needs to be higher than the stress duration to + // pass reliably. + args = append(args, fmt.Sprintf("--test_timeout=%.0f", (timeout+time.Second).Seconds())) + } else { + // We're running under stress and no timeout is specified. We want + // to respect the timeout passed down to stress[1]. Similar to above + // we want the bazel timeout to be longer, so lets just set it to + // 24h. + // + // [1]: Through --stress-arg=-maxtime or if nothing is specified, a + // -maxtime of 0 that's taken as "run forever") + args = append(args, "--run_under", fmt.Sprintf("%s %s", stressTarget, stressArgs)) + args = append(args, fmt.Sprintf("--test_timeout=%.0f", 24*time.Hour.Seconds())) + } } else if timeout > 0 { args = append(args, fmt.Sprintf("--test_timeout=%d", int(timeout.Seconds()))) } + if filter != "" { args = append(args, fmt.Sprintf("--test_filter=%s", filter)) } diff --git a/pkg/cmd/dev/testdata/recording/test.txt b/pkg/cmd/dev/testdata/recording/test.txt index e25223471f80..6f438e02163a 100644 --- a/pkg/cmd/dev/testdata/recording/test.txt +++ b/pkg/cmd/dev/testdata/recording/test.txt @@ -84,7 +84,7 @@ bazel query 'kind(go_test, //pkg/util/tracing:all)' ---- //pkg/util/tracing:tracing_test -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress ' '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress ' --test_timeout=86400 '--test_filter=TestStartChild*' --test_output streamed ---- ---- //pkg/util/tracing:tracing_test PASSED in 12.3s diff --git a/pkg/cmd/dev/testdata/test.txt b/pkg/cmd/dev/testdata/test.txt index 219b42681d86..492431d6ace6 100644 --- a/pkg/cmd/dev/testdata/test.txt +++ b/pkg/cmd/dev/testdata/test.txt @@ -31,7 +31,7 @@ bazel test //pkg/util/tracing:tracing_test --nocache_test_results '--test_filter dev test --stress pkg/util/tracing --filter TestStartChild* ---- bazel query 'kind(go_test, //pkg/util/tracing:all)' -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress ' '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress ' --test_timeout=86400 '--test_filter=TestStartChild*' --test_output streamed dev test --stress pkg/util/tracing --filter TestStartChild* --timeout=10s -v ---- From 4cd306208480af038eb7d2bdf4a8aac13d2a03f6 Mon Sep 17 00:00:00 2001 From: rimadeodhar Date: Wed, 11 Aug 2021 13:59:17 -0700 Subject: [PATCH 070/205] sqlinstance: Update instancereader with cache. This PR adds rangefeed backed cache for the sqlinstance table. This will prevent the SQL instance lookup code from making multiple roundtrips. This should have good performance gains for when we support multi-region for serverless. Release note (performance improvement): The sqlinstance subsystem no longer reads from the backing SQL table for every request for SQL instance details. This will result in improved performance for when we support multi-region setup for the multi-tenant architecture. --- pkg/server/server_sql.go | 8 +- .../sqlinstance/instanceprovider/BUILD.bazel | 2 + .../instanceprovider/instanceprovider.go | 56 +++- .../instanceprovider/test_helpers.go | 1 + .../sqlinstance/instancestorage/BUILD.bazel | 6 + .../instancestorage/instancereader.go | 251 +++++++++++++++--- .../instancestorage/instancereader_test.go | 205 ++++++++------ .../instancestorage/instancestorage.go | 4 +- .../instancestorage/instancestorage_test.go | 4 +- .../sqlinstance/instancestorage/row_codec.go | 19 +- pkg/sql/sqlinstance/sqlinstance.go | 8 + 11 files changed, 430 insertions(+), 134 deletions(-) diff --git a/pkg/server/server_sql.go b/pkg/server/server_sql.go index 699f3aa4da05..f3177deb2eee 100644 --- a/pkg/server/server_sql.go +++ b/pkg/server/server_sql.go @@ -361,7 +361,7 @@ func newSQLServer(ctx context.Context, cfg sqlServerArgs) (*SQLServer, error) { cfg.stopper, cfg.clock, cfg.db, codec, cfg.Settings, sqllivenessKnobs, ) cfg.sqlInstanceProvider = instanceprovider.New( - cfg.stopper, cfg.db, codec, cfg.sqlLivenessProvider, cfg.advertiseAddr, + cfg.stopper, cfg.db, codec, cfg.sqlLivenessProvider, cfg.advertiseAddr, cfg.rangeFeedFactory, cfg.clock, ) jobRegistry := cfg.circularJobRegistry @@ -953,6 +953,12 @@ func (s *SQLServer) startSQLLivenessAndInstanceProviders(ctx context.Context) er } } s.sqlLivenessProvider.Start(ctx) + // sqlInstanceProvider must always be started after sqlLivenessProvider + // as sqlInstanceProvider relies on the session initialized and maintained by + // sqlLivenessProvider. + if err := s.sqlInstanceProvider.Start(ctx); err != nil { + return err + } return nil } diff --git a/pkg/sql/sqlinstance/instanceprovider/BUILD.bazel b/pkg/sql/sqlinstance/instanceprovider/BUILD.bazel index 7e7b55ed2061..45ab33fc1a16 100644 --- a/pkg/sql/sqlinstance/instanceprovider/BUILD.bazel +++ b/pkg/sql/sqlinstance/instanceprovider/BUILD.bazel @@ -12,12 +12,14 @@ go_library( "//pkg/base", "//pkg/keys", "//pkg/kv", + "//pkg/kv/kvclient/rangefeed:with-mocks", "//pkg/sql/sqlinstance", "//pkg/sql/sqlinstance/instancestorage", "//pkg/sql/sqlliveness", "//pkg/util/hlc", "//pkg/util/log", "//pkg/util/stop", + "//pkg/util/syncutil", "@com_github_cockroachdb_errors//:errors", "@com_github_cockroachdb_logtags//:logtags", ], diff --git a/pkg/sql/sqlinstance/instanceprovider/instanceprovider.go b/pkg/sql/sqlinstance/instanceprovider/instanceprovider.go index ef6643b2d91e..ca65f0222417 100644 --- a/pkg/sql/sqlinstance/instanceprovider/instanceprovider.go +++ b/pkg/sql/sqlinstance/instanceprovider/instanceprovider.go @@ -18,12 +18,14 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed" "github.com/cockroachdb/cockroach/pkg/sql/sqlinstance" "github.com/cockroachdb/cockroach/pkg/sql/sqlinstance/instancestorage" "github.com/cockroachdb/cockroach/pkg/sql/sqlliveness" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/stop" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" "github.com/cockroachdb/errors" "github.com/cockroachdb/logtags" ) @@ -35,7 +37,7 @@ type writer interface { // provider implements the sqlinstance.Provider interface for access to the sqlinstance subsystem. type provider struct { - sqlinstance.AddressResolver + *instancestorage.Reader storage writer stopper *stop.Stopper instanceAddr string @@ -45,6 +47,10 @@ type provider struct { instanceID base.SQLInstanceID sessionID sqlliveness.SessionID initError error + mu struct { + syncutil.Mutex + started bool + } } // New constructs a new Provider. @@ -54,24 +60,53 @@ func New( codec keys.SQLCodec, slProvider sqlliveness.Provider, addr string, + f *rangefeed.Factory, + clock *hlc.Clock, ) sqlinstance.Provider { storage := instancestorage.NewStorage(db, codec, slProvider) - reader := instancestorage.NewReader(storage, slProvider) + reader := instancestorage.NewReader(storage, slProvider.CachedReader(), f, codec, clock, stopper) p := &provider{ - storage: storage, - stopper: stopper, - AddressResolver: reader, - session: slProvider, - instanceAddr: addr, - initialized: make(chan struct{}), + storage: storage, + stopper: stopper, + Reader: reader, + session: slProvider, + instanceAddr: addr, + initialized: make(chan struct{}), } return p } -// Instance returns the instance ID for the current SQL instance. +// Start implements the sqlinstance.Provider interface. +func (p *provider) Start(ctx context.Context) error { + if p.started() { + return p.initError + } + if err := p.Reader.Start(ctx); err != nil { + p.initOnce.Do(func() { + p.initError = err + close(p.initialized) + }) + } + p.mu.Lock() + defer p.mu.Unlock() + p.mu.started = true + return p.initError +} + +func (p *provider) started() bool { + p.mu.Lock() + defer p.mu.Unlock() + return p.mu.started +} + +// Instance implements the sqlinstance.Provider interface. func (p *provider) Instance( ctx context.Context, ) (_ base.SQLInstanceID, _ sqlliveness.SessionID, err error) { + if !p.started() { + return base.SQLInstanceID(0), "", sqlinstance.NotStartedError + } + p.maybeInitialize() select { case <-ctx.Done(): @@ -119,6 +154,9 @@ func (p *provider) initialize(ctx context.Context) error { // shutdownSQLInstance shuts down the SQL instance. func (p *provider) shutdownSQLInstance(ctx context.Context) { + if !p.started() { + return + } // Initialize initError if shutdownSQLInstance is called // before initialization of the instance ID go func() { diff --git a/pkg/sql/sqlinstance/instanceprovider/test_helpers.go b/pkg/sql/sqlinstance/instanceprovider/test_helpers.go index edf3d4020222..dd83dd00b727 100644 --- a/pkg/sql/sqlinstance/instanceprovider/test_helpers.go +++ b/pkg/sql/sqlinstance/instanceprovider/test_helpers.go @@ -39,6 +39,7 @@ func NewTestInstanceProvider( instanceAddr: addr, initialized: make(chan struct{}), } + p.mu.started = true return p } diff --git a/pkg/sql/sqlinstance/instancestorage/BUILD.bazel b/pkg/sql/sqlinstance/instancestorage/BUILD.bazel index 9f62da9b6855..088cf72056ab 100644 --- a/pkg/sql/sqlinstance/instancestorage/BUILD.bazel +++ b/pkg/sql/sqlinstance/instancestorage/BUILD.bazel @@ -14,6 +14,7 @@ go_library( "//pkg/base", "//pkg/keys", "//pkg/kv", + "//pkg/kv/kvclient/rangefeed:with-mocks", "//pkg/multitenant", "//pkg/roachpb:with-mocks", "//pkg/sql/catalog", @@ -26,8 +27,10 @@ go_library( "//pkg/sql/sqlliveness", "//pkg/sql/types", "//pkg/util/encoding", + "//pkg/util/grpcutil", "//pkg/util/hlc", "//pkg/util/log", + "//pkg/util/stop", "//pkg/util/syncutil", "@com_github_cockroachdb_errors//:errors", ], @@ -44,6 +47,7 @@ go_test( ":instancestorage", "//pkg/base", "//pkg/keys", + "//pkg/kv/kvclient/rangefeed:with-mocks", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", @@ -52,6 +56,7 @@ go_test( "//pkg/sql/sqlinstance", "//pkg/sql/sqlliveness", "//pkg/sql/sqlliveness/slstorage", + "//pkg/testutils", "//pkg/testutils/serverutils", "//pkg/testutils/sqlutils", "//pkg/testutils/testcluster", @@ -62,6 +67,7 @@ go_test( "//pkg/util/stop", "//pkg/util/syncutil", "//pkg/util/timeutil", + "@com_github_cockroachdb_errors//:errors", "@com_github_stretchr_testify//require", ], ) diff --git a/pkg/sql/sqlinstance/instancestorage/instancereader.go b/pkg/sql/sqlinstance/instancestorage/instancereader.go index a295b41ccb28..157a5c7ef89d 100644 --- a/pkg/sql/sqlinstance/instancestorage/instancereader.go +++ b/pkg/sql/sqlinstance/instancestorage/instancereader.go @@ -13,76 +13,219 @@ package instancestorage import ( "context" "sort" + "strings" "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/sqlinstance" "github.com/cockroachdb/cockroach/pkg/sql/sqlliveness" + "github.com/cockroachdb/cockroach/pkg/util/grpcutil" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/stop" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" + "github.com/cockroachdb/errors" ) -// Reader implements the sqlinstance.AddressResolver interface. -// TODO(rima): Add caching backed by rangefeed to Reader. +// Reader implements the sqlinstance.AddressResolver interface. It uses +// caching backed by rangefeed to cache instance information. type Reader struct { - storage *Storage - slReader sqlliveness.Reader + storage *Storage + slReader sqlliveness.Reader + f *rangefeed.Factory + codec keys.SQLCodec + tableID descpb.ID + clock *hlc.Clock + stopper *stop.Stopper + rowcodec rowCodec + initialScanDone chan struct{} + mu struct { + syncutil.Mutex + instances map[base.SQLInstanceID]instancerow + startError error + started bool + } +} + +// NewTestingReader constructs a new Reader with control for the database +// in which the `sql_instances` table should exist. +func NewTestingReader( + storage *Storage, + slReader sqlliveness.Reader, + f *rangefeed.Factory, + codec keys.SQLCodec, + tableID descpb.ID, + clock *hlc.Clock, + stopper *stop.Stopper, +) *Reader { + r := &Reader{ + storage: storage, + slReader: slReader, + f: f, + codec: codec, + tableID: tableID, + clock: clock, + rowcodec: makeRowCodec(codec), + initialScanDone: make(chan struct{}), + stopper: stopper, + } + r.mu.instances = make(map[base.SQLInstanceID]instancerow) + return r } // NewReader constructs a new reader for SQL instance data. -func NewReader(storage *Storage, slReader sqlliveness.Reader) *Reader { - return &Reader{ - storage: storage, - slReader: slReader, +func NewReader( + storage *Storage, + slReader sqlliveness.Reader, + f *rangefeed.Factory, + codec keys.SQLCodec, + clock *hlc.Clock, + stopper *stop.Stopper, +) *Reader { + return NewTestingReader(storage, slReader, f, codec, keys.SQLInstancesTableID, clock, stopper) +} + +// Start initializes the rangefeed for the Reader. +func (r *Reader) Start(ctx context.Context) error { + rf := r.maybeStartRangeFeed(ctx) + select { + case <-r.initialScanDone: + // TODO(rimadeodhar): Avoid blocking on initial + // scan until first call to read. + if rf != nil { + // Add rangefeed to the stopper to ensure it + // is shutdown correctly. + r.stopper.AddCloser(rf) + } + return r.checkStarted() + case <-r.stopper.ShouldQuiesce(): + return errors.Wrap(stop.ErrUnavailable, + "failed to retrieve initial instance data") + case <-ctx.Done(): + return errors.Wrap(ctx.Err(), + "failed to retrieve initial instance data") + } +} +func (r *Reader) maybeStartRangeFeed(ctx context.Context) *rangefeed.RangeFeed { + if r.started() { + // Nothing to do, return + return nil + } + updateCacheFn := func( + ctx context.Context, keyVal *roachpb.RangeFeedValue, + ) { + instanceID, addr, sessionID, timestamp, tombstone, err := r.rowcodec.decodeRow(kv.KeyValue{ + Key: keyVal.Key, + Value: &keyVal.Value, + }) + if err != nil { + log.Ops.Warningf(ctx, "failed to decode settings row %v: %v", keyVal.Key, err) + return + } + instance := instancerow{ + instanceID: instanceID, + addr: addr, + sessionID: sessionID, + timestamp: timestamp, + } + r.updateInstanceMap(instance, tombstone) + } + initialScanDoneFn := func(_ context.Context) { + close(r.initialScanDone) + } + initialScanErrFn := func(_ context.Context, err error) (shouldFail bool) { + if grpcutil.IsAuthError(err) || + // This is a hack around the fact that we do not get properly structured + // errors out of gRPC. See #56208. + strings.Contains(err.Error(), "rpc error: code = Unauthenticated") { + shouldFail = true + r.setStartError(err) + close(r.initialScanDone) + } + return shouldFail + } + + instancesTablePrefix := r.codec.TablePrefix(uint32(r.tableID)) + instancesTableSpan := roachpb.Span{ + Key: instancesTablePrefix, + EndKey: instancesTablePrefix.PrefixEnd(), + } + rf, err := r.f.RangeFeed(ctx, + "sql_instances", + instancesTableSpan, + r.clock.Now(), + updateCacheFn, + rangefeed.WithInitialScan(initialScanDoneFn), + rangefeed.WithOnInitialScanError(initialScanErrFn), + ) + r.setStarted() + if err != nil { + r.setStartError(err) + close(r.initialScanDone) + return nil } + return rf } // GetInstance implements sqlinstance.AddressResolver interface. func (r *Reader) GetInstance( ctx context.Context, instanceID base.SQLInstanceID, -) (instanceInfo sqlinstance.InstanceInfo, _ error) { - instanceData, err := r.storage.getInstanceData(ctx, instanceID) - if err != nil { +) (sqlinstance.InstanceInfo, error) { + if err := r.checkStarted(); err != nil { return sqlinstance.InstanceInfo{}, err } - sessionAlive, err := r.slReader.IsAlive(ctx, instanceData.sessionID) + r.mu.Lock() + instance, ok := r.mu.instances[instanceID] + r.mu.Unlock() + if !ok { + return sqlinstance.InstanceInfo{}, sqlinstance.NonExistentInstanceError + } + alive, err := r.slReader.IsAlive(ctx, instance.sessionID) if err != nil { return sqlinstance.InstanceInfo{}, err } - if !sessionAlive { + if !alive { return sqlinstance.InstanceInfo{}, sqlinstance.NonExistentInstanceError } - instanceInfo = sqlinstance.InstanceInfo{ - InstanceID: instanceData.instanceID, - InstanceAddr: instanceData.addr, - SessionID: instanceData.sessionID, + instanceInfo := sqlinstance.InstanceInfo{ + InstanceID: instance.instanceID, + InstanceAddr: instance.addr, + SessionID: instance.sessionID, } return instanceInfo, nil } // GetAllInstances implements sqlinstance.AddressResolver interface. +// This method does not block as the underlying sqlliveness.Reader +// being used (outside of test environment) is a cached reader which +// does not perform any RPCs in its `isAlive()` calls. func (r *Reader) GetAllInstances( ctx context.Context, -) (instances []sqlinstance.InstanceInfo, _ error) { - instanceRows, err := r.storage.getAllInstancesData(ctx) - if err != nil { +) (sqlInstances []sqlinstance.InstanceInfo, _ error) { + if err := r.checkStarted(); err != nil { return nil, err } - instanceRows, err = r.filterInactiveInstances(ctx, instanceRows) + liveInstances, err := r.getAllLiveInstances(ctx) if err != nil { return nil, err } - for _, instanceRow := range instanceRows { + for _, liveInstance := range liveInstances { instanceInfo := sqlinstance.InstanceInfo{ - InstanceID: instanceRow.instanceID, - InstanceAddr: instanceRow.addr, - SessionID: instanceRow.sessionID, + InstanceID: liveInstance.instanceID, + InstanceAddr: liveInstance.addr, + SessionID: liveInstance.sessionID, } - instances = append(instances, instanceInfo) + sqlInstances = append(sqlInstances, instanceInfo) } - return instances, nil + return sqlInstances, nil } -func (r *Reader) filterInactiveInstances( - ctx context.Context, rows []instancerow, -) ([]instancerow, error) { +func (r *Reader) getAllLiveInstances(ctx context.Context) ([]instancerow, error) { + rows := r.getAllInstanceRows() // Filter inactive instances. { truncated := rows[:0] @@ -115,3 +258,51 @@ func (r *Reader) filterInactiveInstances( } return rows, nil } + +// getAllInstanceRows returns all instancerow objects contained +// within the map, in an arbitrary order. +func (r *Reader) getAllInstanceRows() (instances []instancerow) { + r.mu.Lock() + defer r.mu.Unlock() + for _, instance := range r.mu.instances { + instances = append(instances, instance) + } + return instances +} + +func (r *Reader) updateInstanceMap(instance instancerow, deletionEvent bool) { + r.mu.Lock() + defer r.mu.Unlock() + if deletionEvent { + delete(r.mu.instances, instance.instanceID) + return + } + r.mu.instances[instance.instanceID] = instance +} + +func (r *Reader) setStarted() { + r.mu.Lock() + defer r.mu.Unlock() + r.mu.started = true +} + +func (r *Reader) started() bool { + r.mu.Lock() + defer r.mu.Unlock() + return r.mu.started +} + +func (r *Reader) checkStarted() error { + r.mu.Lock() + defer r.mu.Unlock() + if !r.mu.started { + return sqlinstance.NotStartedError + } + return r.mu.startError +} + +func (r *Reader) setStartError(err error) { + r.mu.Lock() + defer r.mu.Unlock() + r.mu.startError = err +} diff --git a/pkg/sql/sqlinstance/instancestorage/instancereader_test.go b/pkg/sql/sqlinstance/instancestorage/instancereader_test.go index 1150868d6ec8..f74beb7b28a8 100644 --- a/pkg/sql/sqlinstance/instancestorage/instancereader_test.go +++ b/pkg/sql/sqlinstance/instancestorage/instancereader_test.go @@ -12,25 +12,25 @@ package instancestorage_test import ( "context" - "sort" "strings" "testing" "time" "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed" "github.com/cockroachdb/cockroach/pkg/sql/catalog/systemschema" "github.com/cockroachdb/cockroach/pkg/sql/sqlinstance" "github.com/cockroachdb/cockroach/pkg/sql/sqlinstance/instancestorage" "github.com/cockroachdb/cockroach/pkg/sql/sqlliveness" "github.com/cockroachdb/cockroach/pkg/sql/sqlliveness/slstorage" - "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/stop" - "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" "github.com/stretchr/testify/require" ) @@ -40,11 +40,14 @@ func TestReader(t *testing.T) { defer log.Scope(t).Close(t) ctx := context.Background() - s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) - defer s.Stopper().Stop(ctx) - tDB := sqlutils.MakeSQLRunner(sqlDB) + tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{}) + defer tc.Stopper().Stop(ctx) + s := tc.Server(0) + tDB := sqlutils.MakeSQLRunner(tc.ServerConn(0)) + // Enable rangefeed for the test. + tDB.Exec(t, `SET CLUSTER SETTING kv.rangefeed.enabled = true`) setup := func(t *testing.T) ( - *stop.Stopper, *instancestorage.Storage, *slstorage.FakeStorage, *hlc.Clock, *instancestorage.Reader, + *instancestorage.Storage, *slstorage.FakeStorage, *hlc.Clock, *instancestorage.Reader, ) { dbName := t.Name() tDB.Exec(t, `CREATE DATABASE "`+dbName+`"`) @@ -53,22 +56,26 @@ func TestReader(t *testing.T) { `CREATE TABLE "`+dbName+`".sql_instances`, 1) tDB.Exec(t, schema) tableID := getTableID(t, tDB, dbName, "sql_instances") - clock := hlc.NewClock(func() int64 { - return timeutil.NewTestTimeSource().Now().UnixNano() - }, base.DefaultMaxClockOffset) - stopper := stop.NewStopper() slStorage := slstorage.NewFakeStorage() - storage := instancestorage.NewTestingStorage(kvDB, keys.SystemSQLCodec, tableID, slStorage) - reader := instancestorage.NewReader(storage, slStorage) - return stopper, storage, slStorage, clock, reader + storage := instancestorage.NewTestingStorage(s.DB(), keys.SystemSQLCodec, tableID, slStorage) + reader := instancestorage.NewTestingReader(storage, slStorage, s.RangeFeedFactory().(*rangefeed.Factory), keys.SystemSQLCodec, tableID, s.Clock(), s.Stopper()) + return storage, slStorage, s.Clock(), reader } + t.Run("unstarted-reader", func(t *testing.T) { + _, _, _, reader := setup(t) + _, err := reader.GetInstance(ctx, 1) + require.ErrorIs(t, err, sqlinstance.NotStartedError) + }) + t.Run("basic-get-instance-data", func(t *testing.T) { - stopper, storage, slStorage, clock, reader := setup(t) - defer stopper.Stop(ctx) + storage, slStorage, clock, reader := setup(t) + require.NoError(t, reader.Start(ctx)) const sessionID = sqlliveness.SessionID("session_id") const addr = "addr" - const expiration = time.Minute + // Set a high enough expiration to ensure the session stays + // live through the test. + const expiration = 10 * time.Minute { sessionExpiry := clock.Now().Add(expiration.Nanoseconds(), 0) id, err := storage.CreateInstance(ctx, sessionID, sessionExpiry, addr) @@ -79,19 +86,47 @@ func TestReader(t *testing.T) { if err != nil { t.Fatal(err) } - instanceInfo, err := reader.GetInstance(ctx, id) - require.NoError(t, err) - require.Equal(t, addr, instanceInfo.InstanceAddr) + testutils.SucceedsSoon(t, func() error { + instanceInfo, err := reader.GetInstance(ctx, id) + if err != nil { + return err + } + if addr != instanceInfo.InstanceAddr { + return errors.Newf("expected instance address %s != actual instance address %s", addr, instanceInfo.InstanceAddr) + } + return nil + }) } }) t.Run("release-instance-get-all-instances", func(t *testing.T) { - const expiration = time.Minute - stopper, storage, slStorage, clock, reader := setup(t) - defer stopper.Stop(ctx) - // Create three instances and release one. - instanceIDs := [...]base.SQLInstanceID{1, 2, 3} - addresses := [...]string{"addr1", "addr2", "addr3"} - sessionIDs := [...]sqlliveness.SessionID{"session1", "session2", "session3"} + // Set a high enough expiration to ensure the session stays + // live through the test. + const expiration = 10 * time.Minute + storage, slStorage, clock, reader := setup(t) + require.NoError(t, reader.Start(ctx)) + + // Set up expected test data. + instanceIDs := []base.SQLInstanceID{1, 2, 3} + addresses := []string{"addr1", "addr2", "addr3"} + sessionIDs := []sqlliveness.SessionID{"session1", "session2", "session3"} + + testOutputFn := func(expectedIDs []base.SQLInstanceID, expectedAddresses []string, expectedSessionIDs []sqlliveness.SessionID, actualInstances []sqlinstance.InstanceInfo) error { + if len(expectedIDs) != len(actualInstances) { + return errors.Newf("expected %d instances, got %d instances", len(expectedIDs), len(actualInstances)) + } + for index, instance := range actualInstances { + if expectedIDs[index] != instance.InstanceID { + return errors.Newf("expected instance ID %d != actual instance ID %d", expectedIDs[index], instance.InstanceID) + } + if expectedAddresses[index] != instance.InstanceAddr { + return errors.Newf("expected instance address %s != actual instance address %s", expectedAddresses[index], instance.InstanceAddr) + } + if expectedSessionIDs[index] != instance.SessionID { + return errors.Newf("expected session ID %s != actual session ID %s", expectedSessionIDs[index], instance.SessionID) + } + } + return nil + } { // Set up mock data within instance and session storage. for index, addr := range addresses { @@ -105,21 +140,15 @@ func TestReader(t *testing.T) { t.Fatal(err) } } - } - - // Verify all instances are returned by GetAllInstances. - { - instances, err := reader.GetAllInstances(ctx) - sort.SliceStable(instances, func(idx1, idx2 int) bool { - return instances[idx1].InstanceID < instances[idx2].InstanceID + // Verify all instances are returned by GetAllInstances. + testutils.SucceedsSoon(t, func() error { + instances, err := reader.GetAllInstances(ctx) + if err != nil { + return err + } + sortInstances(instances) + return testOutputFn(instanceIDs, addresses, sessionIDs, instances) }) - require.NoError(t, err) - require.Equal(t, len(instanceIDs), len(instances)) - for index, instance := range instances { - require.Equal(t, instanceIDs[index], instance.InstanceID) - require.Equal(t, sessionIDs[index], instance.SessionID) - require.Equal(t, addresses[index], instance.InstanceAddr) - } } // Release an instance and verify only active instances are returned. @@ -128,15 +157,14 @@ func TestReader(t *testing.T) { if err != nil { t.Fatal(err) } - instances, err := reader.GetAllInstances(ctx) - require.NoError(t, err) - require.Equal(t, 2, len(instances)) - sortInstances(instances) - for index, instance := range instances { - require.Equal(t, instanceIDs[index+1], instance.InstanceID) - require.Equal(t, sessionIDs[index+1], instance.SessionID) - require.Equal(t, addresses[index+1], instance.InstanceAddr) - } + testutils.SucceedsSoon(t, func() error { + instances, err := reader.GetAllInstances(ctx) + if err != nil { + return err + } + sortInstances(instances) + return testOutputFn(instanceIDs[1:], addresses[1:], sessionIDs[1:], instances) + }) } // Verify instances with expired sessions are filtered out. @@ -145,23 +173,20 @@ func TestReader(t *testing.T) { if err != nil { t.Fatal(err) } - var instances []sqlinstance.InstanceInfo - instances, err = reader.GetAllInstances(ctx) - sortInstances(instances) - require.NoError(t, err) - // One instance ID has been released and one is associated with an expired session. - // So only one active instance exists at this point. - require.Equal(t, 1, len(instances)) - require.Equal(t, instanceIDs[2], instances[0].InstanceID) - require.Equal(t, sessionIDs[2], instances[0].SessionID) - require.Equal(t, addresses[2], instances[0].InstanceAddr) + testutils.SucceedsSoon(t, func() error { + instances, err := reader.GetAllInstances(ctx) + if err != nil { + return err + } + sortInstances(instances) + return testOutputFn(instanceIDs[2:], addresses[2:], sessionIDs[2:], instances) + }) } // When multiple instances have the same address, verify that only // the latest instance information is returned. This heuristic is used // when instance information isn't released correctly prior to SQL instance shutdown. { - var instances []sqlinstance.InstanceInfo sessionID := sqlliveness.SessionID("session4") sessionExpiry := clock.Now().Add(expiration.Nanoseconds(), 0) id, err := storage.CreateInstance(ctx, sessionID, sessionExpiry, addresses[2]) @@ -172,19 +197,22 @@ func TestReader(t *testing.T) { if err != nil { t.Fatal(err) } - instances, err = reader.GetAllInstances(ctx) - require.NoError(t, err) - // Verify returned instance information is for the latest instance. - require.Equal(t, 1, len(instances)) - require.Equal(t, id, instances[0].InstanceID) - require.Equal(t, sessionID, instances[0].SessionID) - require.Equal(t, addresses[2], instances[0].InstanceAddr) + testutils.SucceedsSoon(t, func() error { + instances, err := reader.GetAllInstances(ctx) + if err != nil { + return err + } + sortInstances(instances) + return testOutputFn([]base.SQLInstanceID{id}, []string{addresses[2]}, []sqlliveness.SessionID{sessionID}, instances) + }) } }) t.Run("release-instance-get-instance", func(t *testing.T) { - const expiration = time.Minute - stopper, storage, slStorage, clock, reader := setup(t) - defer stopper.Stop(ctx) + // Set a high enough expiration to ensure the session stays + // live through the test. + const expiration = 10 * time.Minute + storage, slStorage, clock, reader := setup(t) + require.NoError(t, reader.Start(ctx)) // Create three instances and release one. instanceIDs := [...]base.SQLInstanceID{1, 2, 3} addresses := [...]string{"addr1", "addr2", "addr3"} @@ -206,9 +234,16 @@ func TestReader(t *testing.T) { // Verify active instance data is returned with no error. { - instanceInfo, err := reader.GetInstance(ctx, instanceIDs[0]) - require.NoError(t, err) - require.Equal(t, addresses[0], instanceInfo.InstanceAddr) + testutils.SucceedsSoon(t, func() error { + instanceInfo, err := reader.GetInstance(ctx, instanceIDs[0]) + if err != nil { + return err + } + if addresses[0] != instanceInfo.InstanceAddr { + return errors.Newf("expected instance address %s != actual instance address %s", addresses[0], instanceInfo.InstanceAddr) + } + return nil + }) } // Verify request for released instance data results in an error. @@ -217,9 +252,13 @@ func TestReader(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = reader.GetInstance(ctx, instanceIDs[0]) - require.Error(t, err) - require.ErrorIs(t, err, sqlinstance.NonExistentInstanceError) + testutils.SucceedsSoon(t, func() error { + _, err = reader.GetInstance(ctx, instanceIDs[0]) + if !errors.Is(err, sqlinstance.NonExistentInstanceError) { + return errors.Newf("expected non existent instance error") + } + return nil + }) } // Verify request for instance with expired session results in an error. { @@ -227,9 +266,13 @@ func TestReader(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = reader.GetInstance(ctx, instanceIDs[1]) - require.Error(t, err) - require.ErrorIs(t, err, sqlinstance.NonExistentInstanceError) + testutils.SucceedsSoon(t, func() error { + _, err = reader.GetInstance(ctx, instanceIDs[0]) + if !errors.Is(err, sqlinstance.NonExistentInstanceError) { + return errors.Newf("expected non existent instance error") + } + return nil + }) } }) } diff --git a/pkg/sql/sqlinstance/instancestorage/instancestorage.go b/pkg/sql/sqlinstance/instancestorage/instancestorage.go index 0844b2841136..b89838af4cda 100644 --- a/pkg/sql/sqlinstance/instancestorage/instancestorage.go +++ b/pkg/sql/sqlinstance/instancestorage/instancestorage.go @@ -162,7 +162,7 @@ func (s *Storage) getInstanceData( if row.Value == nil { return instancerow{}, sqlinstance.NonExistentInstanceError } - _, addr, sessionID, timestamp, err := s.rowcodec.decodeRow(row) + _, addr, sessionID, timestamp, _, err := s.rowcodec.decodeRow(row) if err != nil { return instancerow{}, errors.Wrapf(err, "could not decode data for instance %d", instanceID) } @@ -204,7 +204,7 @@ func (s *Storage) getAllInstanceRows( return nil, err } for i := range rows { - instanceID, addr, sessionID, timestamp, err := s.rowcodec.decodeRow(rows[i]) + instanceID, addr, sessionID, timestamp, _, err := s.rowcodec.decodeRow(rows[i]) if err != nil { log.Warningf(ctx, "failed to decode row %v: %v", rows[i].Key, err) return nil, err diff --git a/pkg/sql/sqlinstance/instancestorage/instancestorage_test.go b/pkg/sql/sqlinstance/instancestorage/instancestorage_test.go index 19e975935b9b..fbe15db864ec 100644 --- a/pkg/sql/sqlinstance/instancestorage/instancestorage_test.go +++ b/pkg/sql/sqlinstance/instancestorage/instancestorage_test.go @@ -106,9 +106,7 @@ func TestStorage(t *testing.T) { // Verify all instances are returned by GetAllInstancesDataForTest. { instances, err := storage.GetAllInstancesDataForTest(ctx) - sort.SliceStable(instances, func(idx1, idx2 int) bool { - return instances[idx1].InstanceID < instances[idx2].InstanceID - }) + sortInstances(instances) require.NoError(t, err) require.Equal(t, len(instanceIDs), len(instances)) for index, instance := range instances { diff --git a/pkg/sql/sqlinstance/instancestorage/row_codec.go b/pkg/sql/sqlinstance/instancestorage/row_codec.go index 2572dc396e5f..bec03699f019 100644 --- a/pkg/sql/sqlinstance/instancestorage/row_codec.go +++ b/pkg/sql/sqlinstance/instancestorage/row_codec.go @@ -82,6 +82,7 @@ func (d *rowCodec) decodeRow( addr string, sessionID sqlliveness.SessionID, timestamp hlc.Timestamp, + tombstone bool, _ error, ) { tbl := systemschema.SQLInstancesTable @@ -92,21 +93,23 @@ func (d *rowCodec) decodeRow( row := make([]rowenc.EncDatum, 1) _, _, _, err := rowenc.DecodeIndexKey(d.codec, tbl, tbl.GetPrimaryIndex(), types, row, nil, kv.Key) if err != nil { - return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, errors.Wrap(err, "failed to decode key") + return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, false, errors.Wrap(err, "failed to decode key") } if err := row[0].EnsureDecoded(types[0], &alloc); err != nil { - return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, err + return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, false, err } instanceID = base.SQLInstanceID(tree.MustBeDInt(row[0].Datum)) } - + if !kv.Value.IsPresent() { + return instanceID, "", "", hlc.Timestamp{}, true, nil + } // The rest of the columns are stored as a family, packed with diff-encoded // column IDs followed by their values. { bytes, err := kv.Value.GetTuple() timestamp = kv.Value.Timestamp if err != nil { - return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, err + return instanceID, "", "", hlc.Timestamp{}, false, err } var colIDDiff uint32 var lastColID descpb.ColumnID @@ -114,14 +117,14 @@ func (d *rowCodec) decodeRow( for len(bytes) > 0 { _, _, colIDDiff, _, err = encoding.DecodeValueTag(bytes) if err != nil { - return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, err + return instanceID, "", "", hlc.Timestamp{}, false, err } colID := lastColID + descpb.ColumnID(colIDDiff) lastColID = colID if idx, ok := d.colIdxMap.Get(colID); ok { res, bytes, err = rowenc.DecodeTableValue(&alloc, tbl.PublicColumns()[idx].GetType(), bytes) if err != nil { - return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, err + return instanceID, "", "", hlc.Timestamp{}, false, err } switch colID { case tbl.PublicColumns()[1].GetID(): // addr @@ -133,12 +136,12 @@ func (d *rowCodec) decodeRow( sessionID = sqlliveness.SessionID(tree.MustBeDBytes(res)) } default: - return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, errors.Errorf("unknown column: %v", colID) + return instanceID, "", "", hlc.Timestamp{}, false, errors.Errorf("unknown column: %v", colID) } } } } - return instanceID, addr, sessionID, timestamp, nil + return instanceID, addr, sessionID, timestamp, false, nil } func makeTablePrefix(codec keys.SQLCodec, tableID descpb.ID) roachpb.Key { diff --git a/pkg/sql/sqlinstance/sqlinstance.go b/pkg/sql/sqlinstance/sqlinstance.go index d481d3b0aa19..11247e2718dc 100644 --- a/pkg/sql/sqlinstance/sqlinstance.go +++ b/pkg/sql/sqlinstance/sqlinstance.go @@ -44,8 +44,16 @@ type AddressResolver interface { // Provider is a wrapper around sqlinstance subsystem for external consumption. type Provider interface { AddressResolver + // Instance returns the instance ID and sqlliveness.SessionID for the + // current SQL instance. Instance(context.Context) (base.SQLInstanceID, sqlliveness.SessionID, error) + // Start starts the instanceprovider. This will block until + // the underlying instance data reader has been started. + Start(context.Context) error } // NonExistentInstanceError can be returned if a SQL instance does not exist. var NonExistentInstanceError = errors.Errorf("non existent SQL instance") + +// NotStartedError can be returned if the sqlinstance subsystem has not been started yet. +var NotStartedError = errors.Errorf("sqlinstance subsystem not started") From 00f125f7e3badd1120c46e53502ffbe093024cb3 Mon Sep 17 00:00:00 2001 From: Nick Travers Date: Wed, 27 Oct 2021 10:11:23 -0700 Subject: [PATCH 071/205] cmd/roachtest: skip clearrange/zfs/* tests The clearrange tests occasionally fail when run with a ZFS filesystem. Given that wider ZFS support is minimal, disable these tests until time can be devoted to investigating the cause of the failures. Release note: None. --- pkg/cmd/roachtest/tests/clearrange.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cmd/roachtest/tests/clearrange.go b/pkg/cmd/roachtest/tests/clearrange.go index 1d9e184c3e68..948ccb0af47b 100644 --- a/pkg/cmd/roachtest/tests/clearrange.go +++ b/pkg/cmd/roachtest/tests/clearrange.go @@ -44,6 +44,7 @@ func registerClearRange(r registry.Registry) { // and may need to be tweaked. r.Add(registry.TestSpec{ Name: fmt.Sprintf(`clearrange/zfs/checks=%t`, checks), + Skip: "Consistently failing. See #70306 and #68420 for context.", Owner: registry.OwnerStorage, // 5h for import, 120 for the test. The import should take closer // to <3:30h but it varies. From cdc206ec764ac6bf1f18e6c6d8cef585a22e8a87 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Wed, 27 Oct 2021 16:28:00 -0400 Subject: [PATCH 072/205] sql: fix indkey column in pg_catalog.pg_index for expression indexes Fixes #72041 Release note (bug fix): A bug has been fixed that incorrectly populated the `indkey` column of `pg_catalog.pg_index` for expression indexes. This bug was present since the introduction of expression indexes in version 21.2.0. --- pkg/sql/logictest/testdata/logic_test/pg_catalog | 15 ++++++++++++++- pkg/sql/pg_catalog.go | 8 +++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index 051d7535e765..b1ba416e62de 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -1033,7 +1033,20 @@ WHERE indnkeyatts = 2 indexrelid indrelid indislive indisreplident indkey indcollation indclass indoption indexprs indpred 450499961 55 true false 3 4 0 0 0 0 2 2 NULL NULL 3660126516 59 true false 1 2 3 0 0 0 0 2 1 NULL NULL -2574785777 63 true false 6 7 3403232968 0 0 0 2 2 NULL NULL +2574785777 63 true false 0 0 3403232968 0 0 0 2 2 NULL NULL + +# indkey should be 0 for expression elements an index. +# TODO: In Postgres indexprs are values that represent the expression. Matching +# this representation may be difficult and unnecessary. +query TTT colnames +SELECT c.relname, i.indkey, i.indexprs +FROM pg_catalog.pg_index i +JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid +WHERE c.relname IN ('t6_expr_idx', 't6_expr_expr1_idx') +---- +relname indkey indexprs +t6_expr_idx 0 NULL +t6_expr_expr1_idx 0 0 NULL statement ok SET DATABASE = system diff --git a/pkg/sql/pg_catalog.go b/pkg/sql/pg_catalog.go index 0905467abd34..e3e947ec62d0 100644 --- a/pkg/sql/pg_catalog.go +++ b/pkg/sql/pg_catalog.go @@ -1698,11 +1698,17 @@ https://www.postgresql.org/docs/9.5/catalog-pg-index.html`, colIDs := make([]descpb.ColumnID, 0, index.NumKeyColumns()) for i := index.IndexDesc().ExplicitColumnStartIdx(); i < index.NumKeyColumns(); i++ { columnID := index.GetKeyColumnID(i) - colIDs = append(colIDs, columnID) col, err := table.FindColumnWithID(columnID) if err != nil { return err } + // The indkey for an expression element in an index + // should be 0. + if col.IsExpressionIndexColumn() { + colIDs = append(colIDs, 0) + } else { + colIDs = append(colIDs, columnID) + } if err := collationOids.Append(typColl(col.GetType(), h)); err != nil { return err } From a7160c3e6d514480f0740482bf4a4138d1a545bc Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Thu, 7 Oct 2021 14:51:09 -0700 Subject: [PATCH 073/205] coldata: introduce TypedVecs helper This commit extracts the helper struct out of the ordered synchronizer that stores the well-typed columns alongside the untyped Vecs. The idea is that every Vec is stored both in Vecs slice as well as in the typed slice, in order. Components that know the type of the vector they are working with can then access the typed column directly, avoiding expensive type casts. The newly introduced struct is used by the ordered synchronizer and the follow-up commits will introduce it elsewhere (for example, in the cFetcher). Release note: None --- pkg/col/coldata/vec.eg.go | 140 +++++++++++++++++ pkg/col/coldata/vec_tmpl.go | 69 +++++++++ .../cmd/execgen/ordered_synchronizer_gen.go | 2 - .../execgen/cmd/execgen/overloads_base.go | 8 + .../colexec/execgen/cmd/execgen/vec_gen.go | 2 + pkg/sql/colexec/ordered_synchronizer.eg.go | 142 ++---------------- pkg/sql/colexec/ordered_synchronizer_tmpl.go | 54 +------ 7 files changed, 237 insertions(+), 180 deletions(-) diff --git a/pkg/col/coldata/vec.eg.go b/pkg/col/coldata/vec.eg.go index 204d7842af15..9accc3f4b346 100644 --- a/pkg/col/coldata/vec.eg.go +++ b/pkg/col/coldata/vec.eg.go @@ -15,9 +15,11 @@ import ( "github.com/cockroachdb/apd/v2" "github.com/cockroachdb/cockroach/pkg/col/typeconv" + "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/util/duration" "github.com/cockroachdb/cockroach/pkg/util/json" + "github.com/cockroachdb/errors" ) // Workaround for bazel auto-generated code. goimports does not automatically @@ -27,8 +29,146 @@ var ( _ apd.Context _ duration.Duration _ json.JSON + _ = colexecerror.InternalError + _ = errors.AssertionFailedf ) +// TypedVecs represents a slice of Vecs that have been converted into the typed +// columns. The idea is that every Vec is stored both in Vecs slice as well as +// in the typed slice, in order. Components that know the type of the vector +// they are working with can then access the typed column directly, avoiding +// expensive type casts. +type TypedVecs struct { + Vecs []Vec + Nulls []*Nulls + + // Fields below need to be accessed by an index mapped via ColsMap. + BoolCols []Bools + BytesCols []*Bytes + DecimalCols []Decimals + Int16Cols []Int16s + Int32Cols []Int32s + Int64Cols []Int64s + Float64Cols []Float64s + TimestampCols []Times + IntervalCols []Durations + JSONCols []*JSONs + DatumCols []DatumVec + // ColsMap contains the positions of the corresponding vectors in the slice + // for the same types. For example, if we have a batch with + // types = [Int64, Int64, Bool, Bytes, Bool, Int64], + // then ColsMap will be + // [0, 1, 0, 0, 1, 2] + // ^ ^ ^ ^ ^ ^ + // | | | | | | + // | | | | | 3rd among all Int64's + // | | | | 2nd among all Bool's + // | | | 1st among all Bytes's + // | | 1st among all Bool's + // | 2nd among all Int64's + // 1st among all Int64's + ColsMap []int +} + +// SetBatch updates TypedVecs to represent all vectors from batch. +func (v *TypedVecs) SetBatch(batch Batch) { + v.Vecs = batch.ColVecs() + if cap(v.Nulls) < len(v.Vecs) { + v.Nulls = make([]*Nulls, len(v.Vecs)) + v.ColsMap = make([]int, len(v.Vecs)) + } else { + v.Nulls = v.Nulls[:len(v.Vecs)] + v.ColsMap = v.ColsMap[:len(v.Vecs)] + } + v.BoolCols = v.BoolCols[:0] + v.BytesCols = v.BytesCols[:0] + v.DecimalCols = v.DecimalCols[:0] + v.Int16Cols = v.Int16Cols[:0] + v.Int32Cols = v.Int32Cols[:0] + v.Int64Cols = v.Int64Cols[:0] + v.Float64Cols = v.Float64Cols[:0] + v.TimestampCols = v.TimestampCols[:0] + v.IntervalCols = v.IntervalCols[:0] + v.JSONCols = v.JSONCols[:0] + v.DatumCols = v.DatumCols[:0] + for i, vec := range v.Vecs { + v.Nulls[i] = vec.Nulls() + switch vec.CanonicalTypeFamily() { + case types.BoolFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.BoolCols) + v.BoolCols = append(v.BoolCols, vec.Bool()) + } + case types.BytesFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.BytesCols) + v.BytesCols = append(v.BytesCols, vec.Bytes()) + } + case types.DecimalFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.DecimalCols) + v.DecimalCols = append(v.DecimalCols, vec.Decimal()) + } + case types.IntFamily: + switch vec.Type().Width() { + case 16: + v.ColsMap[i] = len(v.Int16Cols) + v.Int16Cols = append(v.Int16Cols, vec.Int16()) + case 32: + v.ColsMap[i] = len(v.Int32Cols) + v.Int32Cols = append(v.Int32Cols, vec.Int32()) + case -1: + default: + v.ColsMap[i] = len(v.Int64Cols) + v.Int64Cols = append(v.Int64Cols, vec.Int64()) + } + case types.FloatFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.Float64Cols) + v.Float64Cols = append(v.Float64Cols, vec.Float64()) + } + case types.TimestampTZFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.TimestampCols) + v.TimestampCols = append(v.TimestampCols, vec.Timestamp()) + } + case types.IntervalFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.IntervalCols) + v.IntervalCols = append(v.IntervalCols, vec.Interval()) + } + case types.JsonFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.JSONCols) + v.JSONCols = append(v.JSONCols, vec.JSON()) + } + case typeconv.DatumVecCanonicalTypeFamily: + switch vec.Type().Width() { + case -1: + default: + v.ColsMap[i] = len(v.DatumCols) + v.DatumCols = append(v.DatumCols, vec.Datum()) + } + default: + colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", vec.Type())) + } + } +} + func (m *memColumn) Append(args SliceArgs) { switch m.CanonicalTypeFamily() { case types.BoolFamily: diff --git a/pkg/col/coldata/vec_tmpl.go b/pkg/col/coldata/vec_tmpl.go index d397f7a5c1ca..68d073fc3fdd 100644 --- a/pkg/col/coldata/vec_tmpl.go +++ b/pkg/col/coldata/vec_tmpl.go @@ -27,9 +27,11 @@ import ( "github.com/cockroachdb/apd/v2" "github.com/cockroachdb/cockroach/pkg/col/typeconv" "github.com/cockroachdb/cockroach/pkg/sql/colexec/execgen" + "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/util/duration" "github.com/cockroachdb/cockroach/pkg/util/json" + "github.com/cockroachdb/errors" ) // Workaround for bazel auto-generated code. goimports does not automatically @@ -39,6 +41,8 @@ var ( _ apd.Context _ duration.Duration _ json.JSON + _ = colexecerror.InternalError + _ = errors.AssertionFailedf ) // {{/* @@ -54,6 +58,71 @@ const _TYPE_WIDTH = 0 // */}} +// TypedVecs represents a slice of Vecs that have been converted into the typed +// columns. The idea is that every Vec is stored both in Vecs slice as well as +// in the typed slice, in order. Components that know the type of the vector +// they are working with can then access the typed column directly, avoiding +// expensive type casts. +type TypedVecs struct { + Vecs []Vec + Nulls []*Nulls + + // Fields below need to be accessed by an index mapped via ColsMap. + // {{range .}} + // {{range .WidthOverloads}} + _TYPECols []_GOTYPESLICE + // {{end}} + // {{end}} + // ColsMap contains the positions of the corresponding vectors in the slice + // for the same types. For example, if we have a batch with + // types = [Int64, Int64, Bool, Bytes, Bool, Int64], + // then ColsMap will be + // [0, 1, 0, 0, 1, 2] + // ^ ^ ^ ^ ^ ^ + // | | | | | | + // | | | | | 3rd among all Int64's + // | | | | 2nd among all Bool's + // | | | 1st among all Bytes's + // | | 1st among all Bool's + // | 2nd among all Int64's + // 1st among all Int64's + ColsMap []int +} + +// SetBatch updates TypedVecs to represent all vectors from batch. +func (v *TypedVecs) SetBatch(batch Batch) { + v.Vecs = batch.ColVecs() + if cap(v.Nulls) < len(v.Vecs) { + v.Nulls = make([]*Nulls, len(v.Vecs)) + v.ColsMap = make([]int, len(v.Vecs)) + } else { + v.Nulls = v.Nulls[:len(v.Vecs)] + v.ColsMap = v.ColsMap[:len(v.Vecs)] + } + // {{range .}} + // {{range .WidthOverloads}} + v._TYPECols = v._TYPECols[:0] + // {{end}} + // {{end}} + for i, vec := range v.Vecs { + v.Nulls[i] = vec.Nulls() + switch vec.CanonicalTypeFamily() { + // {{range .}} + case _CANONICAL_TYPE_FAMILY: + switch vec.Type().Width() { + // {{range .WidthOverloads}} + case _TYPE_WIDTH: + v.ColsMap[i] = len(v._TYPECols) + v._TYPECols = append(v._TYPECols, vec.TemplateType()) + // {{end}} + } + // {{end}} + default: + colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", vec.Type())) + } + } +} + func (m *memColumn) Append(args SliceArgs) { switch m.CanonicalTypeFamily() { // {{range .}} diff --git a/pkg/sql/colexec/execgen/cmd/execgen/ordered_synchronizer_gen.go b/pkg/sql/colexec/execgen/cmd/execgen/ordered_synchronizer_gen.go index d1562cb0caf6..26f3157b754f 100644 --- a/pkg/sql/colexec/execgen/cmd/execgen/ordered_synchronizer_gen.go +++ b/pkg/sql/colexec/execgen/cmd/execgen/ordered_synchronizer_gen.go @@ -22,10 +22,8 @@ const ordSyncTmpl = "pkg/sql/colexec/ordered_synchronizer_tmpl.go" func genOrderedSynchronizer(inputFileContents string, wr io.Writer) error { r := strings.NewReplacer( - "_CANONICAL_TYPE_FAMILY", "{{.CanonicalTypeFamilyStr}}", "_TYPE_WIDTH", typeWidthReplacement, - "_GOTYPESLICE", "{{.GoTypeSliceName}}", "_TYPE", "{{.VecMethod}}", ) s := r.Replace(inputFileContents) diff --git a/pkg/sql/colexec/execgen/cmd/execgen/overloads_base.go b/pkg/sql/colexec/execgen/cmd/execgen/overloads_base.go index 37cd44c5fc18..500ab1c6da1f 100644 --- a/pkg/sql/colexec/execgen/cmd/execgen/overloads_base.go +++ b/pkg/sql/colexec/execgen/cmd/execgen/overloads_base.go @@ -449,6 +449,13 @@ func (b *argWidthOverloadBase) GoTypeSliceName() string { return goTypeSliceName(b.CanonicalTypeFamily, b.Width) } +// GoTypeSliceNameInColdata is the same as GoTypeSliceName, but it removes the +// "coldata." substring (and, thus, can be used by a template in coldata +// package). +func (b *argWidthOverloadBase) GoTypeSliceNameInColdata() string { + return strings.Replace(b.GoTypeSliceName(), "coldata.", "", -1) +} + func copyVal(canonicalTypeFamily types.Family, dest, src string) string { switch canonicalTypeFamily { case types.BytesFamily: @@ -595,6 +602,7 @@ var ( awob = &argWidthOverloadBase{} _ = awob.GoTypeSliceName + _ = awob.GoTypeSliceNameInColdata _ = awob.CopyVal _ = awob.Sliceable _ = awob.AppendSlice diff --git a/pkg/sql/colexec/execgen/cmd/execgen/vec_gen.go b/pkg/sql/colexec/execgen/cmd/execgen/vec_gen.go index 5d9c614e5f5e..854f7c4b88f8 100644 --- a/pkg/sql/colexec/execgen/cmd/execgen/vec_gen.go +++ b/pkg/sql/colexec/execgen/cmd/execgen/vec_gen.go @@ -23,7 +23,9 @@ const vecTmpl = "pkg/col/coldata/vec_tmpl.go" func genVec(inputFileContents string, wr io.Writer) error { r := strings.NewReplacer("_CANONICAL_TYPE_FAMILY", "{{.CanonicalTypeFamilyStr}}", "_TYPE_WIDTH", typeWidthReplacement, + "_GOTYPESLICE", "{{.GoTypeSliceNameInColdata}}", "_GOTYPE", "{{.GoType}}", + "_TYPE", "{{.VecMethod}}", "TemplateType", "{{.VecMethod}}", ) s := r.Replace(inputFileContents) diff --git a/pkg/sql/colexec/ordered_synchronizer.eg.go b/pkg/sql/colexec/ordered_synchronizer.eg.go index 65135b7bd097..75d0af71579f 100644 --- a/pkg/sql/colexec/ordered_synchronizer.eg.go +++ b/pkg/sql/colexec/ordered_synchronizer.eg.go @@ -58,33 +58,7 @@ type OrderedSynchronizer struct { // batch has exceeded the memory limit. maxCapacity int output coldata.Batch - outNulls []*coldata.Nulls - // In order to reduce the number of interface conversions, we will get access - // to the underlying slice for the output vectors and will use them directly. - outBoolCols []coldata.Bools - outBytesCols []*coldata.Bytes - outDecimalCols []coldata.Decimals - outInt16Cols []coldata.Int16s - outInt32Cols []coldata.Int32s - outInt64Cols []coldata.Int64s - outFloat64Cols []coldata.Float64s - outTimestampCols []coldata.Times - outIntervalCols []coldata.Durations - outJSONCols []*coldata.JSONs - outDatumCols []coldata.DatumVec - // outColsMap contains the positions of the corresponding vectors in the - // slice for the same types. For example, if we have an output batch with - // types = [Int64, Int64, Bool, Bytes, Bool, Int64], then outColsMap will be - // [0, 1, 0, 0, 1, 2] - // ^ ^ ^ ^ ^ ^ - // | | | | | | - // | | | | | 3rd among all Int64's - // | | | | 2nd among all Bool's - // | | | 1st among all Bytes's - // | | 1st among all Bool's - // | 2nd among all Int64's - // 1st among all Int64's - outColsMap []int + outVecs coldata.TypedVecs } var ( @@ -154,7 +128,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { for i := range o.typs { vec := batch.ColVec(i) if vec.Nulls().MaybeHasNulls() && vec.Nulls().NullAt(srcRowIdx) { - o.outNulls[i].SetNull(outputIdx) + o.outVecs.Nulls[i].SetNull(outputIdx) } else { switch o.canonicalTypeFamilies[i] { case types.BoolFamily: @@ -162,7 +136,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Bool() - outCol := o.outBoolCols[o.outColsMap[i]] + outCol := o.outVecs.BoolCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -171,7 +145,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Bytes() - outCol := o.outBytesCols[o.outColsMap[i]] + outCol := o.outVecs.BytesCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -180,7 +154,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Decimal() - outCol := o.outDecimalCols[o.outColsMap[i]] + outCol := o.outVecs.DecimalCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -188,18 +162,18 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { switch o.typs[i].Width() { case 16: srcCol := vec.Int16() - outCol := o.outInt16Cols[o.outColsMap[i]] + outCol := o.outVecs.Int16Cols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) case 32: srcCol := vec.Int32() - outCol := o.outInt32Cols[o.outColsMap[i]] + outCol := o.outVecs.Int32Cols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) case -1: default: srcCol := vec.Int64() - outCol := o.outInt64Cols[o.outColsMap[i]] + outCol := o.outVecs.Int64Cols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -208,7 +182,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Float64() - outCol := o.outFloat64Cols[o.outColsMap[i]] + outCol := o.outVecs.Float64Cols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -217,7 +191,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Timestamp() - outCol := o.outTimestampCols[o.outColsMap[i]] + outCol := o.outVecs.TimestampCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -226,7 +200,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Interval() - outCol := o.outIntervalCols[o.outColsMap[i]] + outCol := o.outVecs.IntervalCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -235,7 +209,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.JSON() - outCol := o.outJSONCols[o.outColsMap[i]] + outCol := o.outVecs.JSONCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -244,7 +218,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { case -1: default: srcCol := vec.Datum() - outCol := o.outDatumCols[o.outColsMap[i]] + outCol := o.outVecs.DatumCols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) } @@ -286,93 +260,7 @@ func (o *OrderedSynchronizer) resetOutput() { o.typs, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) if reallocated { - o.outBoolCols = o.outBoolCols[:0] - o.outBytesCols = o.outBytesCols[:0] - o.outDecimalCols = o.outDecimalCols[:0] - o.outInt16Cols = o.outInt16Cols[:0] - o.outInt32Cols = o.outInt32Cols[:0] - o.outInt64Cols = o.outInt64Cols[:0] - o.outFloat64Cols = o.outFloat64Cols[:0] - o.outTimestampCols = o.outTimestampCols[:0] - o.outIntervalCols = o.outIntervalCols[:0] - o.outJSONCols = o.outJSONCols[:0] - o.outDatumCols = o.outDatumCols[:0] - for i, outVec := range o.output.ColVecs() { - o.outNulls[i] = outVec.Nulls() - switch typeconv.TypeFamilyToCanonicalTypeFamily(o.typs[i].Family()) { - case types.BoolFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outBoolCols) - o.outBoolCols = append(o.outBoolCols, outVec.Bool()) - } - case types.BytesFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outBytesCols) - o.outBytesCols = append(o.outBytesCols, outVec.Bytes()) - } - case types.DecimalFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outDecimalCols) - o.outDecimalCols = append(o.outDecimalCols, outVec.Decimal()) - } - case types.IntFamily: - switch o.typs[i].Width() { - case 16: - o.outColsMap[i] = len(o.outInt16Cols) - o.outInt16Cols = append(o.outInt16Cols, outVec.Int16()) - case 32: - o.outColsMap[i] = len(o.outInt32Cols) - o.outInt32Cols = append(o.outInt32Cols, outVec.Int32()) - case -1: - default: - o.outColsMap[i] = len(o.outInt64Cols) - o.outInt64Cols = append(o.outInt64Cols, outVec.Int64()) - } - case types.FloatFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outFloat64Cols) - o.outFloat64Cols = append(o.outFloat64Cols, outVec.Float64()) - } - case types.TimestampTZFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outTimestampCols) - o.outTimestampCols = append(o.outTimestampCols, outVec.Timestamp()) - } - case types.IntervalFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outIntervalCols) - o.outIntervalCols = append(o.outIntervalCols, outVec.Interval()) - } - case types.JsonFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outJSONCols) - o.outJSONCols = append(o.outJSONCols, outVec.JSON()) - } - case typeconv.DatumVecCanonicalTypeFamily: - switch o.typs[i].Width() { - case -1: - default: - o.outColsMap[i] = len(o.outDatumCols) - o.outDatumCols = append(o.outDatumCols, outVec.Datum()) - } - default: - colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", o.typs[i])) - } - } + o.outVecs.SetBatch(o.output) } } @@ -383,8 +271,6 @@ func (o *OrderedSynchronizer) Init(ctx context.Context) { } o.Ctx, o.span = execinfra.ProcessorSpan(o.Ctx, "ordered sync") o.inputIndices = make([]int, len(o.inputs)) - o.outNulls = make([]*coldata.Nulls, len(o.typs)) - o.outColsMap = make([]int, len(o.typs)) for i := range o.inputs { o.inputs[i].Root.Init(o.Ctx) } diff --git a/pkg/sql/colexec/ordered_synchronizer_tmpl.go b/pkg/sql/colexec/ordered_synchronizer_tmpl.go index 8922ebc926f8..d360aa87f204 100644 --- a/pkg/sql/colexec/ordered_synchronizer_tmpl.go +++ b/pkg/sql/colexec/ordered_synchronizer_tmpl.go @@ -43,9 +43,6 @@ import ( // {{/* // Declarations to make the template compile properly. -// _GOTYPESLICE is the template variable. -type _GOTYPESLICE interface{} - // _CANONICAL_TYPE_FAMILY is the template variable. const _CANONICAL_TYPE_FAMILY = types.UnknownFamily @@ -84,27 +81,7 @@ type OrderedSynchronizer struct { // batch has exceeded the memory limit. maxCapacity int output coldata.Batch - outNulls []*coldata.Nulls - // In order to reduce the number of interface conversions, we will get access - // to the underlying slice for the output vectors and will use them directly. - // {{range .}} - // {{range .WidthOverloads}} - out_TYPECols []_GOTYPESLICE - // {{end}} - // {{end}} - // outColsMap contains the positions of the corresponding vectors in the - // slice for the same types. For example, if we have an output batch with - // types = [Int64, Int64, Bool, Bytes, Bool, Int64], then outColsMap will be - // [0, 1, 0, 0, 1, 2] - // ^ ^ ^ ^ ^ ^ - // | | | | | | - // | | | | | 3rd among all Int64's - // | | | | 2nd among all Bool's - // | | | 1st among all Bytes's - // | | 1st among all Bool's - // | 2nd among all Int64's - // 1st among all Int64's - outColsMap []int + outVecs coldata.TypedVecs } var ( @@ -174,7 +151,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { for i := range o.typs { vec := batch.ColVec(i) if vec.Nulls().MaybeHasNulls() && vec.Nulls().NullAt(srcRowIdx) { - o.outNulls[i].SetNull(outputIdx) + o.outVecs.Nulls[i].SetNull(outputIdx) } else { switch o.canonicalTypeFamilies[i] { // {{range .}} @@ -183,7 +160,7 @@ func (o *OrderedSynchronizer) Next() coldata.Batch { // {{range .WidthOverloads}} case _TYPE_WIDTH: srcCol := vec._TYPE() - outCol := o.out_TYPECols[o.outColsMap[i]] + outCol := o.outVecs._TYPECols[o.outVecs.ColsMap[i]] v := srcCol.Get(srcRowIdx) outCol.Set(outputIdx, v) // {{end}} @@ -227,28 +204,7 @@ func (o *OrderedSynchronizer) resetOutput() { o.typs, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) if reallocated { - // {{range .}} - // {{range .WidthOverloads}} - o.out_TYPECols = o.out_TYPECols[:0] - // {{end}} - // {{end}} - for i, outVec := range o.output.ColVecs() { - o.outNulls[i] = outVec.Nulls() - switch typeconv.TypeFamilyToCanonicalTypeFamily(o.typs[i].Family()) { - // {{range .}} - case _CANONICAL_TYPE_FAMILY: - switch o.typs[i].Width() { - // {{range .WidthOverloads}} - case _TYPE_WIDTH: - o.outColsMap[i] = len(o.out_TYPECols) - o.out_TYPECols = append(o.out_TYPECols, outVec._TYPE()) - // {{end}} - } - // {{end}} - default: - colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", o.typs[i])) - } - } + o.outVecs.SetBatch(o.output) } } @@ -259,8 +215,6 @@ func (o *OrderedSynchronizer) Init(ctx context.Context) { } o.Ctx, o.span = execinfra.ProcessorSpan(o.Ctx, "ordered sync") o.inputIndices = make([]int, len(o.inputs)) - o.outNulls = make([]*coldata.Nulls, len(o.typs)) - o.outColsMap = make([]int, len(o.typs)) for i := range o.inputs { o.inputs[i].Root.Init(o.Ctx) } From 97139f02207e7945ca35a7ef17895d72a1deb792 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Thu, 7 Oct 2021 15:29:27 -0700 Subject: [PATCH 074/205] cfetcher: operate on the typed columns rather than untyped Vecs This commit uses the `coldata.TypedVecs` helper (introduced in the previous commit) in the context of the cFetcher. In all decoding methods we are already performing a large type switch for each value for each row, so we always know exactly the type of the column we're writing into. Additionally, this commit inlines a single function call and removes an `if` conditional in the value decoding path. Release note: None --- pkg/col/coldata/vec.eg.go | 41 +++++++++ pkg/col/coldata/vec_tmpl.go | 15 +++ pkg/sql/colencoding/key_encoding.go | 102 ++++++++++++--------- pkg/sql/colencoding/value_encoding.go | 81 ++++++++-------- pkg/sql/colencoding/value_encoding_test.go | 8 +- pkg/sql/colfetcher/cfetcher.go | 43 ++++----- 6 files changed, 179 insertions(+), 111 deletions(-) diff --git a/pkg/col/coldata/vec.eg.go b/pkg/col/coldata/vec.eg.go index 9accc3f4b346..36b3b3be4655 100644 --- a/pkg/col/coldata/vec.eg.go +++ b/pkg/col/coldata/vec.eg.go @@ -169,6 +169,47 @@ func (v *TypedVecs) SetBatch(batch Batch) { } } +// Reset performs a deep reset of v while keeping the references to the slices. +func (v *TypedVecs) Reset() { + v.Vecs = nil + for i := range v.Nulls { + v.Nulls[i] = nil + } + for i := range v.BoolCols { + v.BoolCols[i] = nil + } + for i := range v.BytesCols { + v.BytesCols[i] = nil + } + for i := range v.DecimalCols { + v.DecimalCols[i] = nil + } + for i := range v.Int16Cols { + v.Int16Cols[i] = nil + } + for i := range v.Int32Cols { + v.Int32Cols[i] = nil + } + for i := range v.Int64Cols { + v.Int64Cols[i] = nil + } + for i := range v.Float64Cols { + v.Float64Cols[i] = nil + } + for i := range v.TimestampCols { + v.TimestampCols[i] = nil + } + for i := range v.IntervalCols { + v.IntervalCols[i] = nil + } + for i := range v.JSONCols { + v.JSONCols[i] = nil + } + for i := range v.DatumCols { + v.DatumCols[i] = nil + } +} + func (m *memColumn) Append(args SliceArgs) { switch m.CanonicalTypeFamily() { case types.BoolFamily: diff --git a/pkg/col/coldata/vec_tmpl.go b/pkg/col/coldata/vec_tmpl.go index 68d073fc3fdd..4a8cdff84052 100644 --- a/pkg/col/coldata/vec_tmpl.go +++ b/pkg/col/coldata/vec_tmpl.go @@ -123,6 +123,21 @@ func (v *TypedVecs) SetBatch(batch Batch) { } } +// Reset performs a deep reset of v while keeping the references to the slices. +func (v *TypedVecs) Reset() { + v.Vecs = nil + for i := range v.Nulls { + v.Nulls[i] = nil + } + // {{range .}} + // {{range .WidthOverloads}} + for i := range v._TYPECols { + v._TYPECols[i] = nil + } + // {{end}} + // {{end}} +} + func (m *memColumn) Append(args SliceArgs) { switch m.CanonicalTypeFamily() { // {{range .}} diff --git a/pkg/sql/colencoding/key_encoding.go b/pkg/sql/colencoding/key_encoding.go index e64674b1c805..85b8131861c3 100644 --- a/pkg/sql/colencoding/key_encoding.go +++ b/pkg/sql/colencoding/key_encoding.go @@ -28,7 +28,7 @@ import ( ) // DecodeKeyValsToCols decodes the values that are part of the key, writing the -// result to the idx'th slot of the input slice of coldata.Vecs. +// result to the rowIdx'th row in coldata.TypedVecs. // // If the unseen int set is non-nil, upon decoding the column with ordinal i, // i will be removed from the set to facilitate tracking whether or not columns @@ -44,8 +44,8 @@ import ( // should be decoded. func DecodeKeyValsToCols( da *rowenc.DatumAlloc, - vecs []coldata.Vec, - idx int, + vecs *coldata.TypedVecs, + rowIdx int, indexColIdx []int, checkAllColsForNull bool, types []*types.T, @@ -57,8 +57,8 @@ func DecodeKeyValsToCols( ) (remainingKey []byte, foundNull bool, retScratch []byte, _ error) { for j := range types { var err error - i := indexColIdx[j] - if i == -1 { + vecIdx := indexColIdx[j] + if vecIdx == -1 { if checkAllColsForNull { isNull := encoding.PeekType(key) == encoding.Null foundNull = foundNull || isNull @@ -67,11 +67,11 @@ func DecodeKeyValsToCols( key, err = rowenc.SkipTableKey(key) } else { if unseen != nil { - unseen.Remove(i) + unseen.Remove(vecIdx) } var isNull bool - isInverted := invertedColIdx == i - key, isNull, scratch, err = decodeTableKeyToCol(da, vecs[i], idx, types[j], key, directions[j], isInverted, scratch) + isInverted := invertedColIdx == vecIdx + key, isNull, scratch, err = decodeTableKeyToCol(da, vecs, vecIdx, rowIdx, types[j], key, directions[j], isInverted, scratch) foundNull = isNull || foundNull } if err != nil { @@ -81,14 +81,15 @@ func DecodeKeyValsToCols( return key, foundNull, scratch, nil } -// decodeTableKeyToCol decodes a value encoded by EncodeTableKey, writing the result -// to the idx'th slot of the input colexec.Vec. -// See the analog, DecodeTableKey, in sqlbase/column_type_encoding.go. +// decodeTableKeyToCol decodes a value encoded by EncodeTableKey, writing the +// result to the rowIdx'th slot of the vecIdx'th vector in coldata.TypedVecs. +// See the analog, rowenc.DecodeTableKey, in rowenc/column_type_encoding.go. // decodeTableKeyToCol also returns whether or not the decoded value was NULL. func decodeTableKeyToCol( da *rowenc.DatumAlloc, - vec coldata.Vec, - idx int, + vecs *coldata.TypedVecs, + vecIdx int, + rowIdx int, valType *types.T, key []byte, dir descpb.IndexDescriptor_Direction, @@ -100,10 +101,14 @@ func decodeTableKeyToCol( } var isNull bool if key, isNull = encoding.DecodeIfNull(key); isNull { - vec.Nulls().SetNull(idx) + vecs.Nulls[vecIdx].SetNull(rowIdx) return key, true, scratch, nil } + // Find the position of the target vector among the typed columns of its + // type. + colIdx := vecs.ColsMap[vecIdx] + // Inverted columns should not be decoded, but should instead be // passed on as a DBytes datum. if isInverted { @@ -111,7 +116,7 @@ func decodeTableKeyToCol( if err != nil { return nil, false, scratch, err } - vec.Bytes().Set(idx, key[:keyLen]) + vecs.BytesCols[colIdx].Set(rowIdx, key[:keyLen]) return key[keyLen:], false, scratch, nil } @@ -125,7 +130,7 @@ func decodeTableKeyToCol( } else { rkey, i, err = encoding.DecodeVarintDescending(key) } - vec.Bool()[idx] = i != 0 + vecs.BoolCols[colIdx][rowIdx] = i != 0 case types.IntFamily, types.DateFamily: var i int64 if dir == descpb.IndexDescriptor_ASC { @@ -135,11 +140,11 @@ func decodeTableKeyToCol( } switch valType.Width() { case 16: - vec.Int16()[idx] = int16(i) + vecs.Int16Cols[colIdx][rowIdx] = int16(i) case 32: - vec.Int32()[idx] = int32(i) + vecs.Int32Cols[colIdx][rowIdx] = int32(i) case 0, 64: - vec.Int64()[idx] = i + vecs.Int64Cols[colIdx][rowIdx] = i } case types.FloatFamily: var f float64 @@ -148,7 +153,7 @@ func decodeTableKeyToCol( } else { rkey, f, err = encoding.DecodeFloatDescending(key) } - vec.Float64()[idx] = f + vecs.Float64Cols[colIdx][rowIdx] = f case types.DecimalFamily: var d apd.Decimal if dir == descpb.IndexDescriptor_ASC { @@ -156,7 +161,7 @@ func decodeTableKeyToCol( } else { rkey, d, err = encoding.DecodeDecimalDescending(key, scratch[:0]) } - vec.Decimal()[idx] = d + vecs.DecimalCols[colIdx][rowIdx] = d case types.BytesFamily, types.StringFamily, types.UuidFamily: if dir == descpb.IndexDescriptor_ASC { // We ask for the deep copy to be made so that scratch doesn't @@ -171,7 +176,7 @@ func decodeTableKeyToCol( // Set() performs a deep copy, so it is safe to return the scratch slice // to the caller. Any modifications to the scratch slice made by the // caller will not affect the value in the vector. - vec.Bytes().Set(idx, scratch) + vecs.BytesCols[colIdx].Set(rowIdx, scratch) case types.TimestampFamily, types.TimestampTZFamily: var t time.Time if dir == descpb.IndexDescriptor_ASC { @@ -179,7 +184,7 @@ func decodeTableKeyToCol( } else { rkey, t, err = encoding.DecodeTimeDescending(key) } - vec.Timestamp()[idx] = t + vecs.TimestampCols[colIdx][rowIdx] = t case types.IntervalFamily: var d duration.Duration if dir == descpb.IndexDescriptor_ASC { @@ -187,13 +192,13 @@ func decodeTableKeyToCol( } else { rkey, d, err = encoding.DecodeDurationDescending(key) } - vec.Interval()[idx] = d + vecs.IntervalCols[colIdx][rowIdx] = d case types.JsonFamily: // Don't attempt to decode the JSON value. Instead, just return the // remaining bytes of the key. var jsonLen int jsonLen, err = encoding.PeekLength(key) - vec.JSON().Bytes.Set(idx, key[:jsonLen]) + vecs.JSONCols[colIdx].Bytes.Set(rowIdx, key[:jsonLen]) rkey = key[jsonLen:] default: var d tree.Datum @@ -202,68 +207,77 @@ func decodeTableKeyToCol( encDir = encoding.Descending } d, rkey, err = rowenc.DecodeTableKey(da, valType, key, encDir) - vec.Datum().Set(idx, d) + vecs.DatumCols[colIdx].Set(rowIdx, d) } return rkey, false, scratch, err } // UnmarshalColumnValueToCol decodes the value from a roachpb.Value using the -// type expected by the column, writing into the input Vec at the given row -// idx. An error is returned if the value's type does not match the column's -// type. -// See the analog, UnmarshalColumnValue, in sqlbase/column_type_encoding.go +// type expected by the column, writing into the vecIdx'th vector of +// coldata.TypedVecs at the given rowIdx. An error is returned if the value's +// type does not match the column's type. +// See the analog, rowenc.UnmarshalColumnValue, in +// rowenc/column_type_encoding.go. func UnmarshalColumnValueToCol( - da *rowenc.DatumAlloc, vec coldata.Vec, idx int, typ *types.T, value roachpb.Value, + da *rowenc.DatumAlloc, + vecs *coldata.TypedVecs, + vecIdx, rowIdx int, + typ *types.T, + value roachpb.Value, ) error { if value.RawBytes == nil { - vec.Nulls().SetNull(idx) + vecs.Nulls[vecIdx].SetNull(rowIdx) } + // Find the position of the target vector among the typed columns of its + // type. + colIdx := vecs.ColsMap[vecIdx] + var err error switch typ.Family() { case types.BoolFamily: var v bool v, err = value.GetBool() - vec.Bool()[idx] = v + vecs.BoolCols[colIdx][rowIdx] = v case types.IntFamily: var v int64 v, err = value.GetInt() switch typ.Width() { case 16: - vec.Int16()[idx] = int16(v) + vecs.Int16Cols[colIdx][rowIdx] = int16(v) case 32: - vec.Int32()[idx] = int32(v) + vecs.Int32Cols[colIdx][rowIdx] = int32(v) default: // Pre-2.1 BIT was using INT encoding with arbitrary sizes. // We map these to 64-bit INT now. See #34161. - vec.Int64()[idx] = v + vecs.Int64Cols[colIdx][rowIdx] = v } case types.FloatFamily: var v float64 v, err = value.GetFloat() - vec.Float64()[idx] = v + vecs.Float64Cols[colIdx][rowIdx] = v case types.DecimalFamily: - err = value.GetDecimalInto(&vec.Decimal()[idx]) + err = value.GetDecimalInto(&vecs.DecimalCols[colIdx][rowIdx]) case types.BytesFamily, types.StringFamily, types.UuidFamily: var v []byte v, err = value.GetBytes() - vec.Bytes().Set(idx, v) + vecs.BytesCols[colIdx].Set(rowIdx, v) case types.DateFamily: var v int64 v, err = value.GetInt() - vec.Int64()[idx] = v + vecs.Int64Cols[colIdx][rowIdx] = v case types.TimestampFamily, types.TimestampTZFamily: var v time.Time v, err = value.GetTime() - vec.Timestamp()[idx] = v + vecs.TimestampCols[colIdx][rowIdx] = v case types.IntervalFamily: var v duration.Duration v, err = value.GetDuration() - vec.Interval()[idx] = v + vecs.IntervalCols[colIdx][rowIdx] = v case types.JsonFamily: var v []byte v, err = value.GetBytes() - vec.JSON().Bytes.Set(idx, v) + vecs.JSONCols[colIdx].Bytes.Set(rowIdx, v) // Types backed by tree.Datums. default: var d tree.Datum @@ -271,7 +285,7 @@ func UnmarshalColumnValueToCol( if err != nil { return err } - vec.Datum().Set(idx, d) + vecs.DatumCols[colIdx].Set(rowIdx, d) } return err } diff --git a/pkg/sql/colencoding/value_encoding.go b/pkg/sql/colencoding/value_encoding.go index 6df8b00e6677..70e8179ebe88 100644 --- a/pkg/sql/colencoding/value_encoding.go +++ b/pkg/sql/colencoding/value_encoding.go @@ -23,81 +23,74 @@ import ( ) // DecodeTableValueToCol decodes a value encoded by EncodeTableValue, writing -// the result to the idx'th position of the input exec.Vec. -// See the analog in sqlbase/column_type_encoding.go. +// the result to the rowIdx'th position of the vecIdx'th vector in +// coldata.TypedVecs. +// See the analog in rowenc/column_type_encoding.go. func DecodeTableValueToCol( da *rowenc.DatumAlloc, - vec coldata.Vec, - idx int, + vecs *coldata.TypedVecs, + vecIdx int, + rowIdx int, typ encoding.Type, dataOffset int, valTyp *types.T, - b []byte, + buf []byte, ) ([]byte, error) { // NULL is special because it is a valid value for any type. if typ == encoding.Null { - vec.Nulls().SetNull(idx) - return b[dataOffset:], nil + vecs.Nulls[vecIdx].SetNull(rowIdx) + return buf[dataOffset:], nil } - // Bool is special because the value is stored in the value tag. - if valTyp.Family() != types.BoolFamily { - b = b[dataOffset:] - } - return decodeUntaggedDatumToCol(da, vec, idx, valTyp, b) -} -// decodeUntaggedDatum is used to decode a Datum whose type is known, -// and which doesn't have a value tag (either due to it having been -// consumed already or not having one in the first place). It writes the result -// to the idx'th position of the input coldata.Vec. -// -// This is used to decode datums encoded using value encoding. -// -// If t is types.Bool, the value tag must be present, as its value is encoded in -// the tag directly. -// See the analog in sqlbase/column_type_encoding.go. -func decodeUntaggedDatumToCol( - da *rowenc.DatumAlloc, vec coldata.Vec, idx int, t *types.T, buf []byte, -) ([]byte, error) { + // Bool is special because the value is stored in the value tag, so we have + // to keep the reference to the original slice. + origBuf := buf + buf = buf[dataOffset:] + + // Find the position of the target vector among the typed columns of its + // type. + colIdx := vecs.ColsMap[vecIdx] + var err error - switch t.Family() { + switch valTyp.Family() { case types.BoolFamily: var b bool // A boolean's value is encoded in its tag directly, so we don't have an - // "Untagged" version of this function. - buf, b, err = encoding.DecodeBoolValue(buf) - vec.Bool()[idx] = b + // "Untagged" version of this function. Note that we also use the + // original buffer. + buf, b, err = encoding.DecodeBoolValue(origBuf) + vecs.BoolCols[colIdx][rowIdx] = b case types.BytesFamily, types.StringFamily: var data []byte buf, data, err = encoding.DecodeUntaggedBytesValue(buf) - vec.Bytes().Set(idx, data) + vecs.BytesCols[colIdx].Set(rowIdx, data) case types.DateFamily: var i int64 buf, i, err = encoding.DecodeUntaggedIntValue(buf) - vec.Int64()[idx] = i + vecs.Int64Cols[colIdx][rowIdx] = i case types.DecimalFamily: - buf, err = encoding.DecodeIntoUntaggedDecimalValue(&vec.Decimal()[idx], buf) + buf, err = encoding.DecodeIntoUntaggedDecimalValue(&vecs.DecimalCols[colIdx][rowIdx], buf) case types.FloatFamily: var f float64 buf, f, err = encoding.DecodeUntaggedFloatValue(buf) - vec.Float64()[idx] = f + vecs.Float64Cols[colIdx][rowIdx] = f case types.IntFamily: var i int64 buf, i, err = encoding.DecodeUntaggedIntValue(buf) - switch t.Width() { + switch valTyp.Width() { case 16: - vec.Int16()[idx] = int16(i) + vecs.Int16Cols[colIdx][rowIdx] = int16(i) case 32: - vec.Int32()[idx] = int32(i) + vecs.Int32Cols[colIdx][rowIdx] = int32(i) default: // Pre-2.1 BIT was using INT encoding with arbitrary sizes. // We map these to 64-bit INT now. See #34161. - vec.Int64()[idx] = i + vecs.Int64Cols[colIdx][rowIdx] = i } case types.JsonFamily: var data []byte buf, data, err = encoding.DecodeUntaggedBytesValue(buf) - vec.JSON().Bytes.Set(idx, data) + vecs.JSONCols[colIdx].Bytes.Set(rowIdx, data) case types.UuidFamily: var data uuid.UUID buf, data, err = encoding.DecodeUntaggedUUIDValue(buf) @@ -106,23 +99,23 @@ func decodeUntaggedDatumToCol( if err != nil { return buf, err } - vec.Bytes().Set(idx, data.GetBytes()) + vecs.BytesCols[colIdx].Set(rowIdx, data.GetBytes()) case types.TimestampFamily, types.TimestampTZFamily: var t time.Time buf, t, err = encoding.DecodeUntaggedTimeValue(buf) - vec.Timestamp()[idx] = t + vecs.TimestampCols[colIdx][rowIdx] = t case types.IntervalFamily: var d duration.Duration buf, d, err = encoding.DecodeUntaggedDurationValue(buf) - vec.Interval()[idx] = d + vecs.IntervalCols[colIdx][rowIdx] = d // Types backed by tree.Datums. default: var d tree.Datum - d, buf, err = rowenc.DecodeUntaggedDatum(da, t, buf) + d, buf, err = rowenc.DecodeUntaggedDatum(da, valTyp, buf) if err != nil { return buf, err } - vec.Datum().Set(idx, d) + vecs.DatumCols[colIdx].Set(rowIdx, d) } return buf, err } diff --git a/pkg/sql/colencoding/value_encoding_test.go b/pkg/sql/colencoding/value_encoding_test.go index e673b1dcd443..32bab922463d 100644 --- a/pkg/sql/colencoding/value_encoding_test.go +++ b/pkg/sql/colencoding/value_encoding_test.go @@ -45,13 +45,17 @@ func TestDecodeTableValueToCol(t *testing.T) { } } batch := coldata.NewMemBatchWithCapacity(typs, 1 /* capacity */, coldataext.NewExtendedColumnFactory(nil /*evalCtx */)) + var vecs coldata.TypedVecs + vecs.SetBatch(batch) for i := 0; i < nCols; i++ { typeOffset, dataOffset, _, typ, err := encoding.DecodeValueTag(buf) if err != nil { t.Fatal(err) } - buf, err = DecodeTableValueToCol(&da, batch.ColVec(i), 0 /* rowIdx */, typ, - dataOffset, typs[i], buf[typeOffset:]) + buf, err = DecodeTableValueToCol( + &da, &vecs, i /* vecIdx */, 0 /* rowIdx */, typ, + dataOffset, typs[i], buf[typeOffset:], + ) if err != nil { t.Fatal(err) } diff --git a/pkg/sql/colfetcher/cfetcher.go b/pkg/sql/colfetcher/cfetcher.go index 6a6fb1553773..4daf8c2cf475 100644 --- a/pkg/sql/colfetcher/cfetcher.go +++ b/pkg/sql/colfetcher/cfetcher.go @@ -294,9 +294,9 @@ type cFetcher struct { // batch is the output batch the fetcher writes to. batch coldata.Batch - // colvecs is a slice of the ColVecs within batch, pulled out to avoid - // having to call batch.Vec too often in the tight loop. - colvecs []coldata.Vec + // colvecs are the vectors of batch that have been converted to the well + // typed columns to avoid expensive type casts on each row. + colvecs coldata.TypedVecs // timestampCol is the underlying ColVec for the timestamp output column, // or nil if the timestamp column was not requested. It is pulled out from @@ -354,13 +354,13 @@ func (rf *cFetcher) resetBatch() { rf.table.typs, rf.machine.batch, minDesiredCapacity, rf.memoryLimit, ) if reallocated { - rf.machine.colvecs = rf.machine.batch.ColVecs() + rf.machine.colvecs.SetBatch(rf.machine.batch) // Pull out any requested system column output vecs. if rf.table.timestampOutputIdx != noOutputColumn { - rf.machine.timestampCol = rf.machine.colvecs[rf.table.timestampOutputIdx].Decimal() + rf.machine.timestampCol = rf.machine.colvecs.DecimalCols[rf.machine.colvecs.ColsMap[rf.table.timestampOutputIdx]] } if rf.table.oidOutputIdx != noOutputColumn { - rf.machine.tableoidCol = rf.machine.colvecs[rf.table.oidOutputIdx].Datum() + rf.machine.tableoidCol = rf.machine.colvecs.DatumCols[rf.machine.colvecs.ColsMap[rf.table.oidOutputIdx]] } // Change the allocation size to be the same as the capacity of the // batch we allocated above. @@ -810,7 +810,7 @@ func (rf *cFetcher) NextBatch(ctx context.Context) (coldata.Batch, error) { checkAllColsForNull := rf.table.isSecondaryIndex && rf.table.index.IsUnique() && rf.table.desc.NumFamilies() != 1 key, foundNull, rf.scratch, err = colencoding.DecodeKeyValsToCols( &rf.table.da, - rf.machine.colvecs, + &rf.machine.colvecs, rf.machine.rowIdx, rf.table.indexColOrdinals, checkAllColsForNull, @@ -1030,7 +1030,7 @@ func (rf *cFetcher) pushState(state fetcherState) { // This function is meant for tracing and should not be used in hot paths. func (rf *cFetcher) getDatumAt(colIdx int, rowIdx int) tree.Datum { res := []tree.Datum{nil} - colconv.ColVecToDatumAndDeselect(res, rf.machine.colvecs[colIdx], 1 /* length */, []int{rowIdx}, &rf.table.da) + colconv.ColVecToDatumAndDeselect(res, rf.machine.colvecs.Vecs[colIdx], 1 /* length */, []int{rowIdx}, &rf.table.da) return res[0] } @@ -1121,7 +1121,7 @@ func (rf *cFetcher) processValue(ctx context.Context, familyID descpb.FamilyID) // column values from the value. valueBytes, _, rf.scratch, err = colencoding.DecodeKeyValsToCols( &table.da, - rf.machine.colvecs, + &rf.machine.colvecs, rf.machine.rowIdx, table.extraValColOrdinals, false, /* checkAllColsForNull */ @@ -1199,7 +1199,7 @@ func (rf *cFetcher) processValueSingle( } typ := rf.table.typs[idx] err := colencoding.UnmarshalColumnValueToCol( - &table.da, rf.machine.colvecs[idx], rf.machine.rowIdx, typ, val, + &table.da, &rf.machine.colvecs, idx, rf.machine.rowIdx, typ, val, ) if err != nil { return "", "", err @@ -1258,20 +1258,20 @@ func (rf *cFetcher) processValueBytes( } colID := lastColID + descpb.ColumnID(colIDDiff) lastColID = colID - idx := -1 + vecIdx := -1 // Find the ordinal into table.cols for the column ID we just decoded, // by advancing through the sorted list of needed value columns until // there's a match, or we passed the column ID we're looking for. for ; lastColIDIndex < len(table.orderedColIdxMap.vals); lastColIDIndex++ { nextID := table.orderedColIdxMap.vals[lastColIDIndex] if nextID == colID { - idx = table.orderedColIdxMap.ords[lastColIDIndex] + vecIdx = table.orderedColIdxMap.ords[lastColIDIndex] break } else if nextID > colID { break } } - if idx == -1 { + if vecIdx == -1 { // This column wasn't requested, so read its length and skip it. len, err := encoding.PeekValueLengthWithOffsetsAndType(valueBytes, dataOffset, typ) if err != nil { @@ -1285,21 +1285,19 @@ func (rf *cFetcher) processValueBytes( } if rf.traceKV { - prettyKey = fmt.Sprintf("%s/%s", prettyKey, table.desc.DeletableColumns()[idx].GetName()) + prettyKey = fmt.Sprintf("%s/%s", prettyKey, table.desc.DeletableColumns()[vecIdx].GetName()) } - vec := rf.machine.colvecs[idx] - - valTyp := rf.table.typs[idx] valueBytes, err = colencoding.DecodeTableValueToCol( - &table.da, vec, rf.machine.rowIdx, typ, dataOffset, valTyp, valueBytes, + &table.da, &rf.machine.colvecs, vecIdx, rf.machine.rowIdx, typ, + dataOffset, rf.table.typs[vecIdx], valueBytes, ) if err != nil { return "", "", err } - rf.machine.remainingValueColsByIdx.Remove(idx) + rf.machine.remainingValueColsByIdx.Remove(vecIdx) if rf.traceKV { - dVal := rf.getDatumAt(idx, rf.machine.rowIdx) + dVal := rf.getDatumAt(vecIdx, rf.machine.rowIdx) if _, err := fmt.Fprintf(rf.machine.prettyValueBuf, "/%v", dVal.String()); err != nil { return "", "", err } @@ -1336,7 +1334,7 @@ func (rf *cFetcher) fillNulls() error { table.desc.GetName(), table.cols[i].GetName(), table.index.GetName(), strings.Join(table.index.IndexDesc().KeyColumnNames, ","), strings.Join(indexColValues, ","))) } - rf.machine.colvecs[i].Nulls().SetNull(rf.machine.rowIdx) + rf.machine.colvecs.Nulls[i].SetNull(rf.machine.rowIdx) } return nil } @@ -1409,9 +1407,12 @@ func (rf *cFetcher) Release() { if rf.table != nil { rf.table.Release() } + colvecs := rf.machine.colvecs + colvecs.Reset() *rf = cFetcher{ scratch: rf.scratch[:0], } + rf.machine.colvecs = colvecs cFetcherPool.Put(rf) } From bce86488a3e22e65f76d202df96ed2448ee1b554 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Sat, 23 Oct 2021 11:46:31 -0700 Subject: [PATCH 075/205] sql: remove redundant TableReaderSpan This commit removes the redundant `TableReaderSpan` protobuf message in favor of using `roachpb.Span`s directly in the spec. This simplifies some things. One notable change is that we no longer need to hold on to `Spans` slice in the `TableReaderSpec` because we're using the passed-in spans slices directly (rather having to copy as we used to). Release note: None --- pkg/sql/colexec/colbuilder/BUILD.bazel | 1 + pkg/sql/colexec/colbuilder/execplan_test.go | 7 +- pkg/sql/colfetcher/colbatch_scan.go | 7 +- pkg/sql/colflow/BUILD.bazel | 1 + pkg/sql/colflow/colbatch_scan_test.go | 9 +- pkg/sql/distsql_physical_planner.go | 17 +- pkg/sql/distsql_plan_backfill.go | 5 +- pkg/sql/distsql_plan_scrub_physical.go | 5 +- pkg/sql/distsql_spec_exec_factory.go | 10 +- pkg/sql/execinfra/readerbase.go | 7 +- pkg/sql/execinfra/version.go | 8 +- pkg/sql/execinfrapb/flow_diagram.go | 2 +- pkg/sql/execinfrapb/processors_base.pb.go | 216 +------ pkg/sql/execinfrapb/processors_base.proto | 9 - pkg/sql/execinfrapb/processors_bulk_io.pb.go | 370 ++++++----- pkg/sql/execinfrapb/processors_bulk_io.proto | 3 +- pkg/sql/execinfrapb/processors_sql.pb.go | 604 +++++++++--------- pkg/sql/execinfrapb/processors_sql.proto | 7 +- pkg/sql/flowinfra/cluster_test.go | 10 +- pkg/sql/flowinfra/server_test.go | 3 +- pkg/sql/physicalplan/aggregator_funcs_test.go | 7 +- pkg/sql/physicalplan/specs.go | 4 +- pkg/sql/rowexec/backfiller.go | 10 +- pkg/sql/rowexec/indexbackfiller.go | 6 +- pkg/sql/rowexec/scrub_tablereader.go | 6 +- pkg/sql/rowexec/tablereader.go | 11 +- pkg/sql/rowexec/tablereader_test.go | 28 +- 27 files changed, 591 insertions(+), 782 deletions(-) diff --git a/pkg/sql/colexec/colbuilder/BUILD.bazel b/pkg/sql/colexec/colbuilder/BUILD.bazel index 0abfe1155dd2..e1d00c2d4f2f 100644 --- a/pkg/sql/colexec/colbuilder/BUILD.bazel +++ b/pkg/sql/colexec/colbuilder/BUILD.bazel @@ -50,6 +50,7 @@ go_test( "//pkg/base", "//pkg/keys", "//pkg/kv", + "//pkg/roachpb:with-mocks", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", diff --git a/pkg/sql/colexec/colbuilder/execplan_test.go b/pkg/sql/colexec/colbuilder/execplan_test.go index 8644fbafff23..152282b9b950 100644 --- a/pkg/sql/colexec/colbuilder/execplan_test.go +++ b/pkg/sql/colexec/colbuilder/execplan_test.go @@ -17,6 +17,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/colexec" @@ -84,15 +85,15 @@ func TestNewColOperatorExpectedTypeSchema(t *testing.T) { desc := catalogkv.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") tr := execinfrapb.TableReaderSpec{ Table: *desc.TableDesc(), - Spans: make([]execinfrapb.TableReaderSpan, 1), + Spans: make([]roachpb.Span, 1), NeededColumns: []uint32{0}, } var err error - tr.Spans[0].Span.Key, err = randgen.TestingMakePrimaryIndexKey(desc, 0) + tr.Spans[0].Key, err = randgen.TestingMakePrimaryIndexKey(desc, 0) if err != nil { t.Fatal(err) } - tr.Spans[0].Span.EndKey, err = randgen.TestingMakePrimaryIndexKey(desc, numRows+1) + tr.Spans[0].EndKey, err = randgen.TestingMakePrimaryIndexKey(desc, numRows+1) if err != nil { t.Fatal(err) } diff --git a/pkg/sql/colfetcher/colbatch_scan.go b/pkg/sql/colfetcher/colbatch_scan.go index c78717cbe003..83a06555b8e8 100644 --- a/pkg/sql/colfetcher/colbatch_scan.go +++ b/pkg/sql/colfetcher/colbatch_scan.go @@ -256,12 +256,7 @@ func NewColBatchScan( } s := colBatchScanPool.Get().(*ColBatchScan) - s.Spans = s.Spans[:0] - specSpans := spec.Spans - for i := range specSpans { - //gcassert:bce - s.Spans = append(s.Spans, specSpans[i].Span) - } + s.Spans = spec.Spans if !flowCtx.Local { // Make a copy of the spans so that we could get the misplanned ranges // info. diff --git a/pkg/sql/colflow/BUILD.bazel b/pkg/sql/colflow/BUILD.bazel index fb6d3d00cda1..2f37f1090746 100644 --- a/pkg/sql/colflow/BUILD.bazel +++ b/pkg/sql/colflow/BUILD.bazel @@ -78,6 +78,7 @@ go_test( "//pkg/col/coldatatestutils", "//pkg/keys", "//pkg/kv", + "//pkg/roachpb:with-mocks", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", diff --git a/pkg/sql/colflow/colbatch_scan_test.go b/pkg/sql/colflow/colbatch_scan_test.go index 7ca27a8899df..3de04fa6186a 100644 --- a/pkg/sql/colflow/colbatch_scan_test.go +++ b/pkg/sql/colflow/colbatch_scan_test.go @@ -22,6 +22,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colbuilder" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" @@ -71,8 +72,8 @@ func TestColBatchScanMeta(t *testing.T) { spec := execinfrapb.ProcessorSpec{ Core: execinfrapb.ProcessorCoreUnion{ TableReader: &execinfrapb.TableReaderSpec{ - Spans: []execinfrapb.TableReaderSpan{ - {Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}, + Spans: []roachpb.Span{ + td.PrimaryIndexSpan(keys.SystemSQLCodec), }, NeededColumns: []uint32{0}, Table: *td.TableDesc(), @@ -128,8 +129,8 @@ func BenchmarkColBatchScan(b *testing.B) { Core: execinfrapb.ProcessorCoreUnion{ TableReader: &execinfrapb.TableReaderSpec{ Table: *tableDesc.TableDesc(), - Spans: []execinfrapb.TableReaderSpan{ - {Span: tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}, + Spans: []roachpb.Span{ + tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec), }, NeededColumns: []uint32{0, 1}, }}, diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index ec1d51d0548b..ea2db24a1bff 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -1105,10 +1105,8 @@ func initTableReaderSpec( Visibility: n.colCfg.visibility, LockingStrength: n.lockingStrength, LockingWaitPolicy: n.lockingWaitPolicy, - // Retain the capacity of the spans slice. - Spans: s.Spans[:0], - HasSystemColumns: n.containsSystemColumns, - NeededColumns: n.colCfg.wantedColumnsOrdinals, + HasSystemColumns: n.containsSystemColumns, + NeededColumns: n.colCfg.wantedColumnsOrdinals, } if vc := getInvertedColumn(n.colCfg.invertedColumn, n.cols); vc != nil { s.InvertedColumn = vc.ColumnDesc() @@ -1503,15 +1501,12 @@ func (dsp *DistSQLPlanner) planTableReaders( } else { // For the rest, we have to copy the spec into a fresh spec. tr = physicalplan.NewTableReaderSpec() - // Grab the Spans field of the new spec, and reuse it in case the pooled - // TableReaderSpec we got has pre-allocated Spans memory. - newSpansSlice := tr.Spans *tr = *info.spec - tr.Spans = newSpansSlice - } - for j := range sp.Spans { - tr.Spans = append(tr.Spans, execinfrapb.TableReaderSpan{Span: sp.Spans[j]}) } + // TODO(yuzefovich): figure out how we could reuse the Spans slice if we + // kept the reference to it in TableReaderSpec (rather than allocating + // new slices in generateScanSpans and PartitionSpans). + tr.Spans = sp.Spans tr.Parallelize = info.parallelize if !tr.Parallelize { diff --git a/pkg/sql/distsql_plan_backfill.go b/pkg/sql/distsql_plan_backfill.go index 9a6ff943af0e..f01a03dd8b8f 100644 --- a/pkg/sql/distsql_plan_backfill.go +++ b/pkg/sql/distsql_plan_backfill.go @@ -65,10 +65,7 @@ func (dsp *DistSQLPlanner) createBackfillerPhysicalPlan( for i, sp := range spanPartitions { ib := &execinfrapb.BackfillerSpec{} *ib = spec - ib.Spans = make([]execinfrapb.TableReaderSpan, len(sp.Spans)) - for j := range sp.Spans { - ib.Spans[j].Span = sp.Spans[j] - } + ib.Spans = sp.Spans proc := physicalplan.Processor{ Node: sp.Node, diff --git a/pkg/sql/distsql_plan_scrub_physical.go b/pkg/sql/distsql_plan_scrub_physical.go index 76ca2c13263d..0fc2ef81d61f 100644 --- a/pkg/sql/distsql_plan_scrub_physical.go +++ b/pkg/sql/distsql_plan_scrub_physical.go @@ -38,10 +38,7 @@ func (dsp *DistSQLPlanner) createScrubPhysicalCheck( for i, sp := range spanPartitions { tr := &execinfrapb.TableReaderSpec{} *tr = *spec - tr.Spans = make([]execinfrapb.TableReaderSpan, len(sp.Spans)) - for j := range sp.Spans { - tr.Spans[j].Span = sp.Spans[j] - } + tr.Spans = sp.Spans corePlacement[i].NodeID = sp.Node corePlacement[i].Core.TableReader = tr diff --git a/pkg/sql/distsql_spec_exec_factory.go b/pkg/sql/distsql_spec_exec_factory.go index 906d27de6a83..ac108250d52e 100644 --- a/pkg/sql/distsql_spec_exec_factory.go +++ b/pkg/sql/distsql_spec_exec_factory.go @@ -231,12 +231,10 @@ func (e *distSQLSpecExecFactory) ConstructScan( colsToTableOrdinalMap := toTableOrdinals(cols, tabDesc, colCfg.visibility) trSpec := physicalplan.NewTableReaderSpec() *trSpec = execinfrapb.TableReaderSpec{ - Table: *tabDesc.TableDesc(), - Reverse: params.Reverse, - IsCheck: false, - Visibility: colCfg.visibility, - // Retain the capacity of the spans slice. - Spans: trSpec.Spans[:0], + Table: *tabDesc.TableDesc(), + Reverse: params.Reverse, + IsCheck: false, + Visibility: colCfg.visibility, HasSystemColumns: scanContainsSystemColumns(&colCfg), NeededColumns: colCfg.wantedColumnsOrdinals, } diff --git a/pkg/sql/execinfra/readerbase.go b/pkg/sql/execinfra/readerbase.go index 7ffbd1efa8c5..12a010c014eb 100644 --- a/pkg/sql/execinfra/readerbase.go +++ b/pkg/sql/execinfra/readerbase.go @@ -98,6 +98,8 @@ func MisplannedRanges( // SpansWithCopy tracks a set of spans (which can be modified) along with the // copy of the original one if needed. +// NB: Spans field is **not** owned by SpansWithCopy (it comes from the +// TableReader spec). type SpansWithCopy struct { Spans roachpb.Spans SpansCopy roachpb.Spans @@ -116,12 +118,9 @@ func (s *SpansWithCopy) MakeSpansCopy() { // Reset deeply resets s. func (s *SpansWithCopy) Reset() { - for i := range s.Spans { - s.Spans[i] = roachpb.Span{} - } for i := range s.SpansCopy { s.SpansCopy[i] = roachpb.Span{} } - s.Spans = s.Spans[:0] + s.Spans = nil s.SpansCopy = s.SpansCopy[:0] } diff --git a/pkg/sql/execinfra/version.go b/pkg/sql/execinfra/version.go index 3355c91a324d..141ecd2da4a1 100644 --- a/pkg/sql/execinfra/version.go +++ b/pkg/sql/execinfra/version.go @@ -39,11 +39,11 @@ import "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" // // ATTENTION: When updating these fields, add a brief description of what // changed to the version history below. -const Version execinfrapb.DistSQLVersion = 50 +const Version execinfrapb.DistSQLVersion = 51 // MinAcceptedVersion is the oldest version that the server is compatible with. // A server will not accept flows with older versions. -const MinAcceptedVersion execinfrapb.DistSQLVersion = 48 +const MinAcceptedVersion execinfrapb.DistSQLVersion = 51 /* @@ -51,6 +51,10 @@ const MinAcceptedVersion execinfrapb.DistSQLVersion = 48 Please add new entries at the top. +- Version: 51 (MinAcceptedVersion: 51) + - Redundant TableReaderSpan message has been removed in favor of using + roachpb.Spans directly. + - Version: 50 (MinAcceptedVersion: 48) - A new field, MinSampleSize, was added to both SamplerSpec and SamplerAggregatorSpec to support dynamically shrinking sample sets. diff --git a/pkg/sql/execinfrapb/flow_diagram.go b/pkg/sql/execinfrapb/flow_diagram.go index 5900e114a3ec..27bc7974cea8 100644 --- a/pkg/sql/execinfrapb/flow_diagram.go +++ b/pkg/sql/execinfrapb/flow_diagram.go @@ -153,7 +153,7 @@ func (tr *TableReaderSpec) summary() (string, []string) { var spanStr strings.Builder spanStr.WriteString("Spans: ") - spanStr.WriteString(catalogkeys.PrettySpan(valDirs, tr.Spans[0].Span, 2)) + spanStr.WriteString(catalogkeys.PrettySpan(valDirs, tr.Spans[0], 2)) if len(tr.Spans) > 1 { spanStr.WriteString(fmt.Sprintf(" and %d other", len(tr.Spans)-1)) diff --git a/pkg/sql/execinfrapb/processors_base.pb.go b/pkg/sql/execinfrapb/processors_base.pb.go index 42a025a730f5..062746d53d72 100644 --- a/pkg/sql/execinfrapb/processors_base.pb.go +++ b/pkg/sql/execinfrapb/processors_base.pb.go @@ -9,7 +9,6 @@ package execinfrapb import ( fmt "fmt" - roachpb "github.com/cockroachdb/cockroach/pkg/roachpb" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" @@ -127,46 +126,9 @@ func (m *Columns) XXX_DiscardUnknown() { var xxx_messageInfo_Columns proto.InternalMessageInfo -type TableReaderSpan struct { - // TODO(radu): the dist_sql APIs should be agnostic to how we map tables to - // KVs. The span should be described as starting and ending lists of values - // for a prefix of the index columns, along with inclusive/exclusive flags. - Span roachpb.Span `protobuf:"bytes,1,opt,name=span" json:"span"` -} - -func (m *TableReaderSpan) Reset() { *m = TableReaderSpan{} } -func (m *TableReaderSpan) String() string { return proto.CompactTextString(m) } -func (*TableReaderSpan) ProtoMessage() {} -func (*TableReaderSpan) Descriptor() ([]byte, []int) { - return fileDescriptor_de929fd066e9cd6d, []int{2} -} -func (m *TableReaderSpan) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *TableReaderSpan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil -} -func (m *TableReaderSpan) XXX_Merge(src proto.Message) { - xxx_messageInfo_TableReaderSpan.Merge(m, src) -} -func (m *TableReaderSpan) XXX_Size() int { - return m.Size() -} -func (m *TableReaderSpan) XXX_DiscardUnknown() { - xxx_messageInfo_TableReaderSpan.DiscardUnknown(m) -} - -var xxx_messageInfo_TableReaderSpan proto.InternalMessageInfo - func init() { proto.RegisterType((*PostProcessSpec)(nil), "cockroach.sql.distsqlrun.PostProcessSpec") proto.RegisterType((*Columns)(nil), "cockroach.sql.distsqlrun.Columns") - proto.RegisterType((*TableReaderSpan)(nil), "cockroach.sql.distsqlrun.TableReaderSpan") } func init() { @@ -174,33 +136,30 @@ func init() { } var fileDescriptor_de929fd066e9cd6d = []byte{ - // 411 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xc1, 0x6e, 0x13, 0x31, - 0x10, 0x86, 0xd7, 0xc9, 0xb6, 0x89, 0x1c, 0x4a, 0x2b, 0x03, 0xea, 0x2a, 0xaa, 0xcc, 0x2a, 0x2a, - 0x62, 0x39, 0xb0, 0x11, 0x3d, 0x72, 0x0c, 0x70, 0x41, 0x42, 0x54, 0x29, 0x27, 0x0e, 0xac, 0x1c, - 0xc7, 0x09, 0x86, 0xad, 0xc7, 0xf1, 0x38, 0x52, 0x5e, 0x02, 0x89, 0xc7, 0xca, 0xb1, 0xc7, 0x9e, - 0x10, 0x24, 0x2f, 0x82, 0x92, 0xdd, 0x8d, 0x36, 0x91, 0x38, 0x70, 0xb3, 0xbe, 0xf9, 0xff, 0x7f, - 0x3c, 0xa3, 0xa1, 0xcf, 0x70, 0x96, 0xf7, 0xd5, 0x42, 0x49, 0x6d, 0x26, 0x4e, 0xd8, 0x51, 0xdf, - 0x3a, 0x90, 0x0a, 0x11, 0x1c, 0x66, 0x23, 0x81, 0x2a, 0xb5, 0x0e, 0x3c, 0xb0, 0x48, 0x82, 0xfc, - 0xee, 0x40, 0xc8, 0xaf, 0x29, 0xce, 0xf2, 0x74, 0xac, 0xd1, 0xe3, 0x2c, 0x77, 0x73, 0xd3, 0xed, - 0x1e, 0x06, 0x8c, 0x85, 0x17, 0x85, 0xab, 0xcb, 0xb6, 0x8e, 0x7d, 0xf6, 0x78, 0x0a, 0x53, 0xd8, - 0x3e, 0xfb, 0x9b, 0x57, 0x41, 0x7b, 0x3f, 0x9a, 0xf4, 0xf4, 0x1a, 0xd0, 0x5f, 0x17, 0xdd, 0x6f, - 0xac, 0x92, 0xec, 0x92, 0x52, 0xeb, 0xe0, 0x9b, 0x92, 0x5e, 0x83, 0x89, 0x1a, 0x31, 0x49, 0xda, - 0x83, 0x70, 0xf9, 0xeb, 0x69, 0x30, 0xac, 0x71, 0xf6, 0x82, 0x3e, 0x84, 0xb9, 0xb7, 0x73, 0x9f, - 0x49, 0xc8, 0xe7, 0xb7, 0x06, 0xa3, 0x66, 0xdc, 0x4c, 0x4e, 0x06, 0x8d, 0x33, 0x32, 0x3c, 0x29, - 0x2a, 0x6f, 0x8a, 0x02, 0x7b, 0x4d, 0xcf, 0xc1, 0xe9, 0xa9, 0x36, 0x22, 0xcf, 0x0e, 0x3c, 0xad, - 0x9d, 0xe7, 0x49, 0x25, 0xf9, 0xb8, 0xe7, 0xfd, 0x40, 0x1f, 0x38, 0x65, 0xc6, 0xca, 0x65, 0x6a, - 0x61, 0x1d, 0x46, 0x61, 0xdc, 0x4c, 0x3a, 0x57, 0x97, 0xe9, 0xbf, 0xf6, 0x92, 0xbe, 0x5b, 0x58, - 0xa7, 0x10, 0x35, 0x98, 0xf2, 0xd3, 0x9d, 0xc2, 0xbf, 0xe1, 0xc8, 0xbe, 0xd0, 0x5d, 0x9f, 0x6c, - 0x2f, 0xb7, 0xfd, 0xdf, 0xb9, 0x8f, 0xaa, 0xa0, 0x61, 0x2d, 0xff, 0x82, 0x1e, 0xc3, 0x64, 0x82, - 0xca, 0x47, 0x47, 0x31, 0x49, 0xc2, 0x52, 0x5a, 0x32, 0xd6, 0xa5, 0x47, 0xb9, 0xbe, 0xd5, 0x3e, - 0x3a, 0xae, 0x15, 0x0b, 0xf4, 0x3e, 0x6c, 0x93, 0xb3, 0x46, 0xef, 0x39, 0x6d, 0x55, 0x93, 0x5f, - 0xd0, 0x56, 0xb5, 0x25, 0xb2, 0xdb, 0x52, 0x85, 0x7a, 0x6f, 0xe9, 0xe9, 0x27, 0x31, 0xca, 0xd5, - 0x50, 0x89, 0xb1, 0x72, 0x37, 0x56, 0x18, 0xf6, 0x8a, 0x86, 0x68, 0x85, 0x89, 0x48, 0x4c, 0x92, - 0xce, 0xd5, 0x79, 0x6d, 0x94, 0xf2, 0x1c, 0xd2, 0x8d, 0xac, 0xec, 0xba, 0x95, 0x0e, 0x5e, 0x2e, - 0xff, 0xf0, 0x60, 0xb9, 0xe2, 0xe4, 0x6e, 0xc5, 0xc9, 0xfd, 0x8a, 0x93, 0xdf, 0x2b, 0x4e, 0x7e, - 0xae, 0x79, 0x70, 0xb7, 0xe6, 0xc1, 0xfd, 0x9a, 0x07, 0x9f, 0x3b, 0xb5, 0x13, 0xfb, 0x1b, 0x00, - 0x00, 0xff, 0xff, 0x0d, 0xef, 0xdf, 0xb2, 0xb5, 0x02, 0x00, 0x00, + // 368 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcd, 0x6e, 0xda, 0x40, + 0x14, 0x85, 0x3d, 0xd8, 0xfc, 0x68, 0x28, 0x2d, 0x72, 0x5b, 0xd5, 0xb2, 0xd0, 0xd4, 0x42, 0x54, + 0x75, 0x17, 0x35, 0x52, 0x97, 0x5d, 0x52, 0x75, 0x13, 0x29, 0x0a, 0x22, 0xbb, 0x2c, 0x62, 0x99, + 0x61, 0x20, 0x93, 0x18, 0xdf, 0x61, 0x66, 0x2c, 0xf1, 0x12, 0x91, 0xf2, 0x58, 0x2c, 0x59, 0xb2, + 0x8a, 0x12, 0xf3, 0x22, 0x11, 0x31, 0x46, 0x06, 0x29, 0x8b, 0xec, 0xae, 0xbe, 0x73, 0xcf, 0xb9, + 0xf6, 0xd1, 0xe0, 0x1f, 0x6a, 0x11, 0xf7, 0xd9, 0x92, 0x51, 0x9e, 0x4c, 0x65, 0x24, 0xc6, 0x7d, + 0x21, 0x81, 0x32, 0xa5, 0x40, 0xaa, 0x70, 0x1c, 0x29, 0x16, 0x08, 0x09, 0x1a, 0x6c, 0x87, 0x02, + 0xbd, 0x93, 0x10, 0xd1, 0x9b, 0x40, 0x2d, 0xe2, 0x60, 0xc2, 0x95, 0x56, 0x8b, 0x58, 0xa6, 0x89, + 0xeb, 0x9e, 0x06, 0x4c, 0x22, 0x1d, 0xe5, 0x2e, 0xf7, 0xcb, 0x0c, 0x66, 0xf0, 0x3a, 0xf6, 0x77, + 0x53, 0x4e, 0xbb, 0xf7, 0x26, 0xfe, 0x34, 0x04, 0xa5, 0x87, 0xf9, 0xa5, 0x4b, 0xc1, 0xa8, 0xdd, + 0xc3, 0x58, 0x48, 0xb8, 0x65, 0x54, 0x73, 0x48, 0x9c, 0x8a, 0x87, 0xfc, 0xc6, 0xc0, 0x5a, 0x3d, + 0x7e, 0x37, 0x46, 0x25, 0x6e, 0xff, 0xc2, 0x1f, 0x21, 0xd5, 0x22, 0xd5, 0x21, 0x85, 0x38, 0x9d, + 0x27, 0xca, 0x31, 0x3d, 0xd3, 0x6f, 0x0d, 0x2a, 0x6d, 0x34, 0x6a, 0xe5, 0xca, 0xbf, 0x5c, 0xb0, + 0xff, 0xe2, 0x6f, 0x20, 0xf9, 0x8c, 0x27, 0x51, 0x1c, 0x9e, 0x78, 0xea, 0x07, 0xcf, 0xd7, 0x62, + 0xe5, 0xe2, 0xc8, 0x7b, 0x8e, 0x3f, 0x48, 0x96, 0x4c, 0x98, 0x0c, 0xd9, 0x52, 0x48, 0xe5, 0x58, + 0x9e, 0xe9, 0x37, 0xff, 0xf4, 0x82, 0xb7, 0x3a, 0x08, 0xfe, 0x2f, 0x85, 0x64, 0x4a, 0x71, 0x48, + 0xf6, 0x1f, 0xdd, 0xcc, 0xfd, 0x3b, 0xae, 0xec, 0x6b, 0x7c, 0xb8, 0x13, 0x1e, 0xe5, 0x36, 0xde, + 0x9d, 0xfb, 0xb9, 0x08, 0x1a, 0x95, 0xf2, 0x3b, 0xb8, 0x06, 0xd3, 0xa9, 0x62, 0xda, 0xa9, 0x7a, + 0xc8, 0xb7, 0xf6, 0xab, 0x7b, 0x66, 0xbb, 0xb8, 0x1a, 0xf3, 0x39, 0xd7, 0x4e, 0xad, 0x24, 0xe6, + 0xe8, 0xcc, 0x6a, 0xa0, 0x76, 0xa5, 0xfb, 0x13, 0xd7, 0x8b, 0x3f, 0xef, 0xe0, 0x7a, 0xd1, 0x12, + 0x3a, 0xb4, 0x54, 0xa0, 0xc1, 0xef, 0xd5, 0x33, 0x31, 0x56, 0x19, 0x41, 0xeb, 0x8c, 0xa0, 0x4d, + 0x46, 0xd0, 0x53, 0x46, 0xd0, 0xc3, 0x96, 0x18, 0xeb, 0x2d, 0x31, 0x36, 0x5b, 0x62, 0x5c, 0x35, + 0x4b, 0x0f, 0xe1, 0x25, 0x00, 0x00, 0xff, 0xff, 0x53, 0x49, 0x15, 0xda, 0x5b, 0x02, 0x00, 0x00, } func (m *PostProcessSpec) Marshal() (dAtA []byte, err error) { @@ -345,39 +304,6 @@ func (m *Columns) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *TableReaderSpan) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *TableReaderSpan) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *TableReaderSpan) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - { - size, err := m.Span.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProcessorsBase(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - func encodeVarintProcessorsBase(dAtA []byte, offset int, v uint64) int { offset -= sovProcessorsBase(v) base := offset @@ -443,17 +369,6 @@ func (m *Columns) Size() (n int) { return n } -func (m *TableReaderSpan) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.Span.Size() - n += 1 + l + sovProcessorsBase(uint64(l)) - return n -} - func sovProcessorsBase(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -914,89 +829,6 @@ func (m *Columns) Unmarshal(dAtA []byte) error { } return nil } -func (m *TableReaderSpan) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProcessorsBase - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: TableReaderSpan: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: TableReaderSpan: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Span", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProcessorsBase - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProcessorsBase - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProcessorsBase - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Span.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipProcessorsBase(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthProcessorsBase - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipProcessorsBase(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/sql/execinfrapb/processors_base.proto b/pkg/sql/execinfrapb/processors_base.proto index bd7412985f3a..fca201c46a46 100644 --- a/pkg/sql/execinfrapb/processors_base.proto +++ b/pkg/sql/execinfrapb/processors_base.proto @@ -20,7 +20,6 @@ package cockroach.sql.distsqlrun; option go_package = "execinfrapb"; import "sql/execinfrapb/data.proto"; -import "roachpb/data.proto"; import "gogoproto/gogo.proto"; // PostProcessSpec describes the processing required to obtain the output (e.g. @@ -69,11 +68,3 @@ message PostProcessSpec { message Columns { repeated uint32 columns = 1 [packed = true]; } - -message TableReaderSpan { - // TODO(radu): the dist_sql APIs should be agnostic to how we map tables to - // KVs. The span should be described as starting and ending lists of values - // for a prefix of the index columns, along with inclusive/exclusive flags. - optional roachpb.Span span = 1 [(gogoproto.nullable) = false]; -} - diff --git a/pkg/sql/execinfrapb/processors_bulk_io.pb.go b/pkg/sql/execinfrapb/processors_bulk_io.pb.go index f855053bb757..c8e3912540ee 100644 --- a/pkg/sql/execinfrapb/processors_bulk_io.pb.go +++ b/pkg/sql/execinfrapb/processors_bulk_io.pb.go @@ -134,8 +134,7 @@ func (BackfillerSpec_Type) EnumDescriptor() ([]byte, []int) { type BackfillerSpec struct { Type BackfillerSpec_Type `protobuf:"varint,1,opt,name=type,enum=cockroach.sql.distsqlrun.BackfillerSpec_Type" json:"type"` Table descpb.TableDescriptor `protobuf:"bytes,2,opt,name=table" json:"table"` - // Sections of the table to be backfilled. - Spans []TableReaderSpan `protobuf:"bytes,3,rep,name=spans" json:"spans"` + Spans []roachpb.Span `protobuf:"bytes,10,rep,name=spans" json:"spans"` // Run the backfill for approximately this duration. // The backfill will always process at least one backfill chunk. Duration time.Duration `protobuf:"varint,4,opt,name=duration,casttype=time.Duration" json:"duration"` @@ -796,139 +795,138 @@ func init() { } var fileDescriptor_6d46d06b67eadaca = []byte{ - // 2098 bytes of a gzipped FileDescriptorProto + // 2086 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x5b, 0x6f, 0x1b, 0xc7, - 0x15, 0xd6, 0xf2, 0x26, 0xf2, 0x50, 0x17, 0x6a, 0x6c, 0xc7, 0x1b, 0x15, 0x95, 0x04, 0xfa, 0x12, - 0xd6, 0x85, 0x49, 0xc4, 0x6e, 0x03, 0x23, 0x69, 0xe2, 0x9a, 0x92, 0xe5, 0x50, 0x8e, 0x6d, 0x75, + 0x15, 0xd6, 0xf2, 0x26, 0xf2, 0x50, 0x17, 0x6a, 0x9c, 0xc4, 0x1b, 0x15, 0x95, 0x04, 0xfa, 0x12, + 0xd6, 0x85, 0x49, 0xc4, 0x6e, 0x03, 0x23, 0x69, 0xe2, 0x9a, 0x94, 0xe5, 0x50, 0x8e, 0x6d, 0x75, 0x69, 0xd9, 0x40, 0xd0, 0x76, 0xb1, 0xdc, 0x1d, 0x51, 0x63, 0x2e, 0x77, 0xd6, 0x33, 0xb3, 0x96, 0xe9, 0xc7, 0xf6, 0xad, 0x4f, 0x7d, 0xed, 0x9f, 0x28, 0x50, 0xa0, 0xff, 0xa0, 0x2f, 0x7e, 0xcc, - 0x63, 0x80, 0x02, 0x42, 0x2b, 0x17, 0xe8, 0x6f, 0xa8, 0x9f, 0x8a, 0xb9, 0x2c, 0xb9, 0x92, 0x25, - 0x4b, 0x72, 0x90, 0x17, 0x6a, 0x35, 0x67, 0xbe, 0x6f, 0xce, 0x99, 0x39, 0xb7, 0x19, 0x68, 0xf0, - 0xe7, 0x61, 0x0b, 0xbf, 0xc4, 0x3e, 0x89, 0xb6, 0x99, 0x17, 0xf7, 0x5a, 0x31, 0xa3, 0x3e, 0xe6, - 0x9c, 0x32, 0xee, 0xf6, 0x92, 0x70, 0xe0, 0x12, 0xda, 0x8c, 0x19, 0x15, 0x14, 0xd9, 0x3e, 0xf5, - 0x07, 0x8c, 0x7a, 0xfe, 0x4e, 0x93, 0x3f, 0x0f, 0x9b, 0x01, 0xe1, 0x82, 0x3f, 0x0f, 0x59, 0x12, - 0x2d, 0x7e, 0xf4, 0x8c, 0xf6, 0x78, 0x4b, 0xfe, 0xc4, 0x3d, 0xf5, 0x47, 0x23, 0x16, 0x6d, 0x35, - 0x3b, 0xee, 0xb5, 0x08, 0xbd, 0xbe, 0x4d, 0xd9, 0xd0, 0x13, 0xa9, 0xe4, 0x92, 0x5c, 0xd5, 0xf7, - 0x84, 0x17, 0xd2, 0x7e, 0x2b, 0xc0, 0xdc, 0x8f, 0x7b, 0x2d, 0x2e, 0x58, 0xe2, 0x8b, 0x84, 0xe1, - 0xc0, 0x4c, 0xba, 0xf2, 0x3e, 0xd5, 0x3c, 0x8e, 0xd3, 0x55, 0x12, 0x41, 0xc2, 0xd6, 0x4e, 0xe8, - 0xb7, 0x04, 0x19, 0x62, 0x2e, 0xbc, 0x61, 0x6c, 0x24, 0xe7, 0xfb, 0xb4, 0x4f, 0xd5, 0x67, 0x4b, - 0x7e, 0x99, 0x51, 0x94, 0x6a, 0x15, 0x78, 0xc2, 0x33, 0x63, 0x0b, 0xe9, 0x98, 0x17, 0x13, 0x3d, - 0x54, 0xff, 0x6f, 0x01, 0xe6, 0xda, 0x9e, 0x3f, 0xd8, 0x26, 0x61, 0x88, 0x59, 0x37, 0xc6, 0x3e, - 0xba, 0x07, 0x05, 0x31, 0x8a, 0xb1, 0x6d, 0xad, 0x58, 0x8d, 0xb9, 0x1b, 0xd7, 0x9b, 0xc7, 0x6d, - 0x48, 0xf3, 0x20, 0xae, 0xf9, 0x78, 0x14, 0xe3, 0x76, 0xe1, 0xf5, 0xde, 0xf2, 0x94, 0xa3, 0x08, - 0x50, 0x1b, 0x8a, 0xc2, 0xeb, 0x85, 0xd8, 0xce, 0xad, 0x58, 0x8d, 0xea, 0x8d, 0xab, 0x87, 0x98, - 0xf8, 0xf3, 0x50, 0xd9, 0xf7, 0x58, 0xce, 0x59, 0xc3, 0xdc, 0x67, 0x24, 0x16, 0x94, 0x19, 0x0a, - 0x0d, 0x45, 0x77, 0xa1, 0xc8, 0x63, 0x2f, 0xe2, 0x76, 0x7e, 0x25, 0xdf, 0xa8, 0xde, 0xf8, 0xd9, - 0xf1, 0xda, 0x28, 0x1a, 0x07, 0x7b, 0x81, 0x54, 0xc7, 0x8b, 0x52, 0x1a, 0x85, 0x46, 0x9f, 0x42, - 0x39, 0x48, 0x98, 0x27, 0x08, 0x8d, 0xec, 0xc2, 0x8a, 0xd5, 0xc8, 0xb7, 0x2f, 0x48, 0xf1, 0xdb, - 0xbd, 0xe5, 0x59, 0xb9, 0x9d, 0xcd, 0x35, 0x23, 0x74, 0xc6, 0xd3, 0xd0, 0x25, 0x00, 0x7f, 0x27, - 0x89, 0x06, 0x2e, 0x27, 0xaf, 0xb0, 0x5d, 0x54, 0x20, 0xcd, 0x59, 0x51, 0xe3, 0x5d, 0xf2, 0x0a, - 0xa3, 0x3b, 0x50, 0xd9, 0x65, 0x44, 0xe0, 0x3b, 0xfc, 0xd1, 0xb6, 0x3d, 0xad, 0xcc, 0xfc, 0x69, - 0x46, 0x45, 0x79, 0x66, 0xcd, 0x9d, 0xd0, 0x6f, 0x3e, 0x4e, 0xcf, 0x2c, 0xa5, 0x18, 0xa3, 0xd0, - 0x6d, 0x28, 0x33, 0xec, 0x05, 0x8a, 0xa1, 0x72, 0x7a, 0x86, 0x31, 0x08, 0x71, 0x38, 0x47, 0xa2, - 0x00, 0xbf, 0xc4, 0xdc, 0x15, 0xd4, 0xed, 0x99, 0x43, 0xb1, 0xcb, 0x2b, 0xf9, 0xc6, 0x6c, 0x7b, - 0xf5, 0xed, 0xde, 0xf2, 0xed, 0x3e, 0x11, 0x3b, 0x49, 0xaf, 0xe9, 0xd3, 0x61, 0x6b, 0xcc, 0x1c, - 0xf4, 0x26, 0xdf, 0xad, 0x78, 0xd0, 0x6f, 0xbd, 0xeb, 0xab, 0xcd, 0x8e, 0xa4, 0xed, 0xac, 0x39, - 0x0b, 0x86, 0xff, 0x31, 0x4d, 0x8f, 0xbc, 0x7e, 0x0d, 0x0a, 0xf2, 0xbc, 0x51, 0x15, 0xa6, 0x3b, - 0xd1, 0x0b, 0x2f, 0x24, 0x41, 0x6d, 0x0a, 0x01, 0x94, 0x56, 0x69, 0x98, 0x0c, 0xa3, 0x9a, 0x85, - 0x2a, 0x50, 0x54, 0xf0, 0x5a, 0x6e, 0xa3, 0x50, 0x2e, 0xd5, 0xa6, 0xeb, 0x7f, 0xb3, 0xa0, 0xba, - 0x41, 0x7b, 0x9b, 0x8c, 0xf6, 0x19, 0xe6, 0x1c, 0xfd, 0x1e, 0x4a, 0xcf, 0x68, 0xcf, 0x25, 0x81, - 0x72, 0xb4, 0x7c, 0xfb, 0x9e, 0x34, 0x6b, 0x7f, 0x6f, 0xb9, 0xb8, 0x41, 0x7b, 0x9d, 0xb5, 0xb7, - 0x7b, 0xcb, 0x9f, 0x9d, 0x4a, 0xed, 0x4c, 0x50, 0x36, 0x15, 0xd2, 0x29, 0x3e, 0xa3, 0xbd, 0x4e, - 0x80, 0x1a, 0x30, 0xe3, 0xd3, 0x48, 0x30, 0xd2, 0x4b, 0xd4, 0xb1, 0x4b, 0x27, 0xcc, 0x99, 0xcd, - 0x3b, 0x20, 0x41, 0x36, 0x14, 0x78, 0x48, 0x85, 0x9d, 0x5f, 0xb1, 0x1a, 0xc5, 0xd4, 0x83, 0xe5, - 0x48, 0xfd, 0x4f, 0x00, 0x48, 0xba, 0x54, 0x67, 0x18, 0x53, 0x26, 0xd6, 0x3c, 0xe1, 0xa9, 0x08, - 0xf9, 0x12, 0x4a, 0x3a, 0xd0, 0xed, 0xb2, 0x3a, 0xb0, 0xe5, 0xcc, 0x81, 0x99, 0x10, 0x6b, 0x76, - 0x1e, 0xad, 0x93, 0x10, 0xaf, 0xab, 0x69, 0x86, 0xd3, 0x80, 0xd0, 0x15, 0xa8, 0x72, 0x6f, 0x18, - 0x87, 0x58, 0xbb, 0x56, 0x2e, 0xb3, 0x2c, 0x68, 0x81, 0xf2, 0xad, 0x27, 0x50, 0x52, 0x31, 0xc0, - 0xed, 0x8a, 0xf2, 0xfd, 0x5b, 0xc7, 0xfb, 0xfe, 0xbb, 0x3a, 0xea, 0x70, 0xe0, 0x77, 0x23, 0xc1, - 0x46, 0x8a, 0xdb, 0x72, 0x0c, 0x1b, 0xba, 0x07, 0xf9, 0x84, 0x11, 0x7b, 0x5a, 0x91, 0xfe, 0xf2, - 0x4c, 0xa4, 0x5b, 0x8c, 0x28, 0x46, 0x47, 0x32, 0xa0, 0x6f, 0x01, 0x18, 0xe6, 0xc9, 0x10, 0xbb, - 0x31, 0xe5, 0xf6, 0x9c, 0xe2, 0xfb, 0xe2, 0x4c, 0x7c, 0x8e, 0x82, 0x6f, 0x52, 0xad, 0xa7, 0x53, - 0x61, 0xe9, 0xff, 0xe8, 0x1e, 0x94, 0x63, 0xe3, 0x29, 0x76, 0x49, 0x6d, 0xf2, 0x95, 0xe3, 0x99, - 0x33, 0x6e, 0x95, 0x46, 0x47, 0x0a, 0x46, 0xb7, 0xe1, 0x63, 0x3e, 0x20, 0xb1, 0x3b, 0x24, 0x9c, - 0x93, 0xa8, 0xef, 0x6e, 0x53, 0x86, 0x49, 0x3f, 0x72, 0x07, 0x78, 0xc4, 0x6d, 0x58, 0xb1, 0x1a, - 0x65, 0x03, 0xf9, 0x48, 0x4e, 0x7b, 0xa0, 0x67, 0xad, 0xeb, 0x49, 0xf7, 0xf1, 0x88, 0xa3, 0x6b, - 0x30, 0xbb, 0xeb, 0x85, 0xa1, 0x4c, 0x13, 0x0f, 0xbd, 0x88, 0x72, 0xbb, 0x9a, 0x49, 0x05, 0x07, - 0x45, 0xe8, 0x06, 0x2c, 0x30, 0x95, 0x81, 0x36, 0x3d, 0xe6, 0x85, 0x21, 0x0e, 0x09, 0x1f, 0xda, - 0xb3, 0x99, 0xf3, 0x7d, 0x57, 0x8c, 0x30, 0x40, 0xc2, 0x31, 0x73, 0x55, 0x3e, 0xb6, 0xe7, 0x57, - 0xac, 0x46, 0xa5, 0xbd, 0x6e, 0x92, 0xd3, 0x57, 0xa7, 0x8b, 0x5c, 0xec, 0x27, 0x8c, 0x88, 0x51, - 0xb3, 0xfb, 0x9b, 0x6f, 0xb6, 0x38, 0x66, 0x91, 0x37, 0xc4, 0x9b, 0x92, 0xcd, 0xa9, 0x48, 0x66, - 0xf5, 0x89, 0xbe, 0x80, 0xa2, 0x4c, 0xca, 0xdc, 0xae, 0xa9, 0x73, 0xba, 0x72, 0x5c, 0x32, 0x1e, - 0xc5, 0x99, 0x5c, 0xec, 0x68, 0x0c, 0xfa, 0x83, 0x05, 0x17, 0x65, 0x1d, 0x91, 0x53, 0xdc, 0x98, - 0x91, 0xa1, 0xc7, 0x46, 0x2e, 0xc3, 0x7d, 0x19, 0x57, 0x0b, 0x4a, 0xe3, 0x0d, 0xa3, 0x71, 0xfb, - 0x43, 0x73, 0x8d, 0xa3, 0xd8, 0x1e, 0x7a, 0x43, 0xec, 0x5c, 0x48, 0x97, 0xda, 0xd4, 0x2b, 0x69, - 0xd1, 0x62, 0x02, 0x55, 0xed, 0x3e, 0xca, 0xb5, 0xd1, 0xaf, 0xa1, 0x20, 0xa1, 0x2a, 0x7b, 0x9c, - 0xad, 0xb8, 0x58, 0x8e, 0x42, 0xa2, 0xcb, 0x00, 0xc2, 0x63, 0x7d, 0x2c, 0x56, 0x69, 0xc8, 0xed, - 0xdc, 0x4a, 0xbe, 0x51, 0x31, 0xf2, 0xcc, 0xf8, 0x22, 0x87, 0x6a, 0x26, 0x96, 0x50, 0x0d, 0xf2, - 0x03, 0x3c, 0x52, 0xab, 0x56, 0x1c, 0xf9, 0x89, 0x1e, 0x42, 0xf1, 0x85, 0x17, 0x26, 0x69, 0x99, - 0x3b, 0x5b, 0x98, 0x66, 0x2c, 0x72, 0x34, 0xcd, 0xe7, 0xb9, 0x5b, 0xd6, 0xe2, 0x67, 0x50, 0x4e, - 0x63, 0x2d, 0xbb, 0x62, 0x51, 0xaf, 0x78, 0x3e, 0xbb, 0x62, 0x25, 0x8b, 0xfb, 0x15, 0xcc, 0x1d, - 0x8c, 0xa9, 0x93, 0xd0, 0xf9, 0x0c, 0x7a, 0xa3, 0x50, 0xb6, 0x54, 0xba, 0xce, 0xd7, 0x0a, 0x1b, - 0x85, 0x72, 0xa1, 0x56, 0xdc, 0x28, 0x94, 0x8b, 0xb5, 0xd2, 0x46, 0xa1, 0x3c, 0x53, 0x9b, 0xad, - 0xff, 0x27, 0x07, 0x17, 0xbb, 0x82, 0x61, 0x6f, 0xd8, 0x89, 0xfa, 0x98, 0xcb, 0xd4, 0x39, 0xce, - 0x88, 0xd7, 0xa1, 0xc2, 0x95, 0x48, 0xe6, 0x73, 0x59, 0x2b, 0x0b, 0xed, 0x9a, 0xc9, 0xe7, 0x65, - 0x83, 0x59, 0x73, 0xca, 0x7a, 0x4a, 0x27, 0x40, 0x97, 0x60, 0x36, 0xf6, 0x98, 0x20, 0x92, 0xc3, - 0x25, 0x81, 0x0c, 0xf1, 0x7c, 0xa3, 0xe2, 0xcc, 0x8c, 0x07, 0x3b, 0x01, 0x47, 0x9f, 0xc0, 0xfc, - 0x64, 0x12, 0x8f, 0xb1, 0xcf, 0x55, 0xce, 0xaa, 0x38, 0x73, 0xe3, 0x61, 0xb9, 0x36, 0x47, 0x2d, - 0x38, 0x37, 0x99, 0xe8, 0x05, 0x81, 0x8c, 0x7b, 0xcc, 0x55, 0x01, 0xac, 0x38, 0x68, 0x2c, 0xba, - 0x93, 0x4a, 0x50, 0x1b, 0x80, 0x0b, 0x8f, 0x09, 0x57, 0x46, 0xae, 0x39, 0xb6, 0xd3, 0x95, 0x6d, - 0x05, 0x93, 0xa3, 0xe8, 0xe7, 0x30, 0x67, 0x2c, 0x36, 0x2b, 0xaa, 0xf2, 0x51, 0x49, 0xf3, 0x82, - 0x96, 0x99, 0x25, 0xd1, 0xe5, 0x71, 0xad, 0xd3, 0xcd, 0xc7, 0xec, 0x81, 0x5a, 0x67, 0x2a, 0x96, - 0xde, 0xfe, 0xfa, 0x3f, 0x2d, 0xf8, 0xc9, 0xa1, 0x6d, 0x5e, 0x67, 0x34, 0x12, 0xc4, 0xb4, 0x67, - 0x0e, 0x9c, 0xdb, 0x21, 0xfd, 0x1d, 0x77, 0xd7, 0x13, 0x98, 0xb9, 0x9e, 0x70, 0x95, 0x4e, 0x26, - 0x0c, 0x4e, 0x65, 0x45, 0x4d, 0xe2, 0x9f, 0x4a, 0xf8, 0x1d, 0xd1, 0x95, 0x60, 0xd4, 0x86, 0x59, - 0xc1, 0x3c, 0x7f, 0x80, 0x03, 0x57, 0x77, 0x5b, 0x39, 0x95, 0x24, 0x2e, 0x1e, 0x51, 0xd7, 0x32, - 0xbd, 0xd5, 0x8c, 0xc1, 0x74, 0x55, 0x8b, 0x35, 0xb1, 0x31, 0x7f, 0xbc, 0x8d, 0xf5, 0x7f, 0x4c, - 0xeb, 0x7e, 0x33, 0x89, 0xc7, 0xbe, 0x73, 0x33, 0x6d, 0xf1, 0xac, 0xd3, 0x2c, 0x6a, 0x1a, 0xba, - 0xaf, 0xa1, 0x46, 0x22, 0xc1, 0x68, 0x90, 0xf8, 0x67, 0x53, 0x7a, 0x7e, 0x02, 0xd3, 0x7a, 0xdf, - 0x84, 0x6a, 0x80, 0xb7, 0xbd, 0x24, 0x14, 0xae, 0x2c, 0x8b, 0xfa, 0x14, 0x91, 0x51, 0x1e, 0xd6, - 0xb4, 0x68, 0xcb, 0xe9, 0x38, 0x60, 0xa6, 0x6d, 0x31, 0x82, 0xfe, 0x68, 0xc1, 0xb9, 0x84, 0x11, - 0xee, 0xf6, 0x46, 0x6e, 0x48, 0x7d, 0x2f, 0x24, 0x62, 0xe4, 0x0e, 0x5e, 0xd8, 0x05, 0xa5, 0xc2, - 0x57, 0xef, 0xef, 0x99, 0x27, 0xb6, 0xcb, 0x82, 0xca, 0xdb, 0xa3, 0x6f, 0x0c, 0xc3, 0xfd, 0x17, - 0xba, 0x5e, 0x9f, 0xdf, 0xdf, 0x5b, 0xae, 0x6d, 0x39, 0x9d, 0xac, 0xe8, 0x89, 0x53, 0x4b, 0x0e, - 0x4d, 0x46, 0x0e, 0x54, 0x87, 0x2f, 0x7c, 0xdf, 0xdd, 0x26, 0xa1, 0xc0, 0x4c, 0xc5, 0xdd, 0xdc, - 0x01, 0x17, 0x48, 0xed, 0x7f, 0xf0, 0x64, 0x75, 0x75, 0x5d, 0x4d, 0x9a, 0x58, 0x36, 0x19, 0x73, - 0x40, 0xb2, 0xe8, 0x6f, 0xf4, 0x35, 0x00, 0x8e, 0x7c, 0x36, 0x8a, 0x55, 0xd3, 0xa4, 0x4b, 0x6f, - 0xe3, 0x08, 0x4a, 0xd9, 0xdd, 0xdc, 0x1d, 0x4f, 0x7c, 0xa4, 0x7e, 0xb9, 0x93, 0xc1, 0xa2, 0x47, - 0xb0, 0xd0, 0x53, 0xd6, 0xba, 0x99, 0x60, 0x3b, 0x43, 0x8f, 0x3c, 0xaf, 0xd1, 0xdd, 0x71, 0xc8, - 0xdd, 0x07, 0x33, 0xe4, 0xe2, 0x28, 0xd0, 0x74, 0xe5, 0xd3, 0xd3, 0xcd, 0x6a, 0xec, 0xdd, 0x28, - 0x50, 0x64, 0x5b, 0x50, 0x8a, 0x07, 0x2a, 0xf7, 0xe8, 0xee, 0xea, 0xe6, 0xa9, 0xcf, 0x6c, 0x73, - 0xd0, 0x09, 0x4c, 0x63, 0x55, 0x91, 0xfe, 0xbd, 0x79, 0xbf, 0xb3, 0xc6, 0x9d, 0x62, 0x2c, 0x87, - 0x0f, 0x55, 0x73, 0xf8, 0x91, 0xaa, 0xf9, 0xe2, 0x2a, 0x5c, 0x38, 0xd2, 0x75, 0x8e, 0x28, 0x4f, - 0xc7, 0x17, 0x8b, 0x5b, 0x00, 0x13, 0x5b, 0xb2, 0xc8, 0xc2, 0x11, 0xc8, 0x72, 0x06, 0x59, 0xe7, - 0x30, 0xef, 0x60, 0x2e, 0x28, 0xc3, 0xd2, 0x0d, 0x54, 0x14, 0x7f, 0x0e, 0xf9, 0x80, 0x30, 0x93, - 0x86, 0xea, 0x47, 0x38, 0xcc, 0xdd, 0x97, 0x42, 0x1a, 0x13, 0x76, 0x05, 0x65, 0x5e, 0x3f, 0xbd, - 0x29, 0x4a, 0x90, 0x6c, 0xc0, 0x63, 0x4f, 0xec, 0x68, 0x0d, 0xd3, 0x06, 0x5c, 0x8e, 0x64, 0x6b, - 0x51, 0xfd, 0x01, 0x80, 0xb9, 0xe3, 0x49, 0xe5, 0x2e, 0x43, 0x89, 0x86, 0x41, 0x7a, 0x7d, 0x98, - 0x9d, 0xa4, 0x9b, 0x47, 0x61, 0x20, 0xd3, 0x0d, 0x0d, 0x83, 0x4e, 0x80, 0x3e, 0x86, 0x72, 0x84, - 0x77, 0x5d, 0xd5, 0x28, 0x48, 0xf6, 0x19, 0x67, 0x3a, 0xc2, 0xbb, 0xb2, 0x1f, 0xa8, 0xff, 0xdd, - 0x82, 0x9a, 0x31, 0x42, 0x26, 0x02, 0xbd, 0x09, 0x9f, 0x42, 0x41, 0xe6, 0x12, 0x63, 0xc6, 0x09, - 0xa9, 0x44, 0x4d, 0x95, 0x37, 0xd4, 0x6d, 0x22, 0xbb, 0xf4, 0xdc, 0x49, 0x37, 0xd4, 0x43, 0x5b, - 0x96, 0x26, 0x34, 0x85, 0x46, 0x57, 0xa1, 0x9a, 0xf6, 0xac, 0x9d, 0xe0, 0xa5, 0xc9, 0xa1, 0x7a, - 0x46, 0x56, 0x50, 0xff, 0x5f, 0x6e, 0xbc, 0xf7, 0xe3, 0x0c, 0xba, 0x0e, 0x33, 0x4c, 0x0f, 0xe9, - 0xa8, 0x38, 0x43, 0x2d, 0xa8, 0x1a, 0xa0, 0x8a, 0x89, 0x83, 0xb1, 0x9f, 0xfb, 0x01, 0xb1, 0xdf, - 0x86, 0x12, 0xc3, 0xaa, 0xc5, 0xd6, 0xf7, 0xf6, 0xcb, 0x27, 0xde, 0xdb, 0x07, 0x78, 0x94, 0x5e, - 0x93, 0x34, 0x52, 0xde, 0x7f, 0x4c, 0x84, 0xea, 0xac, 0xfa, 0x8b, 0x13, 0x77, 0xf6, 0x54, 0x21, - 0xfa, 0x03, 0xdc, 0xfe, 0x2f, 0x39, 0x38, 0xd7, 0x8d, 0x43, 0x22, 0xee, 0x44, 0x41, 0xd7, 0xf7, - 0x84, 0x30, 0x25, 0xf9, 0x77, 0x50, 0x52, 0x4f, 0x02, 0x69, 0x09, 0xbb, 0x7d, 0xbc, 0xa6, 0x47, - 0xc0, 0x53, 0xed, 0x95, 0x3e, 0xab, 0x92, 0x27, 0xdd, 0x08, 0x4d, 0x9a, 0xd9, 0xcc, 0xdc, 0x87, - 0x6e, 0xe6, 0xa2, 0x0b, 0x0b, 0xef, 0x2c, 0x83, 0x36, 0x60, 0x1a, 0xcb, 0x7b, 0x30, 0x4e, 0x15, - 0xbf, 0x76, 0xe2, 0x16, 0x8f, 0x43, 0xc5, 0xf0, 0xa7, 0x04, 0xf5, 0xbf, 0xe6, 0x61, 0x76, 0xb5, - 0xfb, 0xe4, 0x29, 0x23, 0xe9, 0xae, 0x5c, 0x95, 0x85, 0x95, 0x0b, 0x12, 0xe9, 0x67, 0x17, 0x2b, - 0x13, 0xdc, 0x59, 0x01, 0xfa, 0x04, 0x66, 0x64, 0x8e, 0x73, 0x63, 0xb5, 0x23, 0xd1, 0x81, 0x2c, - 0x50, 0x55, 0xd9, 0x4f, 0x0b, 0xd0, 0x97, 0x30, 0x4d, 0xb5, 0xaf, 0xa9, 0xf0, 0xa8, 0x1e, 0x59, - 0xea, 0x56, 0xbb, 0x4f, 0x8c, 0x43, 0xa6, 0x1a, 0x1a, 0xcc, 0xe4, 0x41, 0x87, 0xd1, 0x5d, 0x6e, - 0x1a, 0xb1, 0xec, 0x83, 0x8e, 0x43, 0x77, 0xf9, 0xa1, 0x57, 0x9f, 0xe9, 0xa3, 0x5f, 0x7d, 0x7e, - 0x0b, 0x0b, 0x3e, 0x1d, 0xc6, 0x32, 0x24, 0x65, 0xcb, 0xe9, 0xd3, 0x00, 0xfb, 0xa6, 0xfa, 0xbe, - 0x27, 0xfc, 0x65, 0xd4, 0xac, 0x4e, 0x60, 0x69, 0x33, 0x96, 0x61, 0x5a, 0x95, 0x44, 0x87, 0x4a, - 0x48, 0xe9, 0x47, 0x2a, 0x21, 0xf5, 0xa7, 0xb0, 0xd0, 0x4e, 0x42, 0x69, 0x75, 0xe6, 0xcc, 0xc6, - 0x4f, 0x76, 0xd6, 0x07, 0x3f, 0xd9, 0x5d, 0xbb, 0x02, 0xf3, 0x87, 0x4c, 0x45, 0x65, 0x28, 0x3c, - 0xa4, 0x11, 0xae, 0x4d, 0xc9, 0xaf, 0x7b, 0xaf, 0x48, 0x5c, 0xb3, 0xda, 0xd7, 0x5f, 0xff, 0x7b, - 0x69, 0xea, 0xf5, 0xfe, 0x92, 0xf5, 0xdd, 0xfe, 0x92, 0xf5, 0xfd, 0xfe, 0x92, 0xf5, 0xaf, 0xfd, - 0x25, 0xeb, 0xcf, 0x6f, 0x96, 0xa6, 0xbe, 0x7b, 0xb3, 0x34, 0xf5, 0xfd, 0x9b, 0xa5, 0xa9, 0x6f, - 0xab, 0x99, 0x57, 0xd1, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x39, 0xa6, 0xbe, 0xc2, 0x15, - 0x00, 0x00, + 0x63, 0x80, 0x00, 0x42, 0x2b, 0xf7, 0x4f, 0xd4, 0x4f, 0xc5, 0x5c, 0x96, 0x5c, 0xc9, 0x92, 0x2d, + 0x39, 0xc8, 0x8b, 0xbc, 0x9e, 0x73, 0xbe, 0x6f, 0xce, 0x99, 0x39, 0x37, 0x0e, 0x34, 0xf8, 0xd3, + 0xb0, 0x85, 0x9f, 0x63, 0x9f, 0x44, 0x3b, 0xcc, 0x8b, 0xfb, 0xad, 0x98, 0x51, 0x1f, 0x73, 0x4e, + 0x19, 0x77, 0xfb, 0x49, 0x38, 0x74, 0x09, 0x6d, 0xc6, 0x8c, 0x0a, 0x8a, 0x6c, 0x9f, 0xfa, 0x43, + 0x46, 0x3d, 0x7f, 0xb7, 0xc9, 0x9f, 0x86, 0xcd, 0x80, 0x70, 0xc1, 0x9f, 0x86, 0x2c, 0x89, 0x96, + 0x3f, 0x7a, 0x42, 0xfb, 0xbc, 0x25, 0xff, 0xc4, 0x7d, 0xf5, 0x8f, 0x46, 0x2c, 0xdb, 0x4a, 0x3b, + 0xee, 0xb7, 0x08, 0xbd, 0xba, 0x43, 0xd9, 0xc8, 0x13, 0xa9, 0xe4, 0x82, 0xdc, 0xd5, 0xf7, 0x84, + 0x17, 0xd2, 0x41, 0x2b, 0xc0, 0xdc, 0x8f, 0xfb, 0x2d, 0x2e, 0x58, 0xe2, 0x8b, 0x84, 0xe1, 0xc0, + 0x28, 0x5d, 0x7a, 0x9b, 0x69, 0x1e, 0xc7, 0xe9, 0x2e, 0x89, 0x20, 0x61, 0x6b, 0x37, 0xf4, 0x5b, + 0x82, 0x8c, 0x30, 0x17, 0xde, 0x28, 0x36, 0x92, 0x0f, 0x06, 0x74, 0x40, 0xd5, 0x67, 0x4b, 0x7e, + 0x99, 0x55, 0x94, 0x5a, 0x15, 0x78, 0xc2, 0x33, 0x6b, 0x4b, 0xe9, 0x9a, 0x17, 0x13, 0xbd, 0x54, + 0xff, 0xa1, 0x00, 0x0b, 0x6d, 0xcf, 0x1f, 0xee, 0x90, 0x30, 0xc4, 0xac, 0x17, 0x63, 0x1f, 0xdd, + 0x81, 0x82, 0x18, 0xc7, 0xd8, 0xb6, 0xd6, 0xac, 0xc6, 0xc2, 0xb5, 0xab, 0xcd, 0x93, 0x0e, 0xa4, + 0x79, 0x18, 0xd7, 0x7c, 0x38, 0x8e, 0x71, 0xbb, 0xf0, 0x72, 0x7f, 0x75, 0xc6, 0x51, 0x04, 0xa8, + 0x0d, 0x45, 0xe1, 0xf5, 0x43, 0x6c, 0xe7, 0xd6, 0xac, 0x46, 0xf5, 0xda, 0xe5, 0x23, 0x4c, 0xfc, + 0x69, 0xa8, 0xfc, 0x7b, 0x28, 0x75, 0xd6, 0x31, 0xf7, 0x19, 0x89, 0x05, 0x65, 0x86, 0x42, 0x43, + 0xd1, 0x75, 0x28, 0xf2, 0xd8, 0x8b, 0xb8, 0x0d, 0x6b, 0xf9, 0x46, 0xf5, 0xda, 0xf9, 0x0c, 0x87, + 0x71, 0xa6, 0xd9, 0x8b, 0xbd, 0x28, 0x05, 0x29, 0x5d, 0xf4, 0x29, 0x94, 0x83, 0x84, 0x79, 0x82, + 0xd0, 0xc8, 0x2e, 0xac, 0x59, 0x8d, 0x7c, 0xfb, 0x43, 0x29, 0x7e, 0xbd, 0xbf, 0x3a, 0x2f, 0x0f, + 0xaf, 0xb9, 0x6e, 0x84, 0xce, 0x44, 0x0d, 0x5d, 0x00, 0xf0, 0x77, 0x93, 0x68, 0xe8, 0x72, 0xf2, + 0x02, 0xdb, 0x45, 0x05, 0xd2, 0x9c, 0x15, 0xb5, 0xde, 0x23, 0x2f, 0x30, 0xba, 0x05, 0x95, 0x3d, + 0x46, 0x04, 0xbe, 0xc5, 0x1f, 0xec, 0xd8, 0xb3, 0xca, 0xa9, 0x9f, 0x67, 0x0c, 0x92, 0x37, 0xd4, + 0xdc, 0x0d, 0xfd, 0xe6, 0xc3, 0xf4, 0x86, 0x52, 0x8a, 0x09, 0x0a, 0xdd, 0x84, 0x32, 0xc3, 0x5e, + 0xa0, 0x18, 0x2a, 0xa7, 0x67, 0x98, 0x80, 0x10, 0x87, 0x73, 0x24, 0x0a, 0xf0, 0x73, 0xcc, 0x5d, + 0x41, 0xdd, 0xbe, 0xb9, 0x02, 0xbb, 0xbc, 0x96, 0x6f, 0xcc, 0xb7, 0x3b, 0xaf, 0xf7, 0x57, 0x6f, + 0x0e, 0x88, 0xd8, 0x4d, 0xfa, 0x4d, 0x9f, 0x8e, 0x5a, 0x13, 0xe6, 0xa0, 0x3f, 0xfd, 0x6e, 0xc5, + 0xc3, 0x41, 0xeb, 0xcd, 0xc8, 0x6c, 0x76, 0x25, 0x6d, 0x77, 0xdd, 0x59, 0x32, 0xfc, 0x0f, 0x69, + 0x7a, 0xc1, 0xf5, 0x2b, 0x50, 0x90, 0xb7, 0x8b, 0xaa, 0x30, 0xdb, 0x8d, 0x9e, 0x79, 0x21, 0x09, + 0x6a, 0x33, 0x08, 0xa0, 0xd4, 0xa1, 0x61, 0x32, 0x8a, 0x6a, 0x16, 0xaa, 0x40, 0x51, 0xc1, 0x6b, + 0xb9, 0xcd, 0x42, 0x39, 0x5f, 0x2b, 0x6c, 0x16, 0xca, 0xa5, 0xda, 0x6c, 0xfd, 0x1f, 0x16, 0x54, + 0x37, 0x69, 0x7f, 0x8b, 0xd1, 0x01, 0xc3, 0x9c, 0xa3, 0x3f, 0x42, 0xe9, 0x09, 0xed, 0xbb, 0x24, + 0x50, 0xc1, 0x95, 0x6f, 0xdf, 0x91, 0xce, 0x1d, 0xec, 0xaf, 0x16, 0x37, 0x69, 0xbf, 0xbb, 0xfe, + 0x7a, 0x7f, 0xf5, 0xb3, 0x53, 0x19, 0x9f, 0x49, 0xc4, 0xa6, 0x42, 0x3a, 0xc5, 0x27, 0xb4, 0xdf, + 0x0d, 0x50, 0x03, 0xe6, 0x7c, 0x1a, 0x09, 0x46, 0xfa, 0x89, 0xba, 0x7c, 0x19, 0x78, 0x39, 0x73, + 0x84, 0x87, 0x24, 0xc8, 0x86, 0x02, 0x0f, 0xa9, 0xb0, 0xf3, 0x6b, 0x56, 0xa3, 0x98, 0x46, 0xad, + 0x5c, 0xa9, 0xff, 0x05, 0x00, 0x39, 0xd8, 0x0b, 0xba, 0xa3, 0x98, 0x32, 0xb1, 0xee, 0x09, 0x4f, + 0x65, 0xc5, 0x97, 0x50, 0xd2, 0xc9, 0x6d, 0x97, 0xd5, 0xb5, 0xad, 0x1e, 0x13, 0x89, 0xdd, 0x07, + 0x1b, 0x24, 0xc4, 0x1b, 0x4a, 0xcd, 0x70, 0x1a, 0x10, 0xba, 0x04, 0x55, 0xee, 0x8d, 0xe2, 0x10, + 0xeb, 0x00, 0xcb, 0x65, 0xb6, 0x05, 0x2d, 0x50, 0x11, 0xf6, 0x08, 0x4a, 0x2a, 0xee, 0xb9, 0x5d, + 0x51, 0xf1, 0x7e, 0xe3, 0xe4, 0xec, 0x7b, 0xd3, 0x46, 0x9d, 0x49, 0xfc, 0x76, 0x24, 0xd8, 0x58, + 0x71, 0x5b, 0x8e, 0x61, 0x43, 0x77, 0x20, 0x9f, 0x30, 0x62, 0xcf, 0x2a, 0xd2, 0x5f, 0x9f, 0x89, + 0x74, 0x9b, 0x11, 0xc5, 0xe8, 0x48, 0x06, 0xf4, 0x2d, 0x00, 0xc3, 0x3c, 0x19, 0x61, 0x37, 0xa6, + 0xdc, 0x5e, 0x50, 0x7c, 0x5f, 0x9c, 0x89, 0xcf, 0x51, 0xf0, 0x2d, 0xaa, 0xed, 0x74, 0x2a, 0x2c, + 0xfd, 0x3f, 0xba, 0x03, 0xe5, 0xd8, 0x44, 0x8a, 0x5d, 0x52, 0x87, 0x7c, 0xe9, 0x64, 0xe6, 0x4c, + 0x58, 0xa5, 0x39, 0x92, 0x82, 0xd1, 0x4d, 0xf8, 0x98, 0x0f, 0x49, 0xec, 0x8e, 0x08, 0xe7, 0x24, + 0x1a, 0xb8, 0x3b, 0x94, 0x61, 0x32, 0x88, 0xdc, 0x21, 0x1e, 0xcb, 0x42, 0x62, 0x35, 0xca, 0x06, + 0xf2, 0x91, 0x54, 0xbb, 0xa7, 0xb5, 0x36, 0xb4, 0xd2, 0x5d, 0x3c, 0xe6, 0xe8, 0x0a, 0xcc, 0xef, + 0x79, 0x61, 0x28, 0x8b, 0xc5, 0x7d, 0x2f, 0xa2, 0xdc, 0xae, 0x66, 0x0a, 0xc2, 0x61, 0x11, 0xba, + 0x06, 0x4b, 0x32, 0x39, 0x31, 0xdb, 0xf2, 0x98, 0x17, 0x86, 0x38, 0x24, 0x7c, 0x64, 0xcf, 0x67, + 0xee, 0xf7, 0x4d, 0x31, 0xc2, 0x00, 0x09, 0xc7, 0xcc, 0x55, 0x35, 0xd8, 0x5e, 0x5c, 0xb3, 0x1a, + 0x95, 0xf6, 0x86, 0x29, 0x51, 0x5f, 0x9d, 0x2e, 0x7f, 0xb1, 0x9f, 0x30, 0x22, 0xc6, 0xcd, 0xde, + 0xef, 0xbe, 0xd9, 0xe6, 0x98, 0x45, 0xde, 0x08, 0x6f, 0x49, 0x36, 0xa7, 0x22, 0x99, 0xd5, 0x27, + 0xfa, 0x02, 0x8a, 0xb2, 0x10, 0x73, 0xbb, 0xa6, 0xee, 0xe9, 0xd2, 0x49, 0x05, 0x78, 0x1c, 0x67, + 0xea, 0xaf, 0xa3, 0x31, 0xe8, 0x4f, 0x16, 0x9c, 0x97, 0xbd, 0x43, 0xaa, 0xb8, 0x31, 0x23, 0x23, + 0x8f, 0x8d, 0x5d, 0x86, 0x07, 0x32, 0xaf, 0x96, 0x94, 0xc5, 0x9b, 0xc6, 0xe2, 0xf6, 0xfb, 0x56, + 0x1c, 0x47, 0xb1, 0xdd, 0xf7, 0x46, 0xd8, 0xf9, 0x30, 0xdd, 0x6a, 0x4b, 0xef, 0xa4, 0x45, 0xcb, + 0x09, 0x54, 0x75, 0xf8, 0xa8, 0xd0, 0x46, 0xbf, 0x85, 0x82, 0x84, 0xaa, 0xea, 0x71, 0xb6, 0x86, + 0x62, 0x39, 0x0a, 0x89, 0x2e, 0x02, 0x08, 0x8f, 0x0d, 0xb0, 0xe8, 0xd0, 0x90, 0xdb, 0xb9, 0xb5, + 0x7c, 0xa3, 0x62, 0xe4, 0x99, 0xf5, 0x65, 0x0e, 0xd5, 0x4c, 0x2e, 0xa1, 0x1a, 0xe4, 0x87, 0x78, + 0xac, 0x76, 0xad, 0x38, 0xf2, 0x13, 0xdd, 0x87, 0xe2, 0x33, 0x2f, 0x4c, 0xd2, 0xd6, 0x76, 0xb6, + 0x34, 0xcd, 0x78, 0xe4, 0x68, 0x9a, 0xcf, 0x73, 0x37, 0xac, 0xe5, 0xcf, 0xa0, 0x9c, 0xe6, 0x5a, + 0x76, 0xc7, 0xa2, 0xde, 0xf1, 0x83, 0xec, 0x8e, 0x95, 0x2c, 0xee, 0x37, 0xb0, 0x70, 0x38, 0xa7, + 0xde, 0x85, 0xce, 0x67, 0xd0, 0x9b, 0x85, 0xb2, 0x95, 0x29, 0xda, 0x85, 0x5a, 0x71, 0xb3, 0x50, + 0x2e, 0xd6, 0x4a, 0x9b, 0x85, 0xf2, 0x5c, 0x6d, 0xbe, 0xfe, 0xdf, 0x1c, 0x9c, 0xef, 0x09, 0x86, + 0xbd, 0x51, 0x37, 0x1a, 0x60, 0x2e, 0x4b, 0xe7, 0xa4, 0x22, 0x5e, 0x85, 0x0a, 0x57, 0x22, 0x59, + 0xcf, 0x65, 0xc7, 0x2c, 0xb4, 0x6b, 0xa6, 0x9e, 0x97, 0x0d, 0x66, 0xdd, 0x29, 0x6b, 0x95, 0x6e, + 0x80, 0x2e, 0xc0, 0x7c, 0xec, 0x31, 0x41, 0x24, 0x87, 0x4b, 0x02, 0x99, 0xe2, 0xf9, 0x46, 0xc5, + 0x99, 0x9b, 0x2c, 0x76, 0x03, 0x8e, 0x3e, 0x81, 0xc5, 0xa9, 0x12, 0x8f, 0xb1, 0xcf, 0x55, 0xcd, + 0xaa, 0x38, 0x0b, 0x93, 0x65, 0xb9, 0x37, 0x47, 0x2d, 0x38, 0x37, 0x55, 0xf4, 0x82, 0x40, 0xe6, + 0x3d, 0xe6, 0xaa, 0x0d, 0x56, 0x1c, 0x34, 0x11, 0xdd, 0x4a, 0x25, 0xa8, 0x0d, 0xc0, 0x85, 0xc7, + 0x84, 0x2b, 0x33, 0xd7, 0x5c, 0xdb, 0xe9, 0x9a, 0xb7, 0x82, 0xc9, 0x55, 0xf4, 0x4b, 0x58, 0x30, + 0x1e, 0x9b, 0x1d, 0x55, 0xfb, 0xa8, 0xa4, 0x75, 0x41, 0xcb, 0xcc, 0x96, 0xe8, 0xe2, 0xa4, 0xd7, + 0xe9, 0x11, 0x64, 0xfe, 0x50, 0xaf, 0x33, 0x1d, 0x4b, 0x1f, 0x7f, 0xfd, 0x07, 0x0b, 0x7e, 0x76, + 0xe4, 0x98, 0x37, 0x18, 0x8d, 0x04, 0x31, 0x23, 0x99, 0x03, 0xe7, 0x76, 0xc9, 0x60, 0xd7, 0xdd, + 0xf3, 0x04, 0x66, 0xae, 0x27, 0x5c, 0x65, 0x93, 0x49, 0x83, 0x53, 0x79, 0x51, 0x93, 0xf8, 0xc7, + 0x12, 0x7e, 0x4b, 0xf4, 0x24, 0x18, 0xb5, 0x61, 0x5e, 0x30, 0xcf, 0x1f, 0xe2, 0xc0, 0xd5, 0x13, + 0x56, 0xee, 0x34, 0x13, 0xd6, 0x9c, 0xc1, 0xf4, 0xd4, 0xa0, 0x35, 0xf5, 0x31, 0x7f, 0xb2, 0x8f, + 0xf5, 0x7f, 0xcd, 0xea, 0x19, 0x33, 0x89, 0x27, 0xb1, 0x33, 0x19, 0xeb, 0xac, 0x33, 0x8c, 0x75, + 0x5f, 0x43, 0x8d, 0x44, 0x82, 0xd1, 0x20, 0xf1, 0xcf, 0x66, 0xf4, 0xe2, 0x14, 0xa6, 0xed, 0xbe, + 0x0e, 0xd5, 0x00, 0xef, 0x78, 0x49, 0x28, 0x5c, 0xd9, 0x16, 0xf5, 0x2d, 0x22, 0x63, 0x3c, 0xac, + 0x6b, 0xd1, 0xb6, 0xd3, 0x75, 0xc0, 0xa8, 0x6d, 0x33, 0x82, 0xfe, 0x6c, 0xc1, 0xb9, 0x84, 0x11, + 0xee, 0xf6, 0xc7, 0x6e, 0x48, 0x7d, 0x2f, 0x24, 0x62, 0xec, 0x0e, 0x9f, 0xd9, 0x05, 0x65, 0xc2, + 0x57, 0x6f, 0x9f, 0x93, 0xa7, 0xbe, 0xcb, 0x86, 0xca, 0xdb, 0xe3, 0x6f, 0x0c, 0xc3, 0xdd, 0x67, + 0xba, 0x5f, 0x7f, 0x70, 0xb0, 0xbf, 0x5a, 0xdb, 0x76, 0xba, 0x59, 0xd1, 0x23, 0xa7, 0x96, 0x1c, + 0x51, 0x46, 0x0e, 0x54, 0x47, 0xcf, 0x7c, 0xdf, 0xdd, 0x21, 0xa1, 0xc0, 0x4c, 0xe5, 0xdd, 0xc2, + 0xa1, 0x10, 0x48, 0xfd, 0xbf, 0xf7, 0xa8, 0xd3, 0xd9, 0x50, 0x4a, 0x53, 0xcf, 0xa6, 0x6b, 0x0e, + 0x48, 0x16, 0xfd, 0x8d, 0xbe, 0x06, 0xc0, 0x91, 0xcf, 0xc6, 0xb1, 0x1a, 0x9a, 0x74, 0xeb, 0x6d, + 0x1c, 0x43, 0x29, 0xa7, 0x9b, 0xdb, 0x13, 0xc5, 0x07, 0xea, 0x2f, 0x77, 0x32, 0x58, 0xf4, 0x00, + 0x96, 0xfa, 0xca, 0x5b, 0x37, 0x93, 0x6c, 0x67, 0x98, 0x94, 0x17, 0x35, 0xba, 0x37, 0x49, 0xb9, + 0xbb, 0x60, 0x96, 0x5c, 0x1c, 0x05, 0x9a, 0xae, 0x7c, 0x7a, 0xba, 0x79, 0x8d, 0xbd, 0x1d, 0x05, + 0x8a, 0x6c, 0x1b, 0x4a, 0xf1, 0x50, 0xd5, 0x1e, 0x3d, 0x5d, 0x5d, 0x3f, 0xf5, 0x9d, 0x6d, 0x0d, + 0xbb, 0x81, 0x19, 0xac, 0x2a, 0x32, 0xbe, 0xb7, 0xee, 0x76, 0xd7, 0xb9, 0x53, 0x8c, 0xe5, 0xf2, + 0x91, 0x6e, 0x0e, 0x3f, 0x51, 0x37, 0x5f, 0xee, 0xc0, 0x87, 0xc7, 0x86, 0xce, 0x31, 0xed, 0xe9, + 0xe4, 0x66, 0x71, 0x03, 0x60, 0xea, 0x4b, 0x16, 0x59, 0x38, 0x06, 0x59, 0xce, 0x20, 0xeb, 0x1c, + 0x16, 0x1d, 0xcc, 0x05, 0x65, 0x58, 0x86, 0x81, 0xca, 0xe2, 0xcf, 0x21, 0x1f, 0x10, 0x66, 0xca, + 0x50, 0xfd, 0x98, 0x80, 0xb9, 0xfd, 0x5c, 0x48, 0x67, 0xc2, 0x9e, 0xa0, 0xcc, 0x1b, 0xa4, 0xbf, + 0x0e, 0x25, 0x48, 0x0e, 0xe0, 0xb1, 0x27, 0x76, 0xb5, 0x85, 0xe9, 0x00, 0x2e, 0x57, 0xb2, 0xbd, + 0xa8, 0x7e, 0x0f, 0x40, 0xf7, 0x49, 0x2c, 0x8d, 0xbb, 0x08, 0x25, 0x1a, 0x06, 0xe9, 0xcf, 0x87, + 0xf9, 0x69, 0xb9, 0x79, 0x10, 0x06, 0xb2, 0xdc, 0xd0, 0x30, 0xe8, 0x06, 0xe8, 0x63, 0x28, 0x47, + 0x78, 0xcf, 0x55, 0x83, 0x82, 0x64, 0x9f, 0x73, 0x66, 0x23, 0xbc, 0x27, 0xe7, 0x81, 0xfa, 0x3f, + 0x2d, 0xa8, 0x19, 0x27, 0x64, 0x21, 0xd0, 0x87, 0xf0, 0x29, 0x14, 0x64, 0x2d, 0x31, 0x6e, 0xbc, + 0xa3, 0x94, 0x28, 0x55, 0x74, 0x1b, 0x8a, 0x3b, 0x44, 0x4e, 0xe9, 0xba, 0xfc, 0xfc, 0xe2, 0x6d, + 0xed, 0xff, 0xd0, 0x91, 0xa5, 0x05, 0x4d, 0xa1, 0xd1, 0x65, 0xa8, 0xa6, 0x33, 0x6b, 0x37, 0x78, + 0x6e, 0x6a, 0xa8, 0xd6, 0xc8, 0x0a, 0xea, 0xff, 0xcb, 0x4d, 0xce, 0x7e, 0x52, 0x41, 0x37, 0x60, + 0x8e, 0xe9, 0x25, 0x9d, 0x15, 0x67, 0xe8, 0x05, 0x55, 0x03, 0x54, 0x39, 0x71, 0x38, 0xf7, 0x73, + 0x3f, 0x22, 0xf7, 0xdb, 0x50, 0x62, 0x58, 0x8d, 0xd8, 0x79, 0x75, 0x2a, 0x17, 0x4f, 0x3e, 0x95, + 0xe9, 0x9d, 0xa6, 0x3f, 0x93, 0x34, 0x52, 0xfe, 0xfe, 0x31, 0x19, 0xaa, 0xab, 0xea, 0xaf, 0xde, + 0x79, 0xb2, 0xa7, 0x4a, 0xd1, 0x1f, 0x11, 0xf6, 0x7f, 0xcb, 0xc1, 0xb9, 0x5e, 0x1c, 0x12, 0x71, + 0x2b, 0x0a, 0x7a, 0xbe, 0x27, 0x84, 0x69, 0xc9, 0x7f, 0x80, 0x92, 0x7a, 0x18, 0x48, 0x5b, 0xd8, + 0xcd, 0x93, 0x2d, 0x3d, 0x06, 0x9e, 0x5a, 0xaf, 0xec, 0xe9, 0x48, 0x9e, 0xf4, 0x20, 0x34, 0x69, + 0xe6, 0x30, 0x73, 0xef, 0x7b, 0x98, 0xcb, 0x2e, 0x2c, 0xbd, 0xb1, 0x0d, 0xda, 0x84, 0x59, 0x2c, + 0x7f, 0x07, 0xe3, 0xd4, 0xf0, 0x2b, 0xef, 0x3c, 0xe2, 0x49, 0xaa, 0x18, 0xfe, 0x94, 0xa0, 0xfe, + 0xf7, 0x3c, 0xcc, 0x77, 0x7a, 0x8f, 0x1e, 0x33, 0x92, 0x9e, 0xca, 0x65, 0xd9, 0x58, 0xb9, 0x20, + 0x91, 0x7e, 0x7c, 0xb1, 0x32, 0xc9, 0x9d, 0x15, 0xa0, 0x4f, 0x60, 0x4e, 0xd6, 0x38, 0x37, 0x56, + 0x27, 0x12, 0x1d, 0xaa, 0x02, 0x55, 0x55, 0xfd, 0xb4, 0x00, 0x7d, 0x09, 0xb3, 0x54, 0xc7, 0x9a, + 0x4a, 0x8f, 0xea, 0xb1, 0xad, 0xae, 0xd3, 0x7b, 0x64, 0x02, 0x32, 0xb5, 0xd0, 0x60, 0xa6, 0xcf, + 0x3a, 0x8c, 0xee, 0x71, 0x33, 0x88, 0x65, 0x9f, 0x75, 0x1c, 0xba, 0xc7, 0x8f, 0xbc, 0xfd, 0xcc, + 0x1e, 0xff, 0xf6, 0xf3, 0x7b, 0x58, 0xf2, 0xe9, 0x28, 0x96, 0x29, 0x29, 0x47, 0x4e, 0x9f, 0x06, + 0xd8, 0x37, 0xdd, 0xf7, 0x2d, 0xe9, 0x2f, 0xb3, 0xa6, 0x33, 0x85, 0xa5, 0xc3, 0x58, 0x86, 0xa9, + 0x23, 0x89, 0x8e, 0xb4, 0x90, 0xd2, 0x4f, 0xd4, 0x42, 0xea, 0x8f, 0x61, 0xa9, 0x9d, 0x84, 0xd2, + 0xeb, 0xcc, 0x9d, 0x4d, 0x9e, 0xe9, 0xac, 0xf7, 0x7e, 0xa6, 0xbb, 0x72, 0x09, 0x16, 0x8f, 0xb8, + 0x8a, 0xca, 0x50, 0xb8, 0x4f, 0x23, 0x5c, 0x9b, 0x91, 0x5f, 0x77, 0x5e, 0x90, 0xb8, 0x66, 0xb5, + 0xaf, 0xbe, 0xfc, 0xcf, 0xca, 0xcc, 0xcb, 0x83, 0x15, 0xeb, 0xbb, 0x83, 0x15, 0xeb, 0xfb, 0x83, + 0x15, 0xeb, 0xdf, 0x07, 0x2b, 0xd6, 0x5f, 0x5f, 0xad, 0xcc, 0x7c, 0xf7, 0x6a, 0x65, 0xe6, 0xfb, + 0x57, 0x2b, 0x33, 0xdf, 0x56, 0x33, 0x2f, 0xa1, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xf4, 0x86, + 0x91, 0xe7, 0xb6, 0x15, 0x00, 0x00, } func (m *BackfillerSpec) Marshal() (dAtA []byte, err error) { @@ -951,6 +949,20 @@ func (m *BackfillerSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Spans) > 0 { + for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + } { size, err := m.ReadAsOf.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -984,20 +996,6 @@ func (m *BackfillerSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(m.Duration)) i-- dAtA[i] = 0x20 - if len(m.Spans) > 0 { - for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } { size, err := m.Table.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -1921,12 +1919,6 @@ func (m *BackfillerSpec) Size() (n int) { n += 1 + sovProcessorsBulkIo(uint64(m.Type)) l = m.Table.Size() n += 1 + l + sovProcessorsBulkIo(uint64(l)) - if len(m.Spans) > 0 { - for _, e := range m.Spans { - l = e.Size() - n += 1 + l + sovProcessorsBulkIo(uint64(l)) - } - } n += 1 + sovProcessorsBulkIo(uint64(m.Duration)) n += 1 + sovProcessorsBulkIo(uint64(m.ChunkSize)) l = m.WriteAsOf.Size() @@ -1938,6 +1930,12 @@ func (m *BackfillerSpec) Size() (n int) { } l = m.ReadAsOf.Size() n += 1 + l + sovProcessorsBulkIo(uint64(l)) + if len(m.Spans) > 0 { + for _, e := range m.Spans { + l = e.Size() + n += 1 + l + sovProcessorsBulkIo(uint64(l)) + } + } return n } @@ -2357,40 +2355,6 @@ func (m *BackfillerSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProcessorsBulkIo - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProcessorsBulkIo - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProcessorsBulkIo - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Spans = append(m.Spans, TableReaderSpan{}) - if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Duration", wireType) @@ -2571,6 +2535,40 @@ func (m *BackfillerSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Spans = append(m.Spans, roachpb.Span{}) + if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProcessorsBulkIo(dAtA[iNdEx:]) diff --git a/pkg/sql/execinfrapb/processors_bulk_io.proto b/pkg/sql/execinfrapb/processors_bulk_io.proto index f3023b27218c..d009574076ed 100644 --- a/pkg/sql/execinfrapb/processors_bulk_io.proto +++ b/pkg/sql/execinfrapb/processors_bulk_io.proto @@ -46,7 +46,8 @@ message BackfillerSpec { optional sqlbase.TableDescriptor table = 2 [(gogoproto.nullable) = false]; // Sections of the table to be backfilled. - repeated TableReaderSpan spans = 3 [(gogoproto.nullable) = false]; + reserved 3; + repeated roachpb.Span spans = 10 [(gogoproto.nullable) = false]; // Run the backfill for approximately this duration. // The backfill will always process at least one backfill chunk. diff --git a/pkg/sql/execinfrapb/processors_sql.pb.go b/pkg/sql/execinfrapb/processors_sql.pb.go index 8908b000e6a0..fece8cc06f14 100644 --- a/pkg/sql/execinfrapb/processors_sql.pb.go +++ b/pkg/sql/execinfrapb/processors_sql.pb.go @@ -9,6 +9,7 @@ package execinfrapb import ( fmt "fmt" + roachpb "github.com/cockroachdb/cockroach/pkg/roachpb" descpb "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" inverted "github.com/cockroachdb/cockroach/pkg/sql/inverted" types "github.com/cockroachdb/cockroach/pkg/sql/types" @@ -566,9 +567,9 @@ type TableReaderSpec struct { Table descpb.TableDescriptor `protobuf:"bytes,1,opt,name=table" json:"table"` // If 0, we use the primary index. If non-zero, we use the index_idx-th index, // i.e. table.indexes[index_idx-1] - IndexIdx uint32 `protobuf:"varint,2,opt,name=index_idx,json=indexIdx" json:"index_idx"` - Reverse bool `protobuf:"varint,3,opt,name=reverse" json:"reverse"` - Spans []TableReaderSpan `protobuf:"bytes,4,rep,name=spans" json:"spans"` + IndexIdx uint32 `protobuf:"varint,2,opt,name=index_idx,json=indexIdx" json:"index_idx"` + Reverse bool `protobuf:"varint,3,opt,name=reverse" json:"reverse"` + Spans []roachpb.Span `protobuf:"bytes,18,rep,name=spans" json:"spans"` // A hint for how many rows the consumer of the table reader output might // need. This is used to size the initial KV batches to try to avoid reading // many more rows than needed by the processor receiving the output. @@ -716,8 +717,8 @@ type IndexSkipTableReaderSpec struct { Table descpb.TableDescriptor `protobuf:"bytes,1,opt,name=table" json:"table"` // If 0, we use the primary index. If non-zero, we use the index_idx-th index, // i.e. table.indexes[index_idx-1] - IndexIdx uint32 `protobuf:"varint,2,opt,name=index_idx,json=indexIdx" json:"index_idx"` - Spans []TableReaderSpan `protobuf:"bytes,3,rep,name=spans" json:"spans"` + IndexIdx uint32 `protobuf:"varint,2,opt,name=index_idx,json=indexIdx" json:"index_idx"` + Spans []roachpb.Span `protobuf:"bytes,8,rep,name=spans" json:"spans"` // Indicates the visibility level of the columns that should be returned. // Normally, will be set to PUBLIC. Will be set to PUBLIC_AND_NOT_PUBLIC if // the consumer of this TableReader expects to be able to see in-progress @@ -2005,196 +2006,197 @@ func init() { } var fileDescriptor_90493aed97b6d28f = []byte{ - // 3020 bytes of a gzipped FileDescriptorProto + // 3039 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3a, 0x4b, 0x6f, 0x1b, 0xd7, - 0xd5, 0xe2, 0x4b, 0x22, 0x0f, 0x5f, 0x57, 0xd7, 0x76, 0xcc, 0x28, 0xf9, 0x64, 0x99, 0x76, 0x6c, - 0xd9, 0x71, 0xe4, 0x44, 0xdf, 0x87, 0x7c, 0x48, 0xfa, 0x40, 0x29, 0x72, 0x48, 0x53, 0xa6, 0x66, - 0xe8, 0x21, 0x29, 0xc9, 0x09, 0xd0, 0x8b, 0x11, 0x79, 0x45, 0x4d, 0x4c, 0xce, 0x50, 0x33, 0x43, - 0x4b, 0xca, 0x0f, 0x68, 0x57, 0x05, 0x5a, 0x74, 0x53, 0x64, 0x51, 0x64, 0xd3, 0x4d, 0x37, 0xdd, - 0xf4, 0x27, 0x14, 0x85, 0x97, 0x59, 0xb5, 0x59, 0x15, 0x8d, 0x03, 0x74, 0xd1, 0x3f, 0x50, 0x74, - 0x57, 0xdc, 0xc7, 0x8c, 0x86, 0x8a, 0xe8, 0x86, 0xb6, 0x13, 0x6f, 0x0c, 0xdd, 0xf3, 0xba, 0xe7, - 0x7d, 0xce, 0x1d, 0x1a, 0xae, 0xbb, 0x87, 0x83, 0xbb, 0xf4, 0x98, 0x76, 0x4d, 0x6b, 0xdf, 0x31, - 0x46, 0x7b, 0x77, 0x47, 0x8e, 0xdd, 0xa5, 0xae, 0x6b, 0x3b, 0x2e, 0x71, 0x0f, 0x07, 0x6b, 0x23, - 0xc7, 0xf6, 0x6c, 0x5c, 0xe8, 0xda, 0xdd, 0x47, 0x8e, 0x6d, 0x74, 0x0f, 0xd6, 0x18, 0xb0, 0x67, - 0xba, 0x9e, 0x7b, 0x38, 0x70, 0xc6, 0xd6, 0xd2, 0x35, 0xc6, 0xdf, 0x35, 0x3c, 0x63, 0x60, 0xf7, - 0xef, 0xf6, 0xa8, 0xdb, 0x1d, 0xed, 0xdd, 0x75, 0x3d, 0x67, 0xdc, 0xf5, 0xc6, 0x0e, 0xed, 0x09, - 0xf6, 0xa5, 0xe2, 0x39, 0x44, 0x9f, 0xd8, 0xa6, 0x45, 0xbc, 0x93, 0x11, 0x95, 0x34, 0x2b, 0xe7, - 0xd0, 0x0c, 0xec, 0xee, 0x23, 0xd3, 0xea, 0x4b, 0x8a, 0x4b, 0x8c, 0x82, 0x71, 0xb8, 0xe2, 0x5f, - 0x09, 0x5e, 0x3a, 0x6b, 0x41, 0xcf, 0xf0, 0x0c, 0x89, 0x7b, 0xeb, 0x19, 0xd6, 0xed, 0x19, 0x2e, - 0x0d, 0xeb, 0x67, 0x5a, 0x8f, 0xa9, 0xe3, 0xd1, 0xde, 0x5d, 0x77, 0x64, 0x58, 0x84, 0x1e, 0x8f, - 0x1c, 0xea, 0xba, 0xa6, 0x6d, 0x49, 0x9a, 0x8b, 0x7d, 0xbb, 0x6f, 0xf3, 0x3f, 0xef, 0xb2, 0xbf, - 0x04, 0xb4, 0xf8, 0xeb, 0x08, 0xe4, 0xb6, 0x8d, 0xc1, 0x98, 0xba, 0x65, 0xdb, 0xa1, 0xad, 0x11, - 0xed, 0xe2, 0x32, 0x2c, 0x74, 0xed, 0xc1, 0x78, 0x68, 0xb9, 0x85, 0xc8, 0x4a, 0x6c, 0x35, 0xbd, - 0x7e, 0x6d, 0x6d, 0x9a, 0xf7, 0xd6, 0x2a, 0x86, 0x37, 0x1e, 0xd6, 0xad, 0x7d, 0x7b, 0x23, 0xfe, - 0xe4, 0x6f, 0x57, 0xe6, 0x74, 0x9f, 0x13, 0x5f, 0x81, 0xa4, 0x35, 0x1e, 0x12, 0xc7, 0x3e, 0x72, - 0x0b, 0xb1, 0x95, 0xc8, 0x6a, 0xdc, 0x27, 0xb0, 0xc6, 0x43, 0xdd, 0x3e, 0x72, 0xf1, 0x1b, 0x90, - 0x72, 0x8c, 0x23, 0xb2, 0x77, 0xe2, 0x51, 0xb7, 0x10, 0x5d, 0x89, 0xad, 0x66, 0xf4, 0xa4, 0x63, - 0x1c, 0x6d, 0xb0, 0x73, 0xf1, 0x67, 0x0b, 0x90, 0x6f, 0x1b, 0x7b, 0x03, 0xaa, 0x53, 0xa3, 0x47, - 0x1d, 0xae, 0xd6, 0x06, 0x24, 0x3c, 0x06, 0x2a, 0x44, 0x56, 0x22, 0xab, 0xe9, 0xf5, 0x1b, 0x67, - 0x94, 0x72, 0x0f, 0x07, 0xdc, 0x21, 0x9c, 0xad, 0x42, 0xdd, 0xae, 0x63, 0x8e, 0x3c, 0xdb, 0x91, - 0xd7, 0x0a, 0x56, 0x7c, 0x15, 0x52, 0xa6, 0xd5, 0xa3, 0xc7, 0xc4, 0xec, 0x1d, 0x17, 0xa2, 0x2b, - 0x91, 0xd5, 0xac, 0xc4, 0x27, 0x39, 0xb8, 0xde, 0x3b, 0xc6, 0xcb, 0xb0, 0xe0, 0xd0, 0xc7, 0xd4, - 0x71, 0x29, 0xd7, 0x3b, 0xe9, 0xeb, 0x2d, 0x81, 0x58, 0x81, 0x04, 0xf3, 0xaf, 0x5b, 0x88, 0x73, - 0xdf, 0xdc, 0x9a, 0xee, 0x9b, 0x09, 0x03, 0x0c, 0xcb, 0xd7, 0x84, 0x73, 0xe3, 0x6b, 0x00, 0x03, - 0x73, 0x68, 0x7a, 0xe4, 0xc0, 0xb4, 0xbc, 0x42, 0x62, 0x25, 0xb2, 0x1a, 0x93, 0x04, 0x29, 0x0e, - 0xbf, 0x67, 0x5a, 0x1e, 0x73, 0xa2, 0xe9, 0x92, 0xee, 0x01, 0xed, 0x3e, 0x2a, 0xcc, 0x87, 0x95, - 0x31, 0xdd, 0x32, 0x03, 0x62, 0x15, 0xe0, 0xb1, 0xe9, 0x9a, 0x7b, 0xe6, 0xc0, 0xf4, 0x4e, 0x0a, - 0x0b, 0x2b, 0x91, 0xd5, 0xdc, 0xfa, 0xea, 0x74, 0x8d, 0x5a, 0x5d, 0xc3, 0xda, 0x0e, 0xe8, 0xa5, - 0xb0, 0x90, 0x04, 0x7c, 0x03, 0xd2, 0x23, 0xc3, 0x31, 0x06, 0x03, 0x3a, 0x30, 0x3f, 0xa5, 0x85, - 0x4c, 0xe8, 0xce, 0x30, 0x02, 0xbf, 0x0b, 0x8b, 0x7b, 0x86, 0xd7, 0x3d, 0x10, 0xe1, 0x23, 0x5c, - 0xe3, 0xc2, 0x62, 0xc8, 0x88, 0x3c, 0x47, 0xf3, 0x60, 0x36, 0x18, 0x12, 0xff, 0x00, 0x2e, 0x0f, - 0x8d, 0x63, 0xe2, 0x99, 0x43, 0xea, 0x7a, 0xc6, 0x70, 0x44, 0x8c, 0x3e, 0x25, 0x96, 0x61, 0xd9, - 0x6e, 0x21, 0x15, 0x4a, 0x8f, 0x8b, 0x43, 0xe3, 0xb8, 0xed, 0xd3, 0x94, 0xfa, 0x54, 0x65, 0x14, - 0xf8, 0x63, 0x40, 0xb2, 0x92, 0x88, 0xeb, 0x39, 0xd4, 0xea, 0x7b, 0x07, 0x05, 0xe0, 0xc6, 0xde, - 0x9e, 0x92, 0x05, 0xcc, 0xd2, 0x86, 0x60, 0x69, 0x49, 0x0e, 0x5f, 0xb3, 0xc1, 0x24, 0x18, 0xef, - 0xc1, 0x05, 0x5f, 0xf8, 0x91, 0x61, 0x7a, 0x64, 0x64, 0x0f, 0xcc, 0xee, 0x49, 0x21, 0xcd, 0xe5, - 0xdf, 0xf9, 0xef, 0xf2, 0x77, 0x0c, 0xd3, 0x6b, 0x72, 0x1e, 0x79, 0xc3, 0xe2, 0xe0, 0x2c, 0x02, - 0xaf, 0x03, 0x3e, 0x30, 0x5c, 0xe2, 0x9e, 0xb8, 0x1e, 0x1d, 0x12, 0xbf, 0xba, 0x72, 0x21, 0xf7, - 0xa2, 0x03, 0xc3, 0x6d, 0x71, 0x74, 0x59, 0x56, 0xd0, 0x5b, 0x90, 0xb3, 0x28, 0xed, 0xd1, 0x5e, - 0x40, 0x9f, 0x5f, 0x89, 0xad, 0x66, 0xf5, 0xac, 0x80, 0xfa, 0x64, 0x4d, 0xc8, 0xfb, 0x85, 0x2f, - 0x09, 0x0b, 0x88, 0x17, 0xc8, 0xcd, 0x29, 0xaa, 0x0b, 0xc6, 0xd3, 0x0a, 0xd1, 0x73, 0x3e, 0xbf, - 0xc0, 0x6c, 0xc6, 0x93, 0x49, 0x94, 0xda, 0x8c, 0x27, 0xb3, 0x28, 0x57, 0xd4, 0x21, 0x53, 0x35, - 0x07, 0x1e, 0x75, 0x82, 0x22, 0x9c, 0xdf, 0xe7, 0x67, 0x59, 0x85, 0xd7, 0xa7, 0x27, 0x9b, 0x12, - 0x34, 0x20, 0x69, 0xa2, 0xe4, 0x2c, 0xfe, 0x3b, 0x06, 0x85, 0x3a, 0x2b, 0xb7, 0xd6, 0x23, 0x73, - 0xf4, 0x8a, 0xaa, 0x3c, 0xa8, 0xe2, 0xd8, 0x0b, 0x55, 0xf1, 0x64, 0xfd, 0xc5, 0x5f, 0xb8, 0xfe, - 0x42, 0xcd, 0x27, 0x71, 0x5e, 0xf3, 0x39, 0xaf, 0x10, 0xe6, 0xbf, 0xe3, 0x42, 0x58, 0x78, 0x89, - 0x85, 0x50, 0xfc, 0x47, 0x12, 0x72, 0x9b, 0xb6, 0x69, 0x7d, 0xff, 0x11, 0xbf, 0x0f, 0xe9, 0x81, - 0x6d, 0x3f, 0x1a, 0x8f, 0xf8, 0x64, 0x94, 0x35, 0x32, 0x4b, 0xfa, 0x82, 0x60, 0x67, 0x70, 0xbc, - 0x0b, 0xd8, 0xa1, 0x43, 0xdb, 0xa3, 0x24, 0x2c, 0x73, 0x71, 0x66, 0x99, 0x48, 0x48, 0x69, 0x9c, - 0x4a, 0xbe, 0x05, 0x39, 0x29, 0xd2, 0xaf, 0x7a, 0x96, 0xa1, 0xd9, 0x8d, 0x28, 0x8a, 0xe8, 0x59, - 0x81, 0xf1, 0x2b, 0xff, 0x03, 0x78, 0x6d, 0x92, 0x94, 0x18, 0x0e, 0x25, 0x8f, 0xe8, 0x49, 0x21, - 0x19, 0xca, 0x9d, 0x0b, 0x13, 0x4c, 0x25, 0x87, 0xde, 0xa7, 0x27, 0x6c, 0xc4, 0xdb, 0x62, 0x45, - 0xe0, 0x49, 0x3b, 0x63, 0x1d, 0xdb, 0x16, 0x57, 0xf5, 0x03, 0x88, 0xb3, 0x35, 0x46, 0x26, 0xe0, - 0x95, 0x29, 0x71, 0x63, 0xd1, 0x6e, 0x9f, 0x8c, 0xa8, 0x64, 0xe6, 0x2c, 0x2f, 0x7d, 0x6e, 0x9d, - 0x57, 0x17, 0xa9, 0xef, 0xb8, 0x2e, 0xe0, 0x65, 0x0e, 0x88, 0xf7, 0x60, 0x71, 0x68, 0x98, 0x96, - 0x67, 0x98, 0x16, 0xb1, 0x9d, 0x1e, 0x75, 0x4c, 0xab, 0xcf, 0x47, 0x50, 0x30, 0x1f, 0x7c, 0xb4, - 0x26, 0xb1, 0x53, 0x66, 0x4a, 0xf6, 0x99, 0x33, 0xa5, 0x02, 0x6f, 0x0e, 0xe8, 0xbe, 0x47, 0xf8, - 0xee, 0x7a, 0x64, 0x7a, 0x07, 0x64, 0x64, 0x98, 0x0e, 0xed, 0x71, 0x00, 0x75, 0x26, 0x26, 0x52, - 0x81, 0x51, 0xb2, 0xe8, 0xed, 0x98, 0xde, 0x41, 0x93, 0x93, 0x6d, 0x72, 0x2a, 0xbc, 0x03, 0x37, - 0xec, 0xb1, 0x37, 0x1a, 0x7b, 0xa4, 0xef, 0xd8, 0x3c, 0xfd, 0x2c, 0xcf, 0xb4, 0xc6, 0x86, 0x67, - 0xda, 0x16, 0xd9, 0xb7, 0x1d, 0xc2, 0xef, 0x70, 0xec, 0xa3, 0x42, 0x3e, 0x24, 0xef, 0xaa, 0xe0, - 0xa9, 0x31, 0x96, 0x72, 0x88, 0xa3, 0x6a, 0x3b, 0x0d, 0xba, 0xef, 0xe9, 0xf6, 0x11, 0xfe, 0x11, - 0x14, 0x64, 0x46, 0x7f, 0x73, 0xbb, 0xc0, 0xa1, 0xed, 0xe2, 0x92, 0xa0, 0xda, 0x98, 0xdc, 0x31, - 0x36, 0xe3, 0xc9, 0x04, 0x9a, 0xdf, 0x8c, 0x27, 0x33, 0x28, 0x5b, 0xfc, 0x43, 0x04, 0xa0, 0x65, - 0x3b, 0x9e, 0x6c, 0x32, 0x0f, 0x20, 0x2f, 0x55, 0x0e, 0xbc, 0x2b, 0xda, 0x4d, 0x71, 0x7a, 0xd6, - 0xf9, 0x9e, 0x96, 0x97, 0xe6, 0x84, 0x80, 0xb0, 0xff, 0x7d, 0x59, 0x64, 0xc8, 0xd5, 0x1d, 0x50, - 0x6b, 0xa2, 0xf9, 0x20, 0x1f, 0xbf, 0xc5, 0xd0, 0x0d, 0x6a, 0xe1, 0x25, 0x48, 0x08, 0x6b, 0x62, - 0x21, 0x6b, 0x04, 0xa8, 0xf8, 0xab, 0x28, 0x64, 0x2a, 0xa6, 0xeb, 0x99, 0x56, 0xd7, 0xe3, 0x3a, - 0xdf, 0x84, 0x3c, 0x17, 0x10, 0xda, 0x00, 0x22, 0x7c, 0x03, 0xc8, 0x49, 0xb0, 0x1f, 0xd5, 0x5b, - 0x80, 0x7a, 0x92, 0x31, 0xa0, 0x8c, 0x72, 0xca, 0xbc, 0x0f, 0xf7, 0x49, 0xd7, 0x01, 0x5b, 0xe3, - 0xc1, 0x40, 0xb4, 0x0a, 0x1f, 0x39, 0xb1, 0xe8, 0x22, 0x8e, 0x2f, 0x39, 0xd4, 0xd7, 0x05, 0xdf, - 0x80, 0x0c, 0x75, 0x1c, 0xdb, 0x21, 0xb6, 0x45, 0x7a, 0xe3, 0x11, 0xef, 0x18, 0x29, 0xbf, 0x08, - 0x39, 0x46, 0xb3, 0x2a, 0xe3, 0xd1, 0x79, 0x3e, 0x4e, 0xbc, 0x98, 0x8f, 0x8b, 0x08, 0x72, 0x9a, - 0xd3, 0x33, 0x2d, 0x83, 0x55, 0x39, 0x73, 0x4a, 0xf1, 0x37, 0x31, 0x40, 0x1f, 0x99, 0xfd, 0x4f, - 0x8d, 0xbe, 0x48, 0x46, 0xee, 0xa9, 0x0a, 0xcc, 0xf3, 0x39, 0xe0, 0x3f, 0x58, 0x66, 0x9b, 0x21, - 0x92, 0x17, 0x57, 0x01, 0xe8, 0xe1, 0x84, 0x03, 0xd3, 0xeb, 0x57, 0xa7, 0xab, 0x2e, 0x5d, 0xea, - 0x6f, 0xed, 0xf4, 0xf0, 0x34, 0x1c, 0x39, 0x31, 0x8c, 0x6c, 0xa1, 0xfa, 0x44, 0x0b, 0xe7, 0x18, - 0x69, 0x93, 0xfb, 0x72, 0xfa, 0xf0, 0x7d, 0xc8, 0xec, 0x9b, 0xc7, 0xb4, 0x47, 0x1e, 0xf3, 0x77, - 0x5c, 0x21, 0xc1, 0x35, 0x7f, 0x46, 0x3b, 0x9d, 0x7c, 0xef, 0xe9, 0x69, 0xce, 0x2d, 0x80, 0x2f, - 0xd0, 0xd4, 0x8b, 0x7f, 0x89, 0x41, 0x7e, 0x8b, 0x3a, 0x7d, 0x1a, 0x8a, 0xcc, 0x16, 0x64, 0x79, - 0x33, 0x78, 0xee, 0xaa, 0xcb, 0x30, 0xf6, 0xa0, 0xe6, 0x34, 0xc8, 0x39, 0x66, 0xff, 0x20, 0x24, - 0x2f, 0x3a, 0xa3, 0xbc, 0x2c, 0xe7, 0x0f, 0x04, 0x86, 0x02, 0x90, 0x78, 0x15, 0x83, 0xf0, 0x16, - 0x64, 0x59, 0xbd, 0x11, 0x7a, 0x38, 0x36, 0x82, 0x59, 0xe8, 0x97, 0x62, 0x86, 0xa1, 0x14, 0x89, - 0xc1, 0x1f, 0xc2, 0x65, 0xee, 0xca, 0xd3, 0x1c, 0x9d, 0x32, 0xef, 0xe9, 0xbe, 0xa7, 0x1c, 0x4e, - 0xce, 0xfb, 0x1f, 0x42, 0x41, 0xf8, 0xed, 0x1c, 0xe6, 0x54, 0x88, 0xf9, 0x22, 0xa7, 0x3a, 0xc3, - 0x5d, 0xfc, 0x67, 0x14, 0x72, 0xf7, 0x0c, 0xf7, 0x20, 0x14, 0xd7, 0xdb, 0x90, 0x3f, 0xa3, 0x8c, - 0xe8, 0x4d, 0x72, 0x4f, 0x09, 0xab, 0x80, 0xef, 0x00, 0x3a, 0x7b, 0xb9, 0x68, 0x4f, 0x9c, 0x38, - 0x37, 0x79, 0xe5, 0x2b, 0x8f, 0xc8, 0x2b, 0x73, 0xf3, 0x66, 0x3c, 0xb9, 0x80, 0x92, 0xc5, 0xcf, - 0xe2, 0x80, 0xeb, 0xf2, 0x41, 0x16, 0x72, 0xf8, 0xf7, 0xb4, 0x25, 0x6b, 0x90, 0x0d, 0x5e, 0x93, - 0xcf, 0xd9, 0x96, 0x32, 0xbe, 0x00, 0x1e, 0x89, 0x57, 0x1d, 0xce, 0x73, 0x17, 0xab, 0x85, 0x67, - 0x2e, 0x56, 0xdf, 0x7e, 0xbd, 0x49, 0xce, 0xb6, 0xde, 0x7c, 0x08, 0x97, 0x47, 0x0e, 0xdd, 0x37, - 0x8f, 0x83, 0x72, 0x0f, 0xea, 0x21, 0x15, 0xd4, 0xc3, 0x25, 0x41, 0xe2, 0x97, 0xbd, 0x4c, 0x91, - 0xcd, 0x78, 0x32, 0x86, 0xe2, 0xc5, 0xcf, 0x62, 0x70, 0xd1, 0x4f, 0x8e, 0x89, 0x77, 0xf9, 0x1a, - 0xa0, 0xf0, 0x57, 0x00, 0x1e, 0xe1, 0x48, 0x28, 0xc2, 0xe1, 0x37, 0x3e, 0x8b, 0xf3, 0xf6, 0xd9, - 0x38, 0x8b, 0x3e, 0xfa, 0xf6, 0x19, 0xd7, 0xfa, 0x34, 0x6b, 0xec, 0xe9, 0x7b, 0x1a, 0x9e, 0xa6, - 0x63, 0x7b, 0xf6, 0xb9, 0xe1, 0xfe, 0x04, 0x16, 0x47, 0x0e, 0x25, 0xfb, 0x52, 0x37, 0xe2, 0x8e, - 0x68, 0x97, 0x87, 0x2d, 0xbd, 0xfe, 0xe3, 0xe9, 0x81, 0x3f, 0xcf, 0xa4, 0xb5, 0xa6, 0x43, 0xc3, - 0x67, 0x3d, 0x3f, 0x9a, 0x04, 0x2c, 0xfd, 0x22, 0x02, 0xf9, 0x33, 0x44, 0x78, 0x13, 0xe0, 0xf4, - 0xc3, 0xe7, 0x73, 0x7c, 0xa3, 0x08, 0x71, 0xe3, 0x35, 0x99, 0x75, 0xc2, 0x35, 0x4b, 0x67, 0xb3, - 0x8e, 0x0e, 0xd7, 0xc4, 0x57, 0xdc, 0xb6, 0x9c, 0x7f, 0x7f, 0x06, 0xc8, 0x95, 0xfa, 0x7d, 0x87, - 0xf6, 0x0d, 0xcf, 0x16, 0xea, 0x5c, 0x05, 0xf0, 0x73, 0x68, 0x10, 0x6e, 0x7a, 0xa9, 0xbe, 0x48, - 0x93, 0x81, 0x8b, 0x7f, 0x0a, 0x19, 0x43, 0x32, 0x99, 0x76, 0xf0, 0x41, 0xe2, 0xff, 0xa6, 0xeb, - 0x3c, 0x79, 0x45, 0x70, 0x0c, 0x15, 0x60, 0x58, 0x1e, 0x7e, 0x57, 0xae, 0xa9, 0xb4, 0x47, 0x42, - 0xaa, 0xc4, 0x03, 0x55, 0x90, 0xc4, 0xd6, 0x02, 0x8d, 0x6a, 0xd2, 0xee, 0x04, 0xaf, 0xb6, 0x77, - 0xbe, 0xb5, 0x26, 0xdf, 0xa8, 0xbd, 0x73, 0x16, 0xc2, 0xf9, 0x17, 0x5b, 0x08, 0x97, 0x7e, 0x1e, - 0x85, 0x74, 0xc8, 0x62, 0xa6, 0xeb, 0xfe, 0xd8, 0xea, 0xf2, 0x48, 0xcf, 0xa2, 0x6b, 0x75, 0x6c, - 0x75, 0x7d, 0x5d, 0x99, 0x00, 0xbc, 0x02, 0xc9, 0x60, 0x1d, 0x8e, 0x86, 0xca, 0x3a, 0x80, 0xe2, - 0xcb, 0xfc, 0xb3, 0x38, 0xaf, 0xac, 0x04, 0x5f, 0xae, 0xe7, 0xbb, 0xa2, 0x96, 0xae, 0x43, 0x4e, - 0xe4, 0x7b, 0x50, 0x79, 0xac, 0x69, 0x66, 0xf5, 0x8c, 0x80, 0xca, 0x8a, 0xbb, 0x07, 0x29, 0xc3, - 0xe9, 0x8f, 0x87, 0xd4, 0xf2, 0xdc, 0xc2, 0x3c, 0x0f, 0xf2, 0x2c, 0x89, 0x79, 0xca, 0x2c, 0x5b, - 0xc1, 0xef, 0x13, 0x10, 0x67, 0x56, 0x60, 0x04, 0x99, 0x92, 0xfa, 0x90, 0xa8, 0x5a, 0x9b, 0xa8, - 0x9d, 0x46, 0x03, 0xcd, 0xe1, 0x05, 0x88, 0x95, 0xb6, 0x6b, 0x28, 0x82, 0x33, 0x90, 0xdc, 0xd0, - 0xb4, 0x06, 0x29, 0xa9, 0x15, 0x14, 0xc5, 0x69, 0x58, 0xe0, 0x27, 0x4d, 0x47, 0x31, 0x9c, 0x03, - 0x28, 0x6b, 0x6a, 0xb9, 0xd4, 0x26, 0xa5, 0x5a, 0x0d, 0xc5, 0x71, 0x0a, 0x12, 0x65, 0xad, 0xa3, - 0xb6, 0x51, 0x82, 0xb1, 0x6f, 0x95, 0x76, 0xd1, 0x02, 0xff, 0xa3, 0xae, 0xa2, 0x24, 0x06, 0x98, - 0x6f, 0xb5, 0x2b, 0x15, 0x65, 0x1b, 0xa5, 0x18, 0xb0, 0xd5, 0xd9, 0x42, 0xc0, 0xc4, 0xb5, 0x3a, - 0x5b, 0xa4, 0xae, 0xb6, 0x51, 0x9a, 0xdd, 0xb4, 0x5d, 0xd2, 0xeb, 0x25, 0xb5, 0xac, 0xa0, 0x0c, - 0x43, 0xed, 0x6a, 0x3a, 0x97, 0x9c, 0x15, 0x37, 0x75, 0xd4, 0x36, 0xd1, 0xb5, 0x9d, 0x16, 0xca, - 0x71, 0xbe, 0x07, 0x7a, 0xa5, 0x5e, 0xad, 0xa2, 0x3c, 0xc6, 0x90, 0xab, 0xd6, 0xd5, 0x52, 0x83, - 0x04, 0xdc, 0x88, 0x19, 0x24, 0x60, 0xf2, 0xce, 0x45, 0x9c, 0x85, 0x54, 0x49, 0xd7, 0x4b, 0x0f, - 0xb9, 0x44, 0xcc, 0x2e, 0xdb, 0x6c, 0x69, 0x2a, 0x3f, 0x5d, 0x60, 0x48, 0x76, 0xda, 0xe0, 0xc7, - 0x8b, 0xec, 0xba, 0x56, 0x5b, 0xaf, 0xab, 0x35, 0x7e, 0xbe, 0xc4, 0xad, 0xae, 0xb7, 0xb9, 0x0b, - 0x5e, 0x63, 0x86, 0xb0, 0x83, 0xa6, 0xa3, 0xcb, 0x38, 0x09, 0xf1, 0xb2, 0xa6, 0xeb, 0xa8, 0x80, - 0x0b, 0x70, 0xb1, 0xa9, 0xe8, 0x65, 0x45, 0x6d, 0xd7, 0x1b, 0x0a, 0xa9, 0xd4, 0x5b, 0x65, 0x52, - 0xdf, 0x6a, 0x36, 0xd0, 0xeb, 0x67, 0x30, 0x65, 0x4d, 0x6d, 0x0b, 0xcc, 0x12, 0xbe, 0x00, 0x79, - 0xae, 0x83, 0xb6, 0xb1, 0xa9, 0x94, 0x85, 0x13, 0xdf, 0xc0, 0x17, 0x01, 0x09, 0x55, 0x42, 0xd0, - 0x37, 0x99, 0x06, 0xdb, 0x25, 0x9d, 0x34, 0xb5, 0x26, 0xfa, 0x1f, 0xa1, 0x1e, 0x33, 0x8b, 0x9f, - 0x97, 0x71, 0x1e, 0xd2, 0xad, 0x36, 0xd9, 0x2a, 0xdd, 0x57, 0x1a, 0x75, 0x55, 0x41, 0x57, 0x98, - 0x39, 0xad, 0x36, 0x51, 0x76, 0xdb, 0x8a, 0xda, 0x46, 0x2b, 0xcc, 0xd6, 0x56, 0x9b, 0x74, 0xd4, - 0xba, 0xa6, 0xa2, 0xab, 0x82, 0x9b, 0x94, 0xb5, 0x46, 0x43, 0x29, 0xb7, 0x51, 0x91, 0x11, 0x97, - 0x35, 0x5f, 0xf8, 0x35, 0xe1, 0x6a, 0x76, 0x6c, 0x95, 0xb6, 0x9a, 0xe8, 0x3a, 0xf3, 0xae, 0xae, - 0xd4, 0x74, 0x16, 0x23, 0x66, 0x45, 0xb3, 0x8d, 0xde, 0x62, 0xda, 0x70, 0x98, 0xbe, 0x8e, 0x6e, - 0x30, 0x06, 0x7e, 0x68, 0x35, 0xb4, 0xa6, 0x82, 0x6e, 0xb2, 0xdb, 0xc4, 0x79, 0x77, 0x17, 0xad, - 0x9e, 0x9e, 0x1e, 0x3e, 0x44, 0xb7, 0x42, 0xb8, 0x87, 0xe8, 0x76, 0xc0, 0x29, 0x92, 0xe6, 0x6d, - 0xa6, 0x09, 0x3f, 0x97, 0xb6, 0x6b, 0xbb, 0xe8, 0x4e, 0xf8, 0xf8, 0x10, 0xbd, 0x53, 0xbc, 0x03, - 0x71, 0xd6, 0x1d, 0x98, 0xcf, 0x4b, 0x9d, 0xb6, 0x86, 0xe6, 0x78, 0x4a, 0x95, 0x4b, 0x8d, 0x92, - 0x8e, 0x22, 0x4c, 0x96, 0xaa, 0xa9, 0x44, 0x9e, 0xa3, 0xc5, 0x3f, 0x45, 0x20, 0xd7, 0x74, 0xec, - 0x4f, 0x68, 0xd7, 0x6b, 0x51, 0xf1, 0x16, 0xfe, 0x09, 0x24, 0x58, 0x67, 0xf6, 0x1f, 0x78, 0xb3, - 0x54, 0x8e, 0x60, 0xc4, 0x35, 0x58, 0xec, 0x53, 0x8b, 0x3a, 0x86, 0x17, 0x7a, 0x4f, 0x8b, 0x47, - 0xde, 0xb3, 0x5a, 0x3b, 0x0a, 0x98, 0xfc, 0x05, 0xf5, 0x26, 0x20, 0x6b, 0xcc, 0x3f, 0xb8, 0xb8, - 0x64, 0x44, 0x1d, 0xd2, 0xa7, 0x96, 0x78, 0xe0, 0xe9, 0x59, 0x6b, 0x3c, 0x64, 0x1d, 0xb4, 0x49, - 0x9d, 0x1a, 0xb5, 0x8a, 0x5f, 0x65, 0x21, 0xb3, 0x63, 0x5a, 0x3d, 0xfb, 0x48, 0x0e, 0xa7, 0x15, - 0xfe, 0xeb, 0x8a, 0x67, 0xf2, 0x5e, 0x7d, 0x22, 0x1f, 0xf3, 0x61, 0x10, 0x6e, 0x41, 0xea, 0x88, - 0x73, 0x54, 0x03, 0xe5, 0xee, 0x4e, 0x37, 0x35, 0x2c, 0x5c, 0x1e, 0xaa, 0x41, 0xbf, 0x08, 0xe4, - 0x2c, 0xfd, 0x31, 0x22, 0x3b, 0x45, 0x0b, 0xb2, 0xfe, 0x68, 0xa0, 0xd5, 0xe7, 0xed, 0x9a, 0xfa, - 0xa4, 0x0c, 0xfc, 0x00, 0x40, 0x5e, 0xc5, 0x24, 0x46, 0xb9, 0xc4, 0xf7, 0x66, 0xd3, 0x99, 0x49, - 0x0d, 0x09, 0xf9, 0x30, 0xfe, 0xe4, 0xf3, 0x2b, 0x91, 0xa5, 0xcf, 0x17, 0x20, 0x51, 0x75, 0x8c, - 0x21, 0xc5, 0xf7, 0x21, 0x3e, 0xb4, 0x7b, 0x54, 0xaa, 0xfb, 0x6d, 0x85, 0x73, 0xde, 0xb5, 0x2d, - 0xbb, 0x17, 0x0c, 0x25, 0x26, 0x04, 0x3f, 0x80, 0xf9, 0x3d, 0x7b, 0x6c, 0xf5, 0x5c, 0x39, 0xd7, - 0xff, 0x77, 0x26, 0x71, 0x1b, 0x9c, 0xd5, 0x5f, 0x4f, 0x85, 0x20, 0xfc, 0x11, 0xa4, 0xe8, 0x71, - 0x77, 0x30, 0xe6, 0x3b, 0x47, 0x8c, 0x2b, 0xf9, 0xfe, 0x4c, 0x52, 0x15, 0x9f, 0x3b, 0xf8, 0x98, - 0xe0, 0x03, 0x96, 0xfe, 0x15, 0x81, 0x04, 0xbf, 0x94, 0xdd, 0xc2, 0xef, 0x63, 0x85, 0x24, 0x5d, - 0xf1, 0xfe, 0xec, 0xba, 0x87, 0x86, 0xf4, 0xa9, 0x38, 0x7c, 0x0d, 0xc0, 0xb4, 0x3c, 0x62, 0xef, - 0xef, 0xbb, 0x54, 0xcc, 0x3f, 0xff, 0x07, 0xb9, 0x94, 0x69, 0x79, 0x1a, 0x07, 0xe3, 0xab, 0x90, - 0x61, 0x55, 0xd1, 0xf3, 0xc9, 0x98, 0xa5, 0x19, 0x3d, 0xcd, 0x61, 0x92, 0x64, 0x13, 0xd2, 0x02, - 0xc9, 0x7f, 0x18, 0x97, 0x8f, 0x87, 0x19, 0x7e, 0x3e, 0x06, 0xc1, 0xcd, 0x74, 0x5a, 0xfa, 0x6d, - 0x04, 0xe6, 0x85, 0xbb, 0xb1, 0x0a, 0x09, 0xd7, 0x33, 0x1c, 0x4f, 0x2e, 0x74, 0xeb, 0xb3, 0x9b, - 0x1d, 0xfc, 0x6c, 0xc3, 0xc4, 0xe0, 0x0a, 0xc4, 0xa8, 0xd5, 0x93, 0x09, 0xf0, 0x1c, 0xd2, 0x74, - 0xc6, 0x5e, 0xbc, 0x09, 0x71, 0x96, 0x5d, 0x6c, 0x74, 0xea, 0x25, 0xb5, 0xa6, 0xa0, 0x39, 0xd6, - 0xdf, 0xf8, 0x94, 0x8b, 0xb0, 0xfe, 0x56, 0xd3, 0xb5, 0x4e, 0xb3, 0x85, 0xa2, 0xc5, 0x4f, 0x21, - 0x15, 0xf8, 0x1e, 0x5f, 0x86, 0x0b, 0x1d, 0x75, 0x43, 0xeb, 0xa8, 0x15, 0xa5, 0x42, 0x9a, 0xba, - 0x52, 0x56, 0x2a, 0x75, 0xb5, 0x86, 0xe6, 0x26, 0x11, 0x55, 0xad, 0xd1, 0xd0, 0x76, 0x18, 0x22, - 0xc2, 0xa6, 0x8a, 0x56, 0xad, 0xb6, 0x94, 0x76, 0x88, 0x3c, 0x1a, 0x82, 0x9e, 0xd2, 0xc6, 0xd8, - 0x38, 0x29, 0x77, 0x74, 0x5d, 0x11, 0xe3, 0x16, 0xc5, 0x8b, 0x1f, 0x43, 0x2a, 0xc8, 0x2e, 0x36, - 0x59, 0x55, 0x8d, 0x28, 0xbb, 0xe5, 0x46, 0xa7, 0xc5, 0x06, 0x0a, 0xbf, 0x94, 0x1f, 0x2b, 0x0a, - 0x09, 0xf3, 0x45, 0xf0, 0x22, 0x64, 0x7d, 0x04, 0xb7, 0x03, 0x45, 0x19, 0xb7, 0x0f, 0x6a, 0xd7, - 0x95, 0x16, 0x8a, 0x2d, 0xfd, 0x35, 0x0a, 0x49, 0xbf, 0xef, 0x60, 0x25, 0xb4, 0x8a, 0x7d, 0xf3, - 0x25, 0x31, 0xd5, 0xab, 0x67, 0x17, 0xb1, 0x25, 0x48, 0x1a, 0x4e, 0xdf, 0xad, 0xf7, 0x8e, 0xdd, - 0xc2, 0x02, 0xef, 0x90, 0xc1, 0x19, 0x57, 0x20, 0x19, 0x6c, 0x92, 0xf1, 0x19, 0x37, 0xc9, 0x80, - 0x93, 0x3d, 0xa5, 0xf7, 0x59, 0x2c, 0xe5, 0x83, 0xf4, 0xce, 0x2c, 0xf1, 0xd7, 0x05, 0x2b, 0x5e, - 0x85, 0x89, 0xed, 0x8e, 0xef, 0xb5, 0x09, 0x7f, 0xff, 0x9e, 0xd8, 0xfb, 0x56, 0x21, 0x23, 0x76, - 0x58, 0x49, 0x99, 0x0c, 0xbd, 0xca, 0x26, 0x30, 0x9b, 0xf1, 0x64, 0x14, 0xc5, 0xe4, 0x76, 0xf7, - 0xbb, 0x08, 0xc0, 0x69, 0x77, 0xe4, 0xd3, 0x56, 0xdb, 0x21, 0x6a, 0x67, 0x6b, 0x43, 0xd1, 0x65, - 0x9e, 0x95, 0xd4, 0xfb, 0x62, 0x76, 0x56, 0x14, 0xb5, 0xa5, 0x10, 0x7e, 0xe6, 0x41, 0x92, 0x1b, - 0x8b, 0x80, 0xc4, 0xf8, 0x8e, 0xd0, 0xd9, 0xe2, 0x7b, 0x4d, 0x5b, 0x2c, 0x7a, 0x7c, 0x9b, 0x11, - 0x8b, 0x5e, 0xa3, 0x54, 0x43, 0xf3, 0x4c, 0x5c, 0x43, 0x29, 0x55, 0xd0, 0x02, 0xcb, 0x9f, 0x6a, - 0x5d, 0x6f, 0xb5, 0xc9, 0x76, 0xa9, 0xd1, 0x51, 0x50, 0x92, 0xc9, 0x6f, 0x94, 0x82, 0x73, 0x8a, - 0x49, 0x53, 0xdb, 0xf7, 0xe4, 0x11, 0x6e, 0xff, 0x3f, 0xe4, 0x26, 0x7f, 0x9c, 0x61, 0x89, 0xdf, - 0xec, 0x6c, 0x34, 0xea, 0x65, 0x34, 0x87, 0x5f, 0x87, 0x4b, 0xe2, 0x6f, 0xb6, 0x7e, 0xf1, 0x0d, - 0x55, 0xa2, 0x22, 0x1b, 0xef, 0x3c, 0xf9, 0x6a, 0x79, 0xee, 0xc9, 0xd3, 0xe5, 0xc8, 0x17, 0x4f, - 0x97, 0x23, 0x5f, 0x3e, 0x5d, 0x8e, 0xfc, 0xfd, 0xe9, 0x72, 0xe4, 0x97, 0x5f, 0x2f, 0xcf, 0x7d, - 0xf1, 0xf5, 0xf2, 0xdc, 0x97, 0x5f, 0x2f, 0xcf, 0x7d, 0x94, 0x0e, 0xfd, 0xb7, 0x97, 0xff, 0x04, - 0x00, 0x00, 0xff, 0xff, 0x3a, 0x2f, 0x8e, 0x9a, 0xe6, 0x23, 0x00, 0x00, + 0xb9, 0x1a, 0x3e, 0x24, 0xf2, 0xe3, 0x43, 0x47, 0xc7, 0x76, 0xcc, 0x28, 0xb9, 0xb2, 0x4c, 0x3b, + 0xb6, 0xec, 0x38, 0x52, 0xa2, 0x5c, 0xe4, 0x22, 0xb9, 0xf7, 0x16, 0xa5, 0xc8, 0x21, 0x4d, 0x99, + 0x9a, 0xa1, 0x87, 0xa4, 0x2c, 0x27, 0x40, 0x0f, 0x46, 0xe4, 0x11, 0x35, 0x31, 0x39, 0x43, 0xcd, + 0x0c, 0x2d, 0x29, 0x7f, 0xa0, 0xab, 0x02, 0x2d, 0xba, 0x29, 0xb2, 0x28, 0xb2, 0xe9, 0xa6, 0x9b, + 0x6e, 0xfa, 0x13, 0x8a, 0xc2, 0xcb, 0xac, 0xda, 0xac, 0x8a, 0xc6, 0x01, 0xba, 0x28, 0xd0, 0x75, + 0x81, 0xae, 0x8a, 0xf3, 0x98, 0xd1, 0x50, 0x91, 0xdc, 0xd0, 0x76, 0xe2, 0x8d, 0xa1, 0xf3, 0xbd, + 0xce, 0xf7, 0xfe, 0xbe, 0xc3, 0x31, 0x5c, 0xf7, 0x0e, 0x06, 0x6b, 0xf4, 0x88, 0x76, 0x2d, 0x7b, + 0xcf, 0x35, 0x47, 0xbb, 0x6b, 0x23, 0xd7, 0xe9, 0x52, 0xcf, 0x73, 0x5c, 0x8f, 0x78, 0x07, 0x83, + 0xd5, 0x91, 0xeb, 0xf8, 0x0e, 0x2e, 0x74, 0x9d, 0xee, 0x23, 0xd7, 0x31, 0xbb, 0xfb, 0xab, 0x0c, + 0xd8, 0xb3, 0x3c, 0xdf, 0x3b, 0x18, 0xb8, 0x63, 0x7b, 0x11, 0x73, 0xe8, 0x68, 0x77, 0xad, 0x67, + 0xfa, 0xa6, 0xa0, 0x5e, 0xbc, 0xc6, 0x64, 0x76, 0x4d, 0xdf, 0x1c, 0x38, 0xfd, 0xb5, 0x1e, 0xf5, + 0xba, 0xa3, 0xdd, 0x35, 0xcf, 0x77, 0xc7, 0x5d, 0x7f, 0xec, 0xd2, 0x9e, 0x24, 0x2a, 0x9e, 0x41, + 0xf4, 0xa9, 0x63, 0xd9, 0xc4, 0x3f, 0x1e, 0x51, 0x49, 0xb3, 0x7c, 0x06, 0xcd, 0xc0, 0xe9, 0x3e, + 0xb2, 0xec, 0xbe, 0xa4, 0xb8, 0xc4, 0x28, 0x18, 0x87, 0x27, 0xfe, 0x95, 0xe0, 0xc5, 0xd3, 0x56, + 0x45, 0xb4, 0x7b, 0xeb, 0x19, 0x16, 0xef, 0x9a, 0x1e, 0x8d, 0xea, 0x67, 0xd9, 0x8f, 0xa9, 0xeb, + 0xd3, 0xde, 0x9a, 0x37, 0x32, 0x6d, 0x42, 0x8f, 0x46, 0x2e, 0xf5, 0x3c, 0xcb, 0xb1, 0x25, 0xcd, + 0xc5, 0xbe, 0xd3, 0x77, 0xf8, 0x9f, 0x6b, 0xec, 0x2f, 0x01, 0x2d, 0xfe, 0x52, 0x81, 0xfc, 0xb6, + 0x39, 0x18, 0x53, 0xaf, 0xec, 0xb8, 0xb4, 0x35, 0xa2, 0x5d, 0x5c, 0x86, 0xb9, 0xae, 0x33, 0x18, + 0x0f, 0x6d, 0xaf, 0xa0, 0x2c, 0xc7, 0x57, 0x32, 0xeb, 0xd7, 0x56, 0xcf, 0xf3, 0xe8, 0x6a, 0xc5, + 0xf4, 0xc7, 0xc3, 0xba, 0xbd, 0xe7, 0x6c, 0x24, 0x9e, 0xfc, 0xe5, 0xca, 0x8c, 0x11, 0x70, 0xe2, + 0x2b, 0x90, 0xb2, 0xc7, 0x43, 0xe2, 0x3a, 0x87, 0x5e, 0x21, 0xbe, 0xac, 0xac, 0x24, 0x02, 0x02, + 0x7b, 0x3c, 0x34, 0x9c, 0x43, 0x0f, 0xbf, 0x01, 0x69, 0xd7, 0x3c, 0x24, 0xbb, 0xc7, 0x3e, 0xf5, + 0x0a, 0xb1, 0xe5, 0xf8, 0x4a, 0xd6, 0x48, 0xb9, 0xe6, 0xe1, 0x06, 0x3b, 0x17, 0xff, 0x35, 0x0b, + 0xf3, 0x6d, 0x73, 0x77, 0x40, 0x0d, 0x6a, 0xf6, 0xa8, 0xcb, 0xd5, 0xda, 0x80, 0xa4, 0xcf, 0x40, + 0x05, 0x65, 0x59, 0x59, 0xc9, 0xac, 0xdf, 0x38, 0xa5, 0x94, 0x77, 0x30, 0xe0, 0x0e, 0xe1, 0x6c, + 0x15, 0xea, 0x75, 0x5d, 0x6b, 0xe4, 0x3b, 0xae, 0xbc, 0x56, 0xb0, 0xe2, 0xab, 0x90, 0xb6, 0xec, + 0x1e, 0x3d, 0x22, 0x56, 0xef, 0xa8, 0x10, 0x5b, 0x56, 0x56, 0x72, 0x12, 0x9f, 0xe2, 0xe0, 0x7a, + 0xef, 0x08, 0x2f, 0xc1, 0x9c, 0x4b, 0x1f, 0x53, 0xd7, 0xa3, 0x5c, 0xef, 0x54, 0xa0, 0xb7, 0x04, + 0xe2, 0xf7, 0x21, 0xc9, 0xfc, 0xeb, 0x15, 0x30, 0xf7, 0xcd, 0xe5, 0x88, 0x1a, 0x32, 0xbb, 0x56, + 0x5b, 0x23, 0xd3, 0x0e, 0xee, 0xe5, 0xb4, 0xf8, 0x1a, 0xc0, 0xc0, 0x1a, 0x5a, 0x3e, 0xd9, 0xb7, + 0x6c, 0xbf, 0x90, 0x5c, 0x56, 0x56, 0xe2, 0x92, 0x20, 0xcd, 0xe1, 0x77, 0x2d, 0xdb, 0x67, 0x2e, + 0xb3, 0x3c, 0xd2, 0xdd, 0xa7, 0xdd, 0x47, 0x85, 0xd9, 0xe8, 0xd5, 0x96, 0x57, 0x66, 0x40, 0xac, + 0x01, 0x3c, 0xb6, 0x3c, 0x6b, 0xd7, 0x1a, 0x58, 0xfe, 0x71, 0x61, 0x6e, 0x59, 0x59, 0xc9, 0xaf, + 0xaf, 0x9c, 0x1f, 0x9b, 0x56, 0xd7, 0xb4, 0xb7, 0x43, 0x7a, 0x29, 0x2c, 0x22, 0x01, 0xdf, 0x80, + 0xcc, 0xc8, 0x74, 0xcd, 0xc1, 0x80, 0x0e, 0xac, 0xcf, 0x68, 0x21, 0x1b, 0xb9, 0x33, 0x8a, 0xc0, + 0xef, 0xc2, 0xc2, 0xae, 0xe9, 0x77, 0xf7, 0x45, 0xb0, 0x08, 0xd7, 0xb8, 0xb0, 0x10, 0x31, 0x62, + 0x9e, 0xa3, 0x79, 0xe8, 0x1a, 0x0c, 0x89, 0xff, 0x17, 0x2e, 0x0f, 0xcd, 0x23, 0xe2, 0x5b, 0x43, + 0xea, 0xf9, 0xe6, 0x70, 0x44, 0xcc, 0x3e, 0x25, 0xb6, 0x69, 0x3b, 0x5e, 0x21, 0x1d, 0x49, 0x86, + 0x8b, 0x43, 0xf3, 0xa8, 0x1d, 0xd0, 0x94, 0xfa, 0x54, 0x63, 0x14, 0xf8, 0x13, 0x40, 0xb2, 0x6e, + 0x88, 0xe7, 0xbb, 0xd4, 0xee, 0xfb, 0xfb, 0x05, 0xe0, 0xc6, 0xde, 0x3e, 0x27, 0xe6, 0xcc, 0xd2, + 0x86, 0x60, 0x69, 0x49, 0x8e, 0x40, 0xb3, 0xc1, 0x24, 0x18, 0xef, 0xc2, 0x85, 0x40, 0xf8, 0xa1, + 0x69, 0xf9, 0x64, 0xe4, 0x0c, 0xac, 0xee, 0x71, 0x21, 0xc3, 0xe5, 0xdf, 0xf9, 0xcf, 0xf2, 0x1f, + 0x98, 0x96, 0xdf, 0xe4, 0x3c, 0xf2, 0x86, 0x85, 0xc1, 0x69, 0x04, 0x5e, 0x07, 0xbc, 0x6f, 0x7a, + 0xc4, 0x3b, 0xf6, 0x7c, 0x3a, 0x24, 0x41, 0x2d, 0xe5, 0x23, 0xee, 0x45, 0xfb, 0xa6, 0xd7, 0xe2, + 0xe8, 0xb2, 0xac, 0x97, 0xb7, 0x20, 0x6f, 0x53, 0xda, 0xa3, 0xbd, 0x90, 0x7e, 0x7e, 0x39, 0xbe, + 0x92, 0x33, 0x72, 0x02, 0x1a, 0x90, 0x35, 0x61, 0x3e, 0x28, 0x73, 0x49, 0x58, 0x40, 0xbc, 0x1c, + 0x6e, 0x9e, 0xa3, 0xba, 0x60, 0x3c, 0xa9, 0x07, 0x23, 0x1f, 0xf0, 0x0b, 0xcc, 0x66, 0x22, 0x95, + 0x40, 0xc9, 0xcd, 0x44, 0x2a, 0x85, 0xd2, 0x9b, 0x89, 0x54, 0x0e, 0xe5, 0x8b, 0x06, 0x64, 0xab, + 0xd6, 0xc0, 0xa7, 0x6e, 0x58, 0x78, 0xb3, 0x7b, 0xfc, 0x2c, 0x2b, 0xef, 0xfa, 0xf9, 0x29, 0xa7, + 0x86, 0x4d, 0x47, 0x1a, 0x2a, 0x39, 0x8b, 0xff, 0x88, 0x43, 0xa1, 0xce, 0x4a, 0xac, 0xf5, 0xc8, + 0x1a, 0xbd, 0xa2, 0xca, 0x0e, 0x2b, 0x37, 0x35, 0x45, 0xe5, 0x4e, 0xd6, 0x5c, 0xe2, 0x85, 0x6b, + 0x2e, 0xd2, 0x5e, 0x92, 0x67, 0xb5, 0x97, 0xb3, 0x92, 0x7f, 0xf6, 0x7b, 0x4e, 0xfe, 0xb9, 0x97, + 0x98, 0xfc, 0x9b, 0x89, 0x54, 0x1c, 0x25, 0x8a, 0x7f, 0x4b, 0x41, 0x7e, 0xd3, 0xb1, 0xec, 0x1f, + 0x3e, 0xca, 0xf7, 0x20, 0x33, 0x70, 0x9c, 0x47, 0xe3, 0x11, 0x9f, 0x80, 0xb2, 0x3a, 0xa6, 0x49, + 0x59, 0x10, 0xec, 0x0c, 0x8e, 0x77, 0x00, 0xbb, 0x74, 0xe8, 0xf8, 0x94, 0x44, 0x65, 0x2e, 0x4c, + 0x2d, 0x13, 0x09, 0x29, 0x8d, 0x13, 0xc9, 0xb7, 0x20, 0x2f, 0x45, 0x06, 0xf5, 0x1e, 0x67, 0xf5, + 0xbe, 0x11, 0x43, 0x8a, 0x91, 0x13, 0x98, 0xa0, 0xe6, 0x3f, 0x84, 0xd7, 0x26, 0x49, 0x89, 0xe9, + 0x52, 0xf2, 0x88, 0x1e, 0x17, 0x52, 0x91, 0x0c, 0xba, 0x30, 0xc1, 0x54, 0x72, 0xe9, 0x3d, 0x7a, + 0xcc, 0x46, 0xb9, 0x23, 0x56, 0x01, 0x9e, 0xba, 0x53, 0xd6, 0xae, 0x63, 0x73, 0x55, 0x3f, 0x84, + 0x04, 0x5b, 0x57, 0x64, 0x1a, 0x5e, 0x39, 0x27, 0x6e, 0x2c, 0xda, 0xed, 0xe3, 0x11, 0x95, 0xcc, + 0x9c, 0xe5, 0xa5, 0x4f, 0xac, 0xb3, 0xaa, 0x23, 0xfd, 0x3d, 0x57, 0x07, 0xbc, 0xcc, 0xd1, 0xf0, + 0x1e, 0x2c, 0x0c, 0x4d, 0xcb, 0xf6, 0x4d, 0xcb, 0x26, 0x8e, 0xdb, 0xa3, 0xae, 0x65, 0xf7, 0xf9, + 0xf0, 0x09, 0x27, 0x43, 0x80, 0xd6, 0x25, 0xf6, 0x9c, 0x69, 0x92, 0x7b, 0xe6, 0x34, 0xa9, 0xc0, + 0x9b, 0x03, 0xba, 0xe7, 0x13, 0xbe, 0xa3, 0x1e, 0x5a, 0xfe, 0x3e, 0x19, 0x99, 0x96, 0x4b, 0x7b, + 0x1c, 0x40, 0xdd, 0x89, 0x59, 0x54, 0x60, 0x94, 0x2c, 0x7a, 0x0f, 0x2c, 0x7f, 0xbf, 0xc9, 0xc9, + 0x36, 0x39, 0x15, 0x7e, 0x00, 0x37, 0x9c, 0xb1, 0x3f, 0x1a, 0xfb, 0xa4, 0xef, 0x3a, 0x3c, 0xfd, + 0x6c, 0xdf, 0xb2, 0xc7, 0xa6, 0x6f, 0x39, 0x36, 0xd9, 0x73, 0x5c, 0xc2, 0xef, 0x70, 0x9d, 0xc3, + 0xc2, 0x7c, 0x44, 0xde, 0x55, 0xc1, 0x53, 0x63, 0x2c, 0xe5, 0x08, 0x47, 0xd5, 0x71, 0x1b, 0x74, + 0xcf, 0x37, 0x9c, 0x43, 0xfc, 0xff, 0x50, 0x90, 0x19, 0xfd, 0xed, 0xbd, 0x02, 0x47, 0xf6, 0x8a, + 0x4b, 0x82, 0x6a, 0x63, 0x72, 0xbb, 0xd8, 0x4c, 0xa4, 0x92, 0x68, 0x76, 0x33, 0x91, 0xca, 0xa2, + 0x5c, 0xf1, 0x77, 0x0a, 0x40, 0xcb, 0x71, 0x7d, 0xd9, 0x64, 0xee, 0xc3, 0xbc, 0x54, 0x39, 0xf4, + 0xae, 0x68, 0x37, 0xc5, 0xf3, 0xb3, 0x2e, 0xf0, 0xb4, 0xbc, 0x34, 0x2f, 0x04, 0x44, 0xfd, 0x1f, + 0xc8, 0x22, 0x43, 0xae, 0xee, 0x80, 0xda, 0x13, 0xcd, 0x07, 0x05, 0xf8, 0x2d, 0x86, 0x6e, 0x50, + 0x1b, 0x2f, 0x42, 0x52, 0x58, 0x13, 0x8f, 0x58, 0x23, 0x40, 0xc5, 0x5f, 0xc4, 0x20, 0x5b, 0xb1, + 0x3c, 0xdf, 0xb2, 0xbb, 0x3e, 0xd7, 0xf9, 0x26, 0xcc, 0x73, 0x01, 0x91, 0xd9, 0xaf, 0xf0, 0xd9, + 0x9f, 0x97, 0xe0, 0x20, 0xaa, 0xb7, 0x00, 0xf5, 0x24, 0x63, 0x48, 0x19, 0xe3, 0x94, 0xf3, 0x01, + 0x3c, 0x20, 0x5d, 0x07, 0x6c, 0x8f, 0x07, 0x03, 0xd1, 0x2a, 0x02, 0xe4, 0xc4, 0x42, 0x8b, 0x38, + 0xbe, 0xe4, 0xd2, 0x40, 0x17, 0x7c, 0x03, 0xb2, 0xd4, 0x75, 0x1d, 0x97, 0x38, 0x36, 0xe9, 0x8d, + 0x47, 0xbc, 0x63, 0xa4, 0x83, 0x22, 0xe4, 0x18, 0xdd, 0xae, 0x8c, 0x47, 0x67, 0xf9, 0x38, 0xf9, + 0x62, 0x3e, 0x2e, 0x22, 0xc8, 0xeb, 0x6e, 0xcf, 0xb2, 0x4d, 0x56, 0xe5, 0xcc, 0x29, 0xc5, 0x5f, + 0xc5, 0x01, 0x7d, 0x6c, 0xf5, 0x3f, 0x33, 0xfb, 0x22, 0x19, 0xb9, 0xa7, 0x2a, 0x30, 0xcb, 0xe7, + 0x40, 0xf0, 0x30, 0x99, 0x6e, 0x86, 0x48, 0x5e, 0x5c, 0x05, 0xa0, 0x07, 0x13, 0x0e, 0xcc, 0xac, + 0x5f, 0x3d, 0x5f, 0x75, 0xe9, 0xd2, 0x60, 0x5f, 0xa7, 0x07, 0x27, 0xe1, 0xc8, 0x8b, 0x61, 0xe4, + 0x08, 0xd5, 0x27, 0x5a, 0x38, 0xc7, 0x48, 0x9b, 0xbc, 0x97, 0xd3, 0x87, 0xef, 0x41, 0x76, 0xcf, + 0x3a, 0xa2, 0x3d, 0xf2, 0x98, 0xbf, 0xd7, 0x0a, 0x49, 0xae, 0xf9, 0x33, 0xda, 0xe9, 0xe4, 0xbb, + 0xce, 0xc8, 0x70, 0x6e, 0x01, 0x7c, 0x81, 0xa6, 0x5e, 0xfc, 0x53, 0x1c, 0xe6, 0xb7, 0xa8, 0xdb, + 0xa7, 0x91, 0xc8, 0x6c, 0x41, 0x8e, 0x37, 0x83, 0xe7, 0xae, 0xba, 0x2c, 0x63, 0x0f, 0x6b, 0x4e, + 0x87, 0xbc, 0x6b, 0xf5, 0xf7, 0x23, 0xf2, 0x62, 0x53, 0xca, 0xcb, 0x71, 0xfe, 0x50, 0x60, 0x24, + 0x00, 0xc9, 0x57, 0x31, 0x08, 0x6f, 0x41, 0x8e, 0xd5, 0x1b, 0xa1, 0x07, 0x63, 0x33, 0x9c, 0x85, + 0x41, 0x29, 0x66, 0x19, 0x4a, 0x95, 0x18, 0xfc, 0x11, 0x5c, 0xe6, 0xae, 0x3c, 0xc9, 0xd1, 0x73, + 0xe6, 0x3d, 0xdd, 0xf3, 0xd5, 0x83, 0xc9, 0x79, 0xff, 0x7f, 0x50, 0x10, 0x7e, 0x3b, 0x83, 0x39, + 0x1d, 0x61, 0xbe, 0xc8, 0xa9, 0x4e, 0x71, 0x17, 0xff, 0x1e, 0x83, 0xfc, 0x5d, 0xd3, 0xdb, 0x8f, + 0xc4, 0xf5, 0x36, 0xcc, 0x9f, 0x52, 0x46, 0xf4, 0x26, 0xb9, 0xa7, 0x44, 0x55, 0xc0, 0x77, 0x00, + 0x9d, 0xbe, 0x5c, 0xb4, 0x27, 0x4e, 0x9c, 0x9f, 0xbc, 0xf2, 0x95, 0x47, 0xe4, 0x95, 0xb9, 0x79, + 0x33, 0x91, 0x9a, 0x43, 0xa9, 0xe2, 0xe7, 0x09, 0xc0, 0x75, 0xf9, 0x14, 0x8b, 0x38, 0xfc, 0x07, + 0xda, 0x92, 0x75, 0xc8, 0x85, 0xef, 0xc8, 0xe7, 0x6c, 0x4b, 0xd9, 0x40, 0x00, 0x8f, 0xc4, 0xab, + 0x0e, 0xe7, 0x99, 0x8b, 0xd5, 0xdc, 0x33, 0x17, 0xab, 0xef, 0xbe, 0xde, 0xa4, 0xa6, 0x5b, 0x6f, + 0x3e, 0x82, 0xcb, 0x23, 0x97, 0xee, 0x59, 0x47, 0x61, 0xb9, 0x87, 0xf5, 0x90, 0x0e, 0xeb, 0xe1, + 0x92, 0x20, 0x09, 0xca, 0x5e, 0xa6, 0x88, 0x7c, 0x3e, 0x7d, 0x1e, 0x87, 0x8b, 0x41, 0x72, 0x4c, + 0xbc, 0xc5, 0x57, 0x01, 0x45, 0xdf, 0xff, 0x3c, 0xc2, 0x4a, 0x24, 0xc2, 0xd1, 0xd7, 0x3d, 0x8b, + 0xf3, 0xf6, 0xe9, 0x38, 0x8b, 0x3e, 0xfa, 0xf6, 0x29, 0xd7, 0x06, 0x34, 0xfc, 0x01, 0x7c, 0x12, + 0x9e, 0xa6, 0xeb, 0xf8, 0xce, 0x99, 0xe1, 0xfe, 0x14, 0x16, 0x46, 0x2e, 0x25, 0x7b, 0x52, 0x37, + 0xe2, 0x8d, 0x68, 0x97, 0x87, 0x2d, 0xb3, 0xfe, 0xa3, 0xf3, 0x03, 0x7f, 0x96, 0x49, 0xab, 0x4d, + 0x97, 0x46, 0xcf, 0xc6, 0xfc, 0x68, 0x12, 0xb0, 0xf8, 0x33, 0x05, 0xe6, 0x4f, 0x11, 0xe1, 0x4d, + 0x80, 0x93, 0x1f, 0x38, 0x9f, 0xe3, 0x77, 0x89, 0x08, 0x37, 0x5e, 0x95, 0x59, 0x27, 0x5c, 0xb3, + 0x78, 0x3a, 0xeb, 0xe8, 0x70, 0x55, 0xfc, 0x5a, 0xdb, 0x96, 0xf3, 0xef, 0x8f, 0x00, 0xf9, 0x52, + 0xbf, 0xef, 0xd2, 0xbe, 0xe9, 0x3b, 0x42, 0x9d, 0xab, 0x00, 0x41, 0x0e, 0x0d, 0xa2, 0x4d, 0x2f, + 0xdd, 0x17, 0x69, 0x32, 0xf0, 0xf0, 0x4f, 0x20, 0x6b, 0x4a, 0x26, 0xcb, 0x91, 0xcf, 0xbd, 0xcc, + 0xfa, 0x7f, 0x9f, 0xaf, 0xf3, 0xe4, 0x15, 0xe1, 0x31, 0x52, 0x80, 0x51, 0x79, 0xf8, 0x5d, 0xb9, + 0xa6, 0xd2, 0x1e, 0x89, 0xa8, 0x92, 0x08, 0x55, 0x41, 0x12, 0x5b, 0x0b, 0x35, 0xaa, 0x49, 0xbb, + 0x93, 0xbc, 0xda, 0xde, 0xf9, 0xce, 0x9a, 0x7c, 0xab, 0xf6, 0xce, 0x58, 0x08, 0x67, 0x5f, 0x6c, + 0x21, 0x5c, 0xfc, 0x69, 0x0c, 0x32, 0x11, 0x8b, 0x99, 0xae, 0x7b, 0x63, 0xbb, 0xcb, 0x23, 0x3d, + 0x8d, 0xae, 0xd5, 0xb1, 0xdd, 0x0d, 0x74, 0x65, 0x02, 0xf0, 0x32, 0xa4, 0xc2, 0x75, 0x38, 0x16, + 0x29, 0xeb, 0x10, 0x8a, 0x2f, 0xf3, 0x9f, 0xbf, 0x79, 0x65, 0x25, 0xf9, 0x72, 0x3d, 0xdb, 0x15, + 0xb5, 0x74, 0x1d, 0xf2, 0x22, 0xdf, 0xc3, 0xca, 0x63, 0x4d, 0x33, 0x67, 0x64, 0x05, 0x54, 0x56, + 0xdc, 0x5d, 0x48, 0x9b, 0x6e, 0x7f, 0x3c, 0xa4, 0xb6, 0xef, 0x15, 0x66, 0x79, 0x90, 0xa7, 0x49, + 0xcc, 0x13, 0x66, 0xd9, 0x0a, 0x7e, 0x9b, 0x84, 0x04, 0xb3, 0x02, 0x23, 0xc8, 0x96, 0xb4, 0x87, + 0x44, 0xd3, 0xdb, 0x44, 0xeb, 0x34, 0x1a, 0x68, 0x06, 0xcf, 0x41, 0xbc, 0xb4, 0x5d, 0x43, 0x0a, + 0xce, 0x42, 0x6a, 0x43, 0xd7, 0x1b, 0xa4, 0xa4, 0x55, 0x50, 0x0c, 0x67, 0x60, 0x8e, 0x9f, 0x74, + 0x03, 0xc5, 0x71, 0x1e, 0xa0, 0xac, 0x6b, 0xe5, 0x52, 0x9b, 0x94, 0x6a, 0x35, 0x94, 0xc0, 0x69, + 0x48, 0x96, 0xf5, 0x8e, 0xd6, 0x46, 0x49, 0xc6, 0xbe, 0x55, 0xda, 0x41, 0x73, 0xfc, 0x8f, 0xba, + 0x86, 0x52, 0x18, 0x60, 0xb6, 0xd5, 0xae, 0x54, 0xd4, 0x6d, 0x94, 0x66, 0xc0, 0x56, 0x67, 0x0b, + 0x01, 0x13, 0xd7, 0xea, 0x6c, 0x91, 0xba, 0xd6, 0x46, 0x19, 0x76, 0xd3, 0x76, 0xc9, 0xa8, 0x97, + 0xb4, 0xb2, 0x8a, 0xb2, 0x0c, 0xb5, 0xa3, 0x1b, 0x5c, 0x72, 0x4e, 0xdc, 0xd4, 0xd1, 0xda, 0xc4, + 0xd0, 0x1f, 0xb4, 0x50, 0x9e, 0xf3, 0xdd, 0x37, 0x2a, 0xf5, 0x6a, 0x15, 0xcd, 0x63, 0x0c, 0xf9, + 0x6a, 0x5d, 0x2b, 0x35, 0x48, 0xc8, 0x8d, 0x98, 0x41, 0x02, 0x26, 0xef, 0x5c, 0xc0, 0x39, 0x48, + 0x97, 0x0c, 0xa3, 0xf4, 0x90, 0x4b, 0xc4, 0xec, 0xb2, 0xcd, 0x96, 0xae, 0xf1, 0xd3, 0x05, 0x86, + 0x64, 0xa7, 0x0d, 0x7e, 0xbc, 0xc8, 0xae, 0x6b, 0xb5, 0x8d, 0xba, 0x56, 0xe3, 0xe7, 0x4b, 0xdc, + 0xea, 0x7a, 0x9b, 0xbb, 0xe0, 0x35, 0x66, 0x08, 0x3b, 0xe8, 0x06, 0xba, 0x8c, 0x53, 0x90, 0x28, + 0xeb, 0x86, 0x81, 0x0a, 0xb8, 0x00, 0x17, 0x9b, 0xaa, 0x51, 0x56, 0xb5, 0x76, 0xbd, 0xa1, 0x92, + 0x4a, 0xbd, 0x55, 0x26, 0xf5, 0xad, 0x66, 0x03, 0xbd, 0x7e, 0x0a, 0x53, 0xd6, 0xb5, 0xb6, 0xc0, + 0x2c, 0xe2, 0x0b, 0x30, 0xcf, 0x75, 0xd0, 0x37, 0x36, 0xd5, 0xb2, 0x70, 0xe2, 0x1b, 0xf8, 0x22, + 0x20, 0xa1, 0x4a, 0x04, 0xfa, 0x26, 0xd3, 0x60, 0xbb, 0x64, 0x90, 0xa6, 0xde, 0x44, 0xff, 0x25, + 0xd4, 0x63, 0x66, 0xf1, 0xf3, 0x12, 0x9e, 0x87, 0x4c, 0xab, 0x4d, 0xb6, 0x4a, 0xf7, 0xd4, 0x46, + 0x5d, 0x53, 0xd1, 0x15, 0x66, 0x4e, 0xab, 0x4d, 0xd4, 0x9d, 0xb6, 0xaa, 0xb5, 0xd1, 0x32, 0xb3, + 0xb5, 0xd5, 0x26, 0x1d, 0xad, 0xae, 0x6b, 0xe8, 0xaa, 0xe0, 0x26, 0x65, 0xbd, 0xd1, 0x50, 0xcb, + 0x6d, 0x54, 0x64, 0xc4, 0x65, 0x3d, 0x10, 0x7e, 0x4d, 0xb8, 0x9a, 0x1d, 0x5b, 0xa5, 0xad, 0x26, + 0xba, 0xce, 0xbc, 0x6b, 0xa8, 0x35, 0x83, 0xc5, 0x88, 0x59, 0xd1, 0x6c, 0xa3, 0xb7, 0x98, 0x36, + 0x1c, 0x66, 0xac, 0xa3, 0x1b, 0x8c, 0x81, 0x1f, 0x5a, 0x0d, 0xbd, 0xa9, 0xa2, 0x9b, 0xec, 0x36, + 0x71, 0xde, 0xd9, 0x41, 0x2b, 0x27, 0xa7, 0x87, 0x0f, 0xd1, 0xad, 0x08, 0xee, 0x21, 0xba, 0x1d, + 0x72, 0x8a, 0xa4, 0x79, 0x9b, 0x69, 0xc2, 0xcf, 0xa5, 0xed, 0xda, 0x0e, 0xba, 0x13, 0x3d, 0x3e, + 0x44, 0xef, 0x14, 0xef, 0x40, 0x82, 0x75, 0x07, 0xe6, 0xf3, 0x52, 0xa7, 0xad, 0xa3, 0x19, 0x9e, + 0x52, 0xe5, 0x52, 0xa3, 0x64, 0x20, 0x85, 0xc9, 0xd2, 0x74, 0x8d, 0xc8, 0x73, 0xac, 0xf8, 0x07, + 0x05, 0xf2, 0x4d, 0xd7, 0xf9, 0x94, 0x76, 0xfd, 0x16, 0x15, 0x6f, 0xe1, 0x1f, 0x43, 0x92, 0x75, + 0xe6, 0xe0, 0x81, 0x37, 0x4d, 0xe5, 0x08, 0x46, 0x5c, 0x83, 0x85, 0x3e, 0xb5, 0xa9, 0x6b, 0xfa, + 0x91, 0xf7, 0xb4, 0x78, 0xe4, 0x3d, 0xab, 0xb5, 0xa3, 0x90, 0x29, 0x58, 0x50, 0x6f, 0x02, 0xb2, + 0xc7, 0xfc, 0x07, 0x17, 0x8f, 0x8c, 0xa8, 0x4b, 0xfa, 0xd4, 0x16, 0x0f, 0x3c, 0x23, 0x67, 0x8f, + 0x87, 0xac, 0x83, 0x36, 0xa9, 0x5b, 0xa3, 0x76, 0xf1, 0xeb, 0x1c, 0x64, 0x1f, 0x58, 0x76, 0xcf, + 0x39, 0x94, 0xc3, 0x69, 0x99, 0x7f, 0x57, 0xf1, 0x2d, 0xde, 0xab, 0x8f, 0xe5, 0x63, 0x3e, 0x0a, + 0xc2, 0x2d, 0x48, 0x1f, 0x72, 0x8e, 0x6a, 0xa8, 0xdc, 0xda, 0xf9, 0xa6, 0x46, 0x85, 0xcb, 0x43, + 0x35, 0xec, 0x17, 0xa1, 0x9c, 0xc5, 0xdf, 0x2b, 0xb2, 0x53, 0xb4, 0x20, 0x17, 0x8c, 0x06, 0x5a, + 0x7d, 0xde, 0xae, 0x69, 0x4c, 0xca, 0xc0, 0xf7, 0x01, 0xe4, 0x55, 0x4c, 0x62, 0x8c, 0x4b, 0x7c, + 0x6f, 0x3a, 0x9d, 0x99, 0xd4, 0x88, 0x90, 0x8f, 0x12, 0x4f, 0xbe, 0xb8, 0xa2, 0x2c, 0x7e, 0x31, + 0x07, 0xc9, 0xaa, 0x6b, 0x0e, 0x29, 0xbe, 0x07, 0x89, 0xa1, 0xd3, 0xa3, 0x52, 0xdd, 0xef, 0x2a, + 0x9c, 0xf3, 0xae, 0x6e, 0x39, 0xbd, 0x70, 0x28, 0x31, 0x21, 0xf8, 0x3e, 0xcc, 0xee, 0x3a, 0x63, + 0xbb, 0xe7, 0xc9, 0xb9, 0xfe, 0xfe, 0x54, 0xe2, 0x36, 0x38, 0x6b, 0xb0, 0x9e, 0x0a, 0x41, 0xf8, + 0x63, 0x48, 0xd3, 0xa3, 0xee, 0x60, 0xcc, 0x77, 0x8e, 0x38, 0x57, 0xf2, 0x83, 0xa9, 0xa4, 0xaa, + 0x01, 0x77, 0xf8, 0x63, 0x42, 0x00, 0x58, 0xfc, 0xa7, 0x02, 0x49, 0x7e, 0x29, 0xbb, 0x85, 0xdf, + 0xc7, 0x0a, 0x49, 0xba, 0xe2, 0x83, 0xe9, 0x75, 0x8f, 0x0c, 0xe9, 0x13, 0x71, 0xf8, 0x1a, 0x80, + 0x65, 0xfb, 0xc4, 0xd9, 0xdb, 0xf3, 0xa8, 0x98, 0x7f, 0xc1, 0xa7, 0xb8, 0xb4, 0x65, 0xfb, 0x3a, + 0x07, 0xe3, 0xab, 0x90, 0x65, 0x55, 0xd1, 0x0b, 0xc8, 0x98, 0xa5, 0x59, 0x23, 0xc3, 0x61, 0x92, + 0x64, 0x13, 0x32, 0x02, 0xc9, 0x3f, 0x80, 0xcb, 0xc7, 0xc3, 0x14, 0x9f, 0x89, 0x41, 0x70, 0x33, + 0x9d, 0x16, 0x7f, 0xad, 0xc0, 0xac, 0x70, 0x37, 0xd6, 0x20, 0xe9, 0xf9, 0xa6, 0xeb, 0xcb, 0x85, + 0x6e, 0x7d, 0x7a, 0xb3, 0xc3, 0x8f, 0x37, 0x4c, 0x0c, 0xae, 0x40, 0x9c, 0xda, 0x3d, 0x99, 0x00, + 0xcf, 0x21, 0xcd, 0x60, 0xec, 0xc5, 0x9b, 0x90, 0x60, 0xd9, 0xc5, 0x46, 0xa7, 0x51, 0xd2, 0x6a, + 0x2a, 0x9a, 0x61, 0xfd, 0x8d, 0x4f, 0x39, 0x85, 0xf5, 0xb7, 0x9a, 0xa1, 0x77, 0x9a, 0x2d, 0x14, + 0x2b, 0x7e, 0x06, 0xe9, 0xd0, 0xf7, 0xf8, 0x32, 0x5c, 0xe8, 0x68, 0x1b, 0x7a, 0x47, 0xab, 0xa8, + 0x15, 0xd2, 0x34, 0xd4, 0xb2, 0x5a, 0xa9, 0x6b, 0x35, 0x34, 0x33, 0x89, 0xa8, 0xea, 0x8d, 0x86, + 0xfe, 0x80, 0x21, 0x14, 0x36, 0x55, 0xf4, 0x6a, 0xb5, 0xa5, 0xb6, 0x23, 0xe4, 0xb1, 0x08, 0xf4, + 0x84, 0x36, 0xce, 0xc6, 0x49, 0xb9, 0x63, 0x18, 0xaa, 0x18, 0xb7, 0x28, 0x51, 0xfc, 0x04, 0xd2, + 0x61, 0x76, 0xb1, 0xc9, 0xaa, 0xe9, 0x44, 0xdd, 0x29, 0x37, 0x3a, 0x2d, 0x36, 0x50, 0xf8, 0xa5, + 0xfc, 0x58, 0x51, 0x49, 0x94, 0x4f, 0xc1, 0x0b, 0x90, 0x0b, 0x10, 0xdc, 0x0e, 0x14, 0x63, 0xdc, + 0x01, 0xa8, 0x5d, 0x57, 0x5b, 0x28, 0xbe, 0xf8, 0xe7, 0x18, 0xa4, 0x82, 0xbe, 0x83, 0xd5, 0xc8, + 0x2a, 0xf6, 0xed, 0x97, 0xc4, 0xb9, 0x5e, 0x3d, 0xbd, 0x88, 0x2d, 0x42, 0xca, 0x74, 0xfb, 0x5e, + 0xbd, 0x77, 0xe4, 0x15, 0xe6, 0x78, 0x87, 0x0c, 0xcf, 0xb8, 0x02, 0xa9, 0x70, 0x93, 0x4c, 0x4c, + 0xb9, 0x49, 0x86, 0x9c, 0xec, 0x29, 0xbd, 0xc7, 0x62, 0x29, 0x1f, 0xa4, 0x77, 0xa6, 0x89, 0xbf, + 0x21, 0x58, 0xf1, 0x0a, 0x4c, 0x6c, 0x77, 0x7c, 0xaf, 0x4d, 0x06, 0xfb, 0xf7, 0xc4, 0xde, 0xb7, + 0x02, 0x59, 0xb1, 0xc3, 0x4a, 0xca, 0x54, 0xe4, 0x55, 0x36, 0x81, 0xd9, 0x4c, 0xa4, 0x62, 0x28, + 0x2e, 0xb7, 0xbb, 0xdf, 0x28, 0x00, 0x27, 0xdd, 0x91, 0x4f, 0x5b, 0xfd, 0x01, 0xd1, 0x3a, 0x5b, + 0x1b, 0xaa, 0x21, 0xf3, 0xac, 0xa4, 0xdd, 0x13, 0xb3, 0xb3, 0xa2, 0x6a, 0x2d, 0x95, 0xf0, 0x33, + 0x0f, 0x92, 0xdc, 0x58, 0x04, 0x24, 0xce, 0x77, 0x84, 0xce, 0x16, 0xdf, 0x6b, 0xda, 0x62, 0xd1, + 0xe3, 0xdb, 0x8c, 0x58, 0xf4, 0x1a, 0xa5, 0x1a, 0x9a, 0x65, 0xe2, 0x1a, 0x6a, 0xa9, 0x82, 0xe6, + 0x58, 0xfe, 0x54, 0xeb, 0x46, 0xab, 0x4d, 0xb6, 0x4b, 0x8d, 0x8e, 0x8a, 0x52, 0x4c, 0x7e, 0xa3, + 0x14, 0x9e, 0xd3, 0x4c, 0x9a, 0xd6, 0xbe, 0x2b, 0x8f, 0x70, 0xfb, 0x7f, 0x20, 0x3f, 0xf9, 0x71, + 0x86, 0x25, 0x7e, 0xb3, 0xb3, 0xd1, 0xa8, 0x97, 0xd1, 0x0c, 0x7e, 0x1d, 0x2e, 0x89, 0xbf, 0xd9, + 0xfa, 0xc5, 0x37, 0x54, 0x89, 0x52, 0x36, 0xde, 0x79, 0xf2, 0xf5, 0xd2, 0xcc, 0x93, 0xa7, 0x4b, + 0xca, 0x97, 0x4f, 0x97, 0x94, 0xaf, 0x9e, 0x2e, 0x29, 0x7f, 0x7d, 0xba, 0xa4, 0xfc, 0xfc, 0x9b, + 0xa5, 0x99, 0x2f, 0xbf, 0x59, 0x9a, 0xf9, 0xea, 0x9b, 0xa5, 0x99, 0x8f, 0x33, 0x91, 0xff, 0xde, + 0xf2, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xa0, 0xf0, 0xb0, 0xe2, 0x23, 0x00, 0x00, } func (m *ValuesCoreSpec) Marshal() (dAtA []byte, err error) { @@ -2266,6 +2268,22 @@ func (m *TableReaderSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Spans) > 0 { + for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProcessorsSql(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } + } i = encodeVarintProcessorsSql(dAtA, i, uint64(m.BatchBytesLimit)) i-- dAtA[i] = 0x1 @@ -2331,20 +2349,6 @@ func (m *TableReaderSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintProcessorsSql(dAtA, i, uint64(m.LimitHint)) i-- dAtA[i] = 0x28 - if len(m.Spans) > 0 { - for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProcessorsSql(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } - } i-- if m.Reverse { dAtA[i] = 1 @@ -2422,6 +2426,20 @@ func (m *IndexSkipTableReaderSpec) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if len(m.Spans) > 0 { + for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProcessorsSql(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } i = encodeVarintProcessorsSql(dAtA, i, uint64(m.LockingWaitPolicy)) i-- dAtA[i] = 0x38 @@ -2439,20 +2457,6 @@ func (m *IndexSkipTableReaderSpec) MarshalToSizedBuffer(dAtA []byte) (int, error i = encodeVarintProcessorsSql(dAtA, i, uint64(m.Visibility)) i-- dAtA[i] = 0x20 - if len(m.Spans) > 0 { - for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintProcessorsSql(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } i = encodeVarintProcessorsSql(dAtA, i, uint64(m.IndexIdx)) i-- dAtA[i] = 0x10 @@ -3708,12 +3712,6 @@ func (m *TableReaderSpec) Size() (n int) { n += 1 + l + sovProcessorsSql(uint64(l)) n += 1 + sovProcessorsSql(uint64(m.IndexIdx)) n += 2 - if len(m.Spans) > 0 { - for _, e := range m.Spans { - l = e.Size() - n += 1 + l + sovProcessorsSql(uint64(l)) - } - } n += 1 + sovProcessorsSql(uint64(m.LimitHint)) n += 2 n += 1 + sovProcessorsSql(uint64(m.Visibility)) @@ -3732,6 +3730,12 @@ func (m *TableReaderSpec) Size() (n int) { n += 2 + l + sovProcessorsSql(uint64(l)) } n += 2 + sovProcessorsSql(uint64(m.BatchBytesLimit)) + if len(m.Spans) > 0 { + for _, e := range m.Spans { + l = e.Size() + n += 2 + l + sovProcessorsSql(uint64(l)) + } + } return n } @@ -3755,16 +3759,16 @@ func (m *IndexSkipTableReaderSpec) Size() (n int) { l = m.Table.Size() n += 1 + l + sovProcessorsSql(uint64(l)) n += 1 + sovProcessorsSql(uint64(m.IndexIdx)) + n += 1 + sovProcessorsSql(uint64(m.Visibility)) + n += 2 + n += 1 + sovProcessorsSql(uint64(m.LockingStrength)) + n += 1 + sovProcessorsSql(uint64(m.LockingWaitPolicy)) if len(m.Spans) > 0 { for _, e := range m.Spans { l = e.Size() n += 1 + l + sovProcessorsSql(uint64(l)) } } - n += 1 + sovProcessorsSql(uint64(m.Visibility)) - n += 2 - n += 1 + sovProcessorsSql(uint64(m.LockingStrength)) - n += 1 + sovProcessorsSql(uint64(m.LockingWaitPolicy)) return n } @@ -4440,40 +4444,6 @@ func (m *TableReaderSpec) Unmarshal(dAtA []byte) error { } } m.Reverse = bool(v != 0) - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProcessorsSql - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProcessorsSql - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProcessorsSql - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Spans = append(m.Spans, TableReaderSpan{}) - if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LimitHint", wireType) @@ -4760,6 +4730,40 @@ func (m *TableReaderSpec) Unmarshal(dAtA []byte) error { break } } + case 18: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsSql + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProcessorsSql + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProcessorsSql + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Spans = append(m.Spans, roachpb.Span{}) + if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProcessorsSql(dAtA[iNdEx:]) @@ -4945,40 +4949,6 @@ func (m *IndexSkipTableReaderSpec) Unmarshal(dAtA []byte) error { break } } - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowProcessorsSql - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthProcessorsSql - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthProcessorsSql - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Spans = append(m.Spans, TableReaderSpan{}) - if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Visibility", wireType) @@ -5056,6 +5026,40 @@ func (m *IndexSkipTableReaderSpec) Unmarshal(dAtA []byte) error { break } } + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsSql + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProcessorsSql + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProcessorsSql + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Spans = append(m.Spans, roachpb.Span{}) + if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProcessorsSql(dAtA[iNdEx:]) diff --git a/pkg/sql/execinfrapb/processors_sql.proto b/pkg/sql/execinfrapb/processors_sql.proto index 262484b15b7e..193660cddda6 100644 --- a/pkg/sql/execinfrapb/processors_sql.proto +++ b/pkg/sql/execinfrapb/processors_sql.proto @@ -19,6 +19,7 @@ syntax = "proto2"; package cockroach.sql.distsqlrun; option go_package = "execinfrapb"; +import "roachpb/data.proto"; import "sql/catalog/descpb/structured.proto"; import "sql/catalog/descpb/join_type.proto"; import "sql/catalog/descpb/locking.proto"; @@ -68,7 +69,8 @@ message TableReaderSpec { // i.e. table.indexes[index_idx-1] optional uint32 index_idx = 2 [(gogoproto.nullable) = false]; optional bool reverse = 3 [(gogoproto.nullable) = false]; - repeated TableReaderSpan spans = 4 [(gogoproto.nullable) = false]; + reserved 4; + repeated roachpb.Span spans = 18 [(gogoproto.nullable) = false]; // A hint for how many rows the consumer of the table reader output might // need. This is used to size the initial KV batches to try to avoid reading @@ -176,7 +178,8 @@ message IndexSkipTableReaderSpec { // i.e. table.indexes[index_idx-1] optional uint32 index_idx = 2 [(gogoproto.nullable) = false]; - repeated TableReaderSpan spans = 3 [(gogoproto.nullable) = false]; + reserved 3; + repeated roachpb.Span spans = 8 [(gogoproto.nullable) = false]; // Indicates the visibility level of the columns that should be returned. // Normally, will be set to PUBLIC. Will be set to PUBLIC_AND_NOT_PUBLIC if diff --git a/pkg/sql/flowinfra/cluster_test.go b/pkg/sql/flowinfra/cluster_test.go index f4d7c4bb7311..6d9228f7d5b5 100644 --- a/pkg/sql/flowinfra/cluster_test.go +++ b/pkg/sql/flowinfra/cluster_test.go @@ -73,13 +73,13 @@ func TestClusterFlow(t *testing.T) { kvDB := tc.Server(0).DB() desc := catalogkv.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") - makeIndexSpan := func(start, end int) execinfrapb.TableReaderSpan { + makeIndexSpan := func(start, end int) roachpb.Span { var span roachpb.Span prefix := roachpb.Key(rowenc.MakeIndexKeyPrefix(keys.SystemSQLCodec, desc, desc.PublicNonPrimaryIndexes()[0].GetID())) span.Key = append(prefix, encoding.EncodeVarintAscending(nil, int64(start))...) span.EndKey = append(span.EndKey, prefix...) span.EndKey = append(span.EndKey, encoding.EncodeVarintAscending(nil, int64(end))...) - return execinfrapb.TableReaderSpan{Span: span} + return span } // successful indicates whether the flow execution is successful. @@ -110,21 +110,21 @@ func TestClusterFlow(t *testing.T) { tr1 := execinfrapb.TableReaderSpec{ Table: *desc.TableDesc(), IndexIdx: 1, - Spans: []execinfrapb.TableReaderSpan{makeIndexSpan(0, 8)}, + Spans: []roachpb.Span{makeIndexSpan(0, 8)}, NeededColumns: []uint32{0, 1}, } tr2 := execinfrapb.TableReaderSpec{ Table: *desc.TableDesc(), IndexIdx: 1, - Spans: []execinfrapb.TableReaderSpan{makeIndexSpan(8, 12)}, + Spans: []roachpb.Span{makeIndexSpan(8, 12)}, NeededColumns: []uint32{0, 1}, } tr3 := execinfrapb.TableReaderSpec{ Table: *desc.TableDesc(), IndexIdx: 1, - Spans: []execinfrapb.TableReaderSpan{makeIndexSpan(12, 100)}, + Spans: []roachpb.Span{makeIndexSpan(12, 100)}, NeededColumns: []uint32{0, 1}, } diff --git a/pkg/sql/flowinfra/server_test.go b/pkg/sql/flowinfra/server_test.go index f4472786e3ce..be67f7e9af29 100644 --- a/pkg/sql/flowinfra/server_test.go +++ b/pkg/sql/flowinfra/server_test.go @@ -19,6 +19,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/gossip" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/rpc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/distsql" @@ -61,7 +62,7 @@ func TestServer(t *testing.T) { Table: *td.TableDesc(), IndexIdx: 0, Reverse: false, - Spans: []execinfrapb.TableReaderSpan{{Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}}, + Spans: []roachpb.Span{td.PrimaryIndexSpan(keys.SystemSQLCodec)}, NeededColumns: []uint32{0, 1}, } post := execinfrapb.PostProcessSpec{ diff --git a/pkg/sql/physicalplan/aggregator_funcs_test.go b/pkg/sql/physicalplan/aggregator_funcs_test.go index affb7371f80b..36683920d54d 100644 --- a/pkg/sql/physicalplan/aggregator_funcs_test.go +++ b/pkg/sql/physicalplan/aggregator_funcs_test.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/distsql" @@ -127,16 +128,16 @@ func checkDistAggregationInfo( makeTableReader := func(startPK, endPK int, streamID int) execinfrapb.ProcessorSpec { tr := execinfrapb.TableReaderSpec{ Table: *tableDesc.TableDesc(), - Spans: make([]execinfrapb.TableReaderSpan, 1), + Spans: make([]roachpb.Span, 1), NeededColumns: []uint32{uint32(colIdx)}, } var err error - tr.Spans[0].Span.Key, err = randgen.TestingMakePrimaryIndexKey(tableDesc, startPK) + tr.Spans[0].Key, err = randgen.TestingMakePrimaryIndexKey(tableDesc, startPK) if err != nil { t.Fatal(err) } - tr.Spans[0].Span.EndKey, err = randgen.TestingMakePrimaryIndexKey(tableDesc, endPK) + tr.Spans[0].EndKey, err = randgen.TestingMakePrimaryIndexKey(tableDesc, endPK) if err != nil { t.Fatal(err) } diff --git a/pkg/sql/physicalplan/specs.go b/pkg/sql/physicalplan/specs.go index fd57c9dab452..dbc6f66305d0 100644 --- a/pkg/sql/physicalplan/specs.go +++ b/pkg/sql/physicalplan/specs.go @@ -60,8 +60,6 @@ func NewTableReaderSpec() *execinfrapb.TableReaderSpec { // releaseTableReaderSpec puts this TableReaderSpec back into its sync pool. It // may not be used again after Release returns. func releaseTableReaderSpec(s *execinfrapb.TableReaderSpec) { - *s = execinfrapb.TableReaderSpec{ - Spans: s.Spans[:0], - } + *s = execinfrapb.TableReaderSpec{} trSpecPool.Put(s) } diff --git a/pkg/sql/rowexec/backfiller.go b/pkg/sql/rowexec/backfiller.go index c1937e558181..54b07fbcd325 100644 --- a/pkg/sql/rowexec/backfiller.go +++ b/pkg/sql/rowexec/backfiller.go @@ -142,9 +142,9 @@ func (b *backfiller) mainLoop(ctx context.Context) (roachpb.Spans, error) { for i := range b.spec.Spans { log.VEventf(ctx, 2, "%s backfiller starting span %d of %d: %s", - b.name, i+1, len(b.spec.Spans), b.spec.Spans[i].Span) + b.name, i+1, len(b.spec.Spans), b.spec.Spans[i]) chunks := 0 - todo := b.spec.Spans[i].Span + todo := b.spec.Spans[i] for todo.Key != nil { log.VEventf(ctx, 3, "%s backfiller starting chunk %d: %s", b.name, chunks, todo) var err error @@ -168,13 +168,13 @@ func (b *backfiller) mainLoop(ctx context.Context) (roachpb.Spans, error) { log.VEventf(ctx, 2, "%s backfiller ran out of time on span %d of %d, will resume it at %s next time", b.name, i+1, len(b.spec.Spans), todo) - finishedSpans = append(finishedSpans, roachpb.Span{Key: b.spec.Spans[i].Span.Key, EndKey: todo.Key}) + finishedSpans = append(finishedSpans, roachpb.Span{Key: b.spec.Spans[i].Key, EndKey: todo.Key}) break } log.VEventf(ctx, 2, "%s backfiller finished span %d of %d: %s", - b.name, i+1, len(b.spec.Spans), b.spec.Spans[i].Span) + b.name, i+1, len(b.spec.Spans), b.spec.Spans[i]) totalSpans++ - finishedSpans = append(finishedSpans, b.spec.Spans[i].Span) + finishedSpans = append(finishedSpans, b.spec.Spans[i]) } log.VEventf(ctx, 3, "%s backfiller flushing...", b.name) diff --git a/pkg/sql/rowexec/indexbackfiller.go b/pkg/sql/rowexec/indexbackfiller.go index db546bb2e597..8d6bb207f521 100644 --- a/pkg/sql/rowexec/indexbackfiller.go +++ b/pkg/sql/rowexec/indexbackfiller.go @@ -129,8 +129,8 @@ func (ib *indexBackfiller) constructIndexEntries( var entries []rowenc.IndexEntry for i := range ib.spec.Spans { log.VEventf(ctx, 2, "index backfiller starting span %d of %d: %s", - i+1, len(ib.spec.Spans), ib.spec.Spans[i].Span) - todo := ib.spec.Spans[i].Span + i+1, len(ib.spec.Spans), ib.spec.Spans[i]) + todo := ib.spec.Spans[i] for todo.Key != nil { startKey := todo.Key readAsOf := ib.spec.ReadAsOf @@ -145,7 +145,7 @@ func (ib *indexBackfiller) constructIndexEntries( // Identify the Span for which we have constructed index entries. This is // used for reporting progress and updating the job details. - completedSpan := ib.spec.Spans[i].Span + completedSpan := ib.spec.Spans[i] if todo.Key != nil { completedSpan.Key = startKey completedSpan.EndKey = todo.Key diff --git a/pkg/sql/rowexec/scrub_tablereader.go b/pkg/sql/rowexec/scrub_tablereader.go index c2d819dcd637..b76563d96766 100644 --- a/pkg/sql/rowexec/scrub_tablereader.go +++ b/pkg/sql/rowexec/scrub_tablereader.go @@ -14,7 +14,6 @@ import ( "bytes" "context" - "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/execinfra" @@ -134,10 +133,7 @@ func newScrubTableReader( } tr.fetcher = &fetcher - tr.Spans = make(roachpb.Spans, len(spec.Spans)) - for i, s := range spec.Spans { - tr.Spans[i] = s.Span - } + tr.Spans = spec.Spans tr.MakeSpansCopy() return tr, nil diff --git a/pkg/sql/rowexec/tablereader.go b/pkg/sql/rowexec/tablereader.go index 768786c5328e..6b59d3ca879b 100644 --- a/pkg/sql/rowexec/tablereader.go +++ b/pkg/sql/rowexec/tablereader.go @@ -15,7 +15,6 @@ import ( "sync" "time" - "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/sql/execinfra" @@ -165,15 +164,7 @@ func newTableReader( return nil, err } - nSpans := len(spec.Spans) - if cap(tr.Spans) >= nSpans { - tr.Spans = tr.Spans[:nSpans] - } else { - tr.Spans = make(roachpb.Spans, nSpans) - } - for i, s := range spec.Spans { - tr.Spans[i] = s.Span - } + tr.Spans = spec.Spans if !tr.ignoreMisplannedRanges { // Make a copy of the spans so that we could get the misplanned ranges // info. diff --git a/pkg/sql/rowexec/tablereader_test.go b/pkg/sql/rowexec/tablereader_test.go index 61a35e61e503..1e38b399a5db 100644 --- a/pkg/sql/rowexec/tablereader_test.go +++ b/pkg/sql/rowexec/tablereader_test.go @@ -71,13 +71,13 @@ func TestTableReader(t *testing.T) { td := catalogkv.TestingGetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t") - makeIndexSpan := func(start, end int) execinfrapb.TableReaderSpan { + makeIndexSpan := func(start, end int) roachpb.Span { var span roachpb.Span prefix := roachpb.Key(rowenc.MakeIndexKeyPrefix(keys.SystemSQLCodec, td, td.PublicNonPrimaryIndexes()[0].GetID())) span.Key = append(prefix, encoding.EncodeVarintAscending(nil, int64(start))...) span.EndKey = append(span.EndKey, prefix...) span.EndKey = append(span.EndKey, encoding.EncodeVarintAscending(nil, int64(end))...) - return execinfrapb.TableReaderSpan{Span: span} + return span } testCases := []struct { @@ -87,7 +87,7 @@ func TestTableReader(t *testing.T) { }{ { spec: execinfrapb.TableReaderSpec{ - Spans: []execinfrapb.TableReaderSpan{{Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}}, + Spans: []roachpb.Span{td.PrimaryIndexSpan(keys.SystemSQLCodec)}, }, post: execinfrapb.PostProcessSpec{ Projection: true, @@ -97,7 +97,7 @@ func TestTableReader(t *testing.T) { }, { spec: execinfrapb.TableReaderSpec{ - Spans: []execinfrapb.TableReaderSpan{{Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}}, + Spans: []roachpb.Span{td.PrimaryIndexSpan(keys.SystemSQLCodec)}, }, post: execinfrapb.PostProcessSpec{ Projection: true, @@ -110,7 +110,7 @@ func TestTableReader(t *testing.T) { spec: execinfrapb.TableReaderSpec{ IndexIdx: 1, Reverse: true, - Spans: []execinfrapb.TableReaderSpan{makeIndexSpan(4, 6)}, + Spans: []roachpb.Span{makeIndexSpan(4, 6)}, LimitHint: 1, }, post: execinfrapb.PostProcessSpec{ @@ -125,6 +125,10 @@ func TestTableReader(t *testing.T) { t.Run("", func(t *testing.T) { testutils.RunTrueAndFalse(t, "row-source", func(t *testing.T, rowSource bool) { ts := c.spec + // Make a copy of Spans because the table reader will modify + // them. + ts.Spans = make([]roachpb.Span, len(c.spec.Spans)) + copy(ts.Spans, c.spec.Spans) ts.Table = *td.TableDesc() st := s.ClusterSettings() @@ -228,16 +232,16 @@ ALTER TABLE t EXPERIMENTAL_RELOCATE VALUES (ARRAY[2], 1), (ARRAY[1], 2), (ARRAY[ Txn: kv.NewTxn(ctx, tc.Server(0).DB(), tc.Server(0).NodeID()), NodeID: evalCtx.NodeID, } - spec := execinfrapb.TableReaderSpec{ - Spans: []execinfrapb.TableReaderSpan{{Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}}, - Table: *td.TableDesc(), - } post := execinfrapb.PostProcessSpec{ Projection: true, OutputColumns: []uint32{0}, } testutils.RunTrueAndFalse(t, "row-source", func(t *testing.T, rowSource bool) { + spec := execinfrapb.TableReaderSpec{ + Spans: []roachpb.Span{td.PrimaryIndexSpan(keys.SystemSQLCodec)}, + Table: *td.TableDesc(), + } var out execinfra.RowReceiver var buf *distsqlutils.RowBuffer if !rowSource { @@ -337,7 +341,7 @@ func TestTableReaderDrain(t *testing.T) { NodeID: evalCtx.NodeID, } spec := execinfrapb.TableReaderSpec{ - Spans: []execinfrapb.TableReaderSpan{{Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}}, + Spans: []roachpb.Span{td.PrimaryIndexSpan(keys.SystemSQLCodec)}, Table: *td.TableDesc(), } post := execinfrapb.PostProcessSpec{ @@ -389,7 +393,7 @@ func TestLimitScans(t *testing.T) { } spec := execinfrapb.TableReaderSpec{ Table: *tableDesc.TableDesc(), - Spans: []execinfrapb.TableReaderSpan{{Span: tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}}, + Spans: []roachpb.Span{tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}, } // We're going to ask for 3 rows, all contained in the first range. const limit = 3 @@ -506,7 +510,7 @@ func BenchmarkTableReader(b *testing.B) { b.Run(fmt.Sprintf("rows=%d", numRows), func(b *testing.B) { spec := execinfrapb.TableReaderSpec{ Table: *tableDesc.TableDesc(), - Spans: []execinfrapb.TableReaderSpan{{Span: tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}}, + Spans: []roachpb.Span{tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}, } post := execinfrapb.PostProcessSpec{} From 3539976fff4b135ad230a2c6742f014bd3f45569 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Wed, 27 Oct 2021 17:03:17 -0400 Subject: [PATCH 076/205] ui: update sessions table tooltip The tooltip for statement on Sessions table was indicating that the we were displaying most recent or currently active statement. This commits changes to text to the accurate message that we only show currently active statements. Fixes #72047 Release note (ui change): Update tooltip text on Statement column on Session table to the accurate information that we show only currently active statements. --- .../workspaces/cluster-ui/src/sessions/sessionsTableContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTableContent.tsx b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTableContent.tsx index 2a0ac6cc448f..1f52b02ba34f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTableContent.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/sessions/sessionsTableContent.tsx @@ -21,7 +21,7 @@ export const SessionTableTitle = { Statement From 84be65cac16e2237e56bbb67016a57c62fa1e6ec Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Wed, 27 Oct 2021 09:11:09 -0700 Subject: [PATCH 077/205] colexec: optimize sort a bit This commit adds a couple of minor optimizations to the `sortOp`: - we now keep the references to all single column sorters so that we can reuse them when the sort operator is used multiple times - we also speed up the population of the initial `order` slice. Release note: None --- pkg/sql/colexec/colexecutils/utils.go | 11 + .../colexec/execgen/cmd/execgen/sort_gen.go | 2 +- pkg/sql/colexec/hash_aggregator.eg.go | 24 +- pkg/sql/colexec/hash_aggregator_tmpl.go | 24 +- pkg/sql/colexec/sort.eg.go | 498 +++++++++--------- pkg/sql/colexec/sort.go | 42 +- pkg/sql/colexec/sort_tmpl.go | 42 +- 7 files changed, 325 insertions(+), 318 deletions(-) diff --git a/pkg/sql/colexec/colexecutils/utils.go b/pkg/sql/colexec/colexecutils/utils.go index 22a7a2c08371..d1415d2f348c 100644 --- a/pkg/sql/colexec/colexecutils/utils.go +++ b/pkg/sql/colexec/colexecutils/utils.go @@ -310,3 +310,14 @@ func UpdateBatchState(batch coldata.Batch, length int, usesSel bool, sel []int) // in the selection vector to maintain invariants (like for flat bytes). batch.SetLength(length) } + +// DefaultSelectionVector contains all integers in [0, coldata.MaxBatchSize) +// range. +var DefaultSelectionVector []int + +func init() { + DefaultSelectionVector = make([]int, coldata.MaxBatchSize) + for i := range DefaultSelectionVector { + DefaultSelectionVector[i] = i + } +} diff --git a/pkg/sql/colexec/execgen/cmd/execgen/sort_gen.go b/pkg/sql/colexec/execgen/cmd/execgen/sort_gen.go index 8a397efc4a1f..3e29a493638d 100644 --- a/pkg/sql/colexec/execgen/cmd/execgen/sort_gen.go +++ b/pkg/sql/colexec/execgen/cmd/execgen/sort_gen.go @@ -44,7 +44,7 @@ func genSortOps(inputFileContents string, wr io.Writer) error { "_DIR_ENUM", "{{.Dir}}", "_DIR", "{{$dir}}", - "_ISNULL", "{{$nulls}}", + "_WITH_NULLS", "{{if $nulls}}WithNulls{{else}}WithoutNulls{{end}}", "_HANDLES_NULLS", "{{if $nulls}}WithNulls{{else}}{{end}}", ) s := r.Replace(inputFileContents) diff --git a/pkg/sql/colexec/hash_aggregator.eg.go b/pkg/sql/colexec/hash_aggregator.eg.go index 1ad31c9d9346..97c8298afa1f 100644 --- a/pkg/sql/colexec/hash_aggregator.eg.go +++ b/pkg/sql/colexec/hash_aggregator.eg.go @@ -75,17 +75,6 @@ const _ = "template_findSplit" // input tuples are processed before emitting any data. const _ = "template_getNext" -// defaultSelectionVector contains all integers in [0, coldata.MaxBatchSize) -// range. -var defaultSelectionVector []int - -func init() { - defaultSelectionVector = make([]int, coldata.MaxBatchSize) - for i := range defaultSelectionVector { - defaultSelectionVector[i] = i - } -} - func (op *hashAggregator) Next() coldata.Batch { if len(op.spec.OrderedGroupCols) > 0 { return getNext_true(op) @@ -486,7 +475,7 @@ func getNext_true(op *hashAggregator) coldata.Batch { } } if op.curOutputBucketIdx >= len(op.buckets) { - if op.bufferingState.pendingBatch.Length() > 0 { + if l := op.bufferingState.pendingBatch.Length(); l > 0 { // Clear the buckets. op.state = hashAggregatorBuffering op.resetBucketsAndTrackingState(op.Ctx) @@ -497,13 +486,16 @@ func getNext_true(op *hashAggregator) coldata.Batch { // in the buffering state, since it only contains tuples that still // need to be aggregated, so we do not need to reset to the original // batch state. - if op.inputTrackingState.tuples != nil && op.bufferingState.unprocessedIdx < op.bufferingState.pendingBatch.Length() { + if op.inputTrackingState.tuples != nil && op.bufferingState.unprocessedIdx < l { sel := op.bufferingState.pendingBatch.Selection() if sel != nil { - copy(sel, sel[op.bufferingState.unprocessedIdx:op.bufferingState.pendingBatch.Length()]) - op.bufferingState.pendingBatch.SetLength(op.bufferingState.pendingBatch.Length() - op.bufferingState.unprocessedIdx) + copy(sel, sel[op.bufferingState.unprocessedIdx:l]) + op.bufferingState.pendingBatch.SetLength(l - op.bufferingState.unprocessedIdx) } else { - colexecutils.UpdateBatchState(op.bufferingState.pendingBatch, op.bufferingState.pendingBatch.Length()-op.bufferingState.unprocessedIdx, true, defaultSelectionVector[op.bufferingState.unprocessedIdx:op.bufferingState.pendingBatch.Length()]) + colexecutils.UpdateBatchState( + op.bufferingState.pendingBatch, l-op.bufferingState.unprocessedIdx, true, /* usesSel */ + colexecutils.DefaultSelectionVector[op.bufferingState.unprocessedIdx:l], + ) } op.inputTrackingState.tuples.Enqueue(op.Ctx, op.bufferingState.pendingBatch) // We modified pendingBatch to only contain unprocessed diff --git a/pkg/sql/colexec/hash_aggregator_tmpl.go b/pkg/sql/colexec/hash_aggregator_tmpl.go index 804f9646c11a..2661250ec0ab 100644 --- a/pkg/sql/colexec/hash_aggregator_tmpl.go +++ b/pkg/sql/colexec/hash_aggregator_tmpl.go @@ -362,7 +362,7 @@ func getNext(op *hashAggregator, partialOrder bool) coldata.Batch { } if op.curOutputBucketIdx >= len(op.buckets) { if partialOrder { - if op.bufferingState.pendingBatch.Length() > 0 { + if l := op.bufferingState.pendingBatch.Length(); l > 0 { // Clear the buckets. op.state = hashAggregatorBuffering op.resetBucketsAndTrackingState(op.Ctx) @@ -373,13 +373,16 @@ func getNext(op *hashAggregator, partialOrder bool) coldata.Batch { // in the buffering state, since it only contains tuples that still // need to be aggregated, so we do not need to reset to the original // batch state. - if op.inputTrackingState.tuples != nil && op.bufferingState.unprocessedIdx < op.bufferingState.pendingBatch.Length() { + if op.inputTrackingState.tuples != nil && op.bufferingState.unprocessedIdx < l { sel := op.bufferingState.pendingBatch.Selection() if sel != nil { - copy(sel, sel[op.bufferingState.unprocessedIdx:op.bufferingState.pendingBatch.Length()]) - op.bufferingState.pendingBatch.SetLength(op.bufferingState.pendingBatch.Length() - op.bufferingState.unprocessedIdx) + copy(sel, sel[op.bufferingState.unprocessedIdx:l]) + op.bufferingState.pendingBatch.SetLength(l - op.bufferingState.unprocessedIdx) } else { - colexecutils.UpdateBatchState(op.bufferingState.pendingBatch, op.bufferingState.pendingBatch.Length()-op.bufferingState.unprocessedIdx, true, defaultSelectionVector[op.bufferingState.unprocessedIdx:op.bufferingState.pendingBatch.Length()]) + colexecutils.UpdateBatchState( + op.bufferingState.pendingBatch, l-op.bufferingState.unprocessedIdx, true, /* usesSel */ + colexecutils.DefaultSelectionVector[op.bufferingState.unprocessedIdx:l], + ) } op.inputTrackingState.tuples.Enqueue(op.Ctx, op.bufferingState.pendingBatch) // We modified pendingBatch to only contain unprocessed @@ -407,17 +410,6 @@ func getNext(op *hashAggregator, partialOrder bool) coldata.Batch { } } -// defaultSelectionVector contains all integers in [0, coldata.MaxBatchSize) -// range. -var defaultSelectionVector []int - -func init() { - defaultSelectionVector = make([]int, coldata.MaxBatchSize) - for i := range defaultSelectionVector { - defaultSelectionVector[i] = i - } -} - func (op *hashAggregator) Next() coldata.Batch { if len(op.spec.OrderedGroupCols) > 0 { return getNext(op, true) diff --git a/pkg/sql/colexec/sort.eg.go b/pkg/sql/colexec/sort.eg.go index 51111fae9fc4..e03af48e4f13 100644 --- a/pkg/sql/colexec/sort.eg.go +++ b/pkg/sql/colexec/sort.eg.go @@ -162,262 +162,262 @@ func isSorterSupported(t *types.T, dir execinfrapb.Ordering_Column_Direction) bo return false } -func newSingleSorter( - t *types.T, dir execinfrapb.Ordering_Column_Direction, hasNulls bool, -) colSorter { - switch hasNulls { - case true: - switch dir { - case execinfrapb.Ordering_Column_ASC: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - case types.BoolFamily: - switch t.Width() { - case -1: - default: - return &sortBoolAscWithNullsOp{} - } - case types.BytesFamily: - switch t.Width() { - case -1: - default: - return &sortBytesAscWithNullsOp{} - } - case types.DecimalFamily: - switch t.Width() { - case -1: - default: - return &sortDecimalAscWithNullsOp{} - } - case types.IntFamily: - switch t.Width() { - case 16: - return &sortInt16AscWithNullsOp{} - case 32: - return &sortInt32AscWithNullsOp{} - case -1: - default: - return &sortInt64AscWithNullsOp{} - } - case types.FloatFamily: - switch t.Width() { - case -1: - default: - return &sortFloat64AscWithNullsOp{} - } - case types.TimestampTZFamily: - switch t.Width() { - case -1: - default: - return &sortTimestampAscWithNullsOp{} - } - case types.IntervalFamily: - switch t.Width() { - case -1: - default: - return &sortIntervalAscWithNullsOp{} - } - case types.JsonFamily: - switch t.Width() { - case -1: - default: - return &sortJSONAscWithNullsOp{} - } - case typeconv.DatumVecCanonicalTypeFamily: - switch t.Width() { - case -1: - default: - return &sortDatumAscWithNullsOp{} - } +func newSingleSorterWithNulls(t *types.T, dir execinfrapb.Ordering_Column_Direction) colSorter { + switch dir { + case execinfrapb.Ordering_Column_ASC: + switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { + case types.BoolFamily: + switch t.Width() { + case -1: + default: + return &sortBoolAscWithNullsOp{} } - case execinfrapb.Ordering_Column_DESC: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - case types.BoolFamily: - switch t.Width() { - case -1: - default: - return &sortBoolDescWithNullsOp{} - } - case types.BytesFamily: - switch t.Width() { - case -1: - default: - return &sortBytesDescWithNullsOp{} - } - case types.DecimalFamily: - switch t.Width() { - case -1: - default: - return &sortDecimalDescWithNullsOp{} - } - case types.IntFamily: - switch t.Width() { - case 16: - return &sortInt16DescWithNullsOp{} - case 32: - return &sortInt32DescWithNullsOp{} - case -1: - default: - return &sortInt64DescWithNullsOp{} - } - case types.FloatFamily: - switch t.Width() { - case -1: - default: - return &sortFloat64DescWithNullsOp{} - } - case types.TimestampTZFamily: - switch t.Width() { - case -1: - default: - return &sortTimestampDescWithNullsOp{} - } - case types.IntervalFamily: - switch t.Width() { - case -1: - default: - return &sortIntervalDescWithNullsOp{} - } - case types.JsonFamily: - switch t.Width() { - case -1: - default: - return &sortJSONDescWithNullsOp{} - } - case typeconv.DatumVecCanonicalTypeFamily: - switch t.Width() { - case -1: - default: - return &sortDatumDescWithNullsOp{} - } + case types.BytesFamily: + switch t.Width() { + case -1: + default: + return &sortBytesAscWithNullsOp{} + } + case types.DecimalFamily: + switch t.Width() { + case -1: + default: + return &sortDecimalAscWithNullsOp{} + } + case types.IntFamily: + switch t.Width() { + case 16: + return &sortInt16AscWithNullsOp{} + case 32: + return &sortInt32AscWithNullsOp{} + case -1: + default: + return &sortInt64AscWithNullsOp{} + } + case types.FloatFamily: + switch t.Width() { + case -1: + default: + return &sortFloat64AscWithNullsOp{} + } + case types.TimestampTZFamily: + switch t.Width() { + case -1: + default: + return &sortTimestampAscWithNullsOp{} + } + case types.IntervalFamily: + switch t.Width() { + case -1: + default: + return &sortIntervalAscWithNullsOp{} + } + case types.JsonFamily: + switch t.Width() { + case -1: + default: + return &sortJSONAscWithNullsOp{} + } + case typeconv.DatumVecCanonicalTypeFamily: + switch t.Width() { + case -1: + default: + return &sortDatumAscWithNullsOp{} } } - case false: - switch dir { - case execinfrapb.Ordering_Column_ASC: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - case types.BoolFamily: - switch t.Width() { - case -1: - default: - return &sortBoolAscOp{} - } - case types.BytesFamily: - switch t.Width() { - case -1: - default: - return &sortBytesAscOp{} - } - case types.DecimalFamily: - switch t.Width() { - case -1: - default: - return &sortDecimalAscOp{} - } - case types.IntFamily: - switch t.Width() { - case 16: - return &sortInt16AscOp{} - case 32: - return &sortInt32AscOp{} - case -1: - default: - return &sortInt64AscOp{} - } - case types.FloatFamily: - switch t.Width() { - case -1: - default: - return &sortFloat64AscOp{} - } - case types.TimestampTZFamily: - switch t.Width() { - case -1: - default: - return &sortTimestampAscOp{} - } - case types.IntervalFamily: - switch t.Width() { - case -1: - default: - return &sortIntervalAscOp{} - } - case types.JsonFamily: - switch t.Width() { - case -1: - default: - return &sortJSONAscOp{} - } - case typeconv.DatumVecCanonicalTypeFamily: - switch t.Width() { - case -1: - default: - return &sortDatumAscOp{} - } + case execinfrapb.Ordering_Column_DESC: + switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { + case types.BoolFamily: + switch t.Width() { + case -1: + default: + return &sortBoolDescWithNullsOp{} } - case execinfrapb.Ordering_Column_DESC: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - case types.BoolFamily: - switch t.Width() { - case -1: - default: - return &sortBoolDescOp{} - } - case types.BytesFamily: - switch t.Width() { - case -1: - default: - return &sortBytesDescOp{} - } - case types.DecimalFamily: - switch t.Width() { - case -1: - default: - return &sortDecimalDescOp{} - } - case types.IntFamily: - switch t.Width() { - case 16: - return &sortInt16DescOp{} - case 32: - return &sortInt32DescOp{} - case -1: - default: - return &sortInt64DescOp{} - } - case types.FloatFamily: - switch t.Width() { - case -1: - default: - return &sortFloat64DescOp{} - } - case types.TimestampTZFamily: - switch t.Width() { - case -1: - default: - return &sortTimestampDescOp{} - } - case types.IntervalFamily: - switch t.Width() { - case -1: - default: - return &sortIntervalDescOp{} - } - case types.JsonFamily: - switch t.Width() { - case -1: - default: - return &sortJSONDescOp{} - } - case typeconv.DatumVecCanonicalTypeFamily: - switch t.Width() { - case -1: - default: - return &sortDatumDescOp{} - } + case types.BytesFamily: + switch t.Width() { + case -1: + default: + return &sortBytesDescWithNullsOp{} + } + case types.DecimalFamily: + switch t.Width() { + case -1: + default: + return &sortDecimalDescWithNullsOp{} + } + case types.IntFamily: + switch t.Width() { + case 16: + return &sortInt16DescWithNullsOp{} + case 32: + return &sortInt32DescWithNullsOp{} + case -1: + default: + return &sortInt64DescWithNullsOp{} + } + case types.FloatFamily: + switch t.Width() { + case -1: + default: + return &sortFloat64DescWithNullsOp{} + } + case types.TimestampTZFamily: + switch t.Width() { + case -1: + default: + return &sortTimestampDescWithNullsOp{} + } + case types.IntervalFamily: + switch t.Width() { + case -1: + default: + return &sortIntervalDescWithNullsOp{} + } + case types.JsonFamily: + switch t.Width() { + case -1: + default: + return &sortJSONDescWithNullsOp{} + } + case typeconv.DatumVecCanonicalTypeFamily: + switch t.Width() { + case -1: + default: + return &sortDatumDescWithNullsOp{} + } + } + } + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) + // This code is unreachable, but the compiler cannot infer that. + return nil +} + +func newSingleSorterWithoutNulls(t *types.T, dir execinfrapb.Ordering_Column_Direction) colSorter { + switch dir { + case execinfrapb.Ordering_Column_ASC: + switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { + case types.BoolFamily: + switch t.Width() { + case -1: + default: + return &sortBoolAscOp{} + } + case types.BytesFamily: + switch t.Width() { + case -1: + default: + return &sortBytesAscOp{} + } + case types.DecimalFamily: + switch t.Width() { + case -1: + default: + return &sortDecimalAscOp{} + } + case types.IntFamily: + switch t.Width() { + case 16: + return &sortInt16AscOp{} + case 32: + return &sortInt32AscOp{} + case -1: + default: + return &sortInt64AscOp{} + } + case types.FloatFamily: + switch t.Width() { + case -1: + default: + return &sortFloat64AscOp{} + } + case types.TimestampTZFamily: + switch t.Width() { + case -1: + default: + return &sortTimestampAscOp{} + } + case types.IntervalFamily: + switch t.Width() { + case -1: + default: + return &sortIntervalAscOp{} + } + case types.JsonFamily: + switch t.Width() { + case -1: + default: + return &sortJSONAscOp{} + } + case typeconv.DatumVecCanonicalTypeFamily: + switch t.Width() { + case -1: + default: + return &sortDatumAscOp{} + } + } + case execinfrapb.Ordering_Column_DESC: + switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { + case types.BoolFamily: + switch t.Width() { + case -1: + default: + return &sortBoolDescOp{} + } + case types.BytesFamily: + switch t.Width() { + case -1: + default: + return &sortBytesDescOp{} + } + case types.DecimalFamily: + switch t.Width() { + case -1: + default: + return &sortDecimalDescOp{} + } + case types.IntFamily: + switch t.Width() { + case 16: + return &sortInt16DescOp{} + case 32: + return &sortInt32DescOp{} + case -1: + default: + return &sortInt64DescOp{} + } + case types.FloatFamily: + switch t.Width() { + case -1: + default: + return &sortFloat64DescOp{} + } + case types.TimestampTZFamily: + switch t.Width() { + case -1: + default: + return &sortTimestampDescOp{} + } + case types.IntervalFamily: + switch t.Width() { + case -1: + default: + return &sortIntervalDescOp{} + } + case types.JsonFamily: + switch t.Width() { + case -1: + default: + return &sortJSONDescOp{} + } + case typeconv.DatumVecCanonicalTypeFamily: + switch t.Width() { + case -1: + default: + return &sortDatumDescOp{} } } } - colexecerror.InternalError(errors.AssertionFailedf("isSorterSupported should have caught this")) + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) // This code is unreachable, but the compiler cannot infer that. return nil } diff --git a/pkg/sql/colexec/sort.go b/pkg/sql/colexec/sort.go index a332cabcbfe5..ed7f73f157b4 100644 --- a/pkg/sql/colexec/sort.go +++ b/pkg/sql/colexec/sort.go @@ -203,6 +203,13 @@ type sortOp struct { // of the sortOp so that we can correctly choose a sorter based on // whether the input has nulls or not. sorters []colSorter + // sortersWithNulls and sortersWithoutNulls are lazily instantiated in + // sort() when the corresponding colSorter is needed for the first time. The + // references to these are kept so that these sorters can be reused when + // sort() is called multiple times (which is the case for the external sort + // and for sort chunks). + sortersWithNulls []colSorter + sortersWithoutNulls []colSorter // partitioners contains one partitioner per sort column except for the last, // which doesn't need to be partitioned. partitioners []partitioner @@ -340,22 +347,35 @@ func (p *sortOp) sort() { p.allocator.AdjustMemoryUsage(sizeAfter - sizeBefore) p.order = make([]int, spooledTuples) } - p.order = p.order[:spooledTuples] + order := p.order[:spooledTuples] // Initialize the order vector to the ordinal positions within the input set. - for i := 0; i < len(p.order); i++ { - p.order[i] = i + copy(order, colexecutils.DefaultSelectionVector) + for i := len(colexecutils.DefaultSelectionVector); i < len(order); i++ { + //gcassert:bce + order[i] = i } - // TODO(mgartner): Avoid creating new sorters for ever invocation of this - // function. Instead, create two slices of sorters, one to use when the - // vector does not have nulls, and one to use when it maybe has nulls. The - // sorter reset function can be used to clean up the sorters for the next - // invocation. for i := range p.orderingCols { inputVec := p.input.getValues(int(p.orderingCols[i].ColIdx)) - p.sorters[i] = newSingleSorter(p.inputTypes[p.orderingCols[i].ColIdx], p.orderingCols[i].Direction, inputVec.MaybeHasNulls()) - p.sorters[i].init(p.Ctx, p.allocator, inputVec, p.order) + if inputVec.MaybeHasNulls() { + if p.sortersWithNulls == nil { + p.sortersWithNulls = make([]colSorter, len(p.sorters)) + } + if p.sortersWithNulls[i] == nil { + p.sortersWithNulls[i] = newSingleSorterWithNulls(p.inputTypes[p.orderingCols[i].ColIdx], p.orderingCols[i].Direction) + } + p.sorters[i] = p.sortersWithNulls[i] + } else { + if p.sortersWithoutNulls == nil { + p.sortersWithoutNulls = make([]colSorter, len(p.sorters)) + } + if p.sortersWithoutNulls[i] == nil { + p.sortersWithoutNulls[i] = newSingleSorterWithoutNulls(p.inputTypes[p.orderingCols[i].ColIdx], p.orderingCols[i].Direction) + } + p.sorters[i] = p.sortersWithoutNulls[i] + } + p.sorters[i].init(p.Ctx, p.allocator, inputVec, order) } // Now, sort each column in turn. @@ -421,7 +441,7 @@ func (p *sortOp) sort() { // on it, ORing the results together with each subsequent column. This // produces a distinct vector (a boolean vector that has true in each // position that is different from the last position). - p.partitioners[i-offset].partitionWithOrder(p.input.getValues(int(p.orderingCols[i-offset].ColIdx)), p.order, + p.partitioners[i-offset].partitionWithOrder(p.input.getValues(int(p.orderingCols[i-offset].ColIdx)), order, partitionsCol, spooledTuples) } else { omitNextPartitioning = false diff --git a/pkg/sql/colexec/sort_tmpl.go b/pkg/sql/colexec/sort_tmpl.go index 7ddc99a6711f..c79e12e5e2fc 100644 --- a/pkg/sql/colexec/sort_tmpl.go +++ b/pkg/sql/colexec/sort_tmpl.go @@ -60,10 +60,6 @@ const _TYPE_WIDTH = 0 // _DIR_ENUM is the template variable. const _DIR_ENUM = 0 -// _ISNULL is the template type variable for whether the sorter handles nulls -// or not. It will be replaced by the appropriate boolean. -const _ISNULL = false - // _ASSIGN_LT is the template equality function for assigning the first input // to the result of the second input < the third input. func _ASSIGN_LT(_, _, _, _, _, _ string) bool { @@ -96,37 +92,33 @@ func isSorterSupported(t *types.T, dir execinfrapb.Ordering_Column_Direction) bo return false } -func newSingleSorter( - t *types.T, dir execinfrapb.Ordering_Column_Direction, hasNulls bool, -) colSorter { - switch hasNulls { - // {{range .}} - // {{$nulls := .Nulls}} - case _ISNULL: - switch dir { - // {{range .DirOverloads}} - // {{$dir := .DirString}} - case _DIR_ENUM: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - // {{range .FamilyOverloads}} - case _CANONICAL_TYPE_FAMILY: - switch t.Width() { - // {{range .WidthOverloads}} - case _TYPE_WIDTH: - return &sort_TYPE_DIR_HANDLES_NULLSOp{} - // {{end}} - } +// {{range .}} +// {{$nulls := .Nulls}} +func newSingleSorter_WITH_NULLS(t *types.T, dir execinfrapb.Ordering_Column_Direction) colSorter { + switch dir { + // {{range .DirOverloads}} + // {{$dir := .DirString}} + case _DIR_ENUM: + switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { + // {{range .FamilyOverloads}} + case _CANONICAL_TYPE_FAMILY: + switch t.Width() { + // {{range .WidthOverloads}} + case _TYPE_WIDTH: + return &sort_TYPE_DIR_HANDLES_NULLSOp{} // {{end}} } // {{end}} } // {{end}} } - colexecerror.InternalError(errors.AssertionFailedf("isSorterSupported should have caught this")) + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) // This code is unreachable, but the compiler cannot infer that. return nil } +// {{end}} + // {{range .}} // {{$nulls := .Nulls}} // {{range .DirOverloads}} From 984846c788f6f223ea679426ad3735930742b04e Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Wed, 27 Oct 2021 14:38:05 -0700 Subject: [PATCH 078/205] colexec: remove impossible error return value This commit updates the templates for sorters and ordered distinct to not return an error if an unsupported type is found - we now support everything. This allows us to simplify signatures of many methods too. Release note: None --- pkg/sql/colexec/aggregators_test.go | 18 +-- pkg/sql/colexec/colbuilder/execplan.go | 56 +++----- pkg/sql/colexec/colexecbase/distinct.eg.go | 28 ++-- pkg/sql/colexec/colexecbase/distinct.go | 26 ++-- pkg/sql/colexec/colexecbase/distinct_tmpl.go | 16 ++- pkg/sql/colexec/colexecjoin/mergejoiner.go | 33 ++--- .../colexec/colexecjoin/mergejoiner_test.go | 10 +- pkg/sql/colexec/colexectestutils/utils.go | 7 +- pkg/sql/colexec/colexecwindow/partitioner.go | 16 +-- .../colexecwindow/window_peer_grouper.eg.go | 15 +- .../colexecwindow/window_peer_grouper_tmpl.go | 15 +- pkg/sql/colexec/default_agg_test.go | 2 +- pkg/sql/colexec/distinct_test.go | 4 +- pkg/sql/colexec/external_distinct.go | 7 +- pkg/sql/colexec/external_hash_aggregator.go | 13 +- .../colexec/external_hash_aggregator_test.go | 7 +- pkg/sql/colexec/external_hash_joiner.go | 7 +- pkg/sql/colexec/external_sort.go | 8 +- pkg/sql/colexec/hash_aggregator.go | 8 +- pkg/sql/colexec/hash_aggregator_test.go | 8 +- pkg/sql/colexec/mergejoiner_test.go | 20 +-- pkg/sql/colexec/ordered_aggregator.go | 17 +-- pkg/sql/colexec/partially_ordered_distinct.go | 5 +- pkg/sql/colexec/sort.eg.go | 128 ------------------ pkg/sql/colexec/sort.go | 21 +-- pkg/sql/colexec/sort_chunks.go | 24 +--- pkg/sql/colexec/sort_chunks_test.go | 13 +- pkg/sql/colexec/sort_partitioner.eg.go | 28 ++-- pkg/sql/colexec/sort_test.go | 19 +-- pkg/sql/colexec/sort_tmpl.go | 24 ---- pkg/sql/colexec/sorttopk.go | 12 +- pkg/sql/colexec/sorttopk_test.go | 16 +-- pkg/sql/colflow/stats_test.go | 5 +- 33 files changed, 171 insertions(+), 465 deletions(-) diff --git a/pkg/sql/colexec/aggregators_test.go b/pkg/sql/colexec/aggregators_test.go index ca71edc4e49d..ede105224583 100644 --- a/pkg/sql/colexec/aggregators_test.go +++ b/pkg/sql/colexec/aggregators_test.go @@ -70,7 +70,7 @@ const ( // aggType is a helper struct that allows tests to test both the ordered and // hash aggregators at the same time. type aggType struct { - new func(*colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) + new func(*colexecagg.NewAggregatorArgs) colexecop.ResettableOperator name string order ordering } @@ -79,7 +79,7 @@ var aggTypesWithPartial = []aggType{ { // This is a wrapper around NewHashAggregator so its signature is // compatible with NewOrderedAggregator. - new: func(args *colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) { + new: func(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { return NewHashAggregator(args, nil /* newSpillingQueueArgs */, testAllocator, math.MaxInt64) }, name: "hash", @@ -93,7 +93,7 @@ var aggTypesWithPartial = []aggType{ { // This is a wrapper around NewHashAggregator so its signature is // compatible with NewOrderedAggregator. - new: func(args *colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) { + new: func(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { return NewHashAggregator(args, nil /* newSpillingQueueArgs */, testAllocator, math.MaxInt64) }, name: "hash-partial-order", @@ -815,7 +815,7 @@ func TestAggregators(t *testing.T) { Constructors: constructors, ConstArguments: constArguments, OutputTypes: outputTypes, - }) + }), nil }) } } @@ -932,7 +932,7 @@ func TestAggregatorRandom(t *testing.T) { &evalCtx, nil /* semaCtx */, tc.spec.Aggregations, tc.typs, ) require.NoError(t, err) - a, err := agg.new(&colexecagg.NewAggregatorArgs{ + a := agg.new(&colexecagg.NewAggregatorArgs{ Allocator: testAllocator, MemAccount: testMemAcc, Input: source, @@ -943,9 +943,6 @@ func TestAggregatorRandom(t *testing.T) { ConstArguments: constArguments, OutputTypes: outputTypes, }) - if err != nil { - t.Fatal(err) - } a.Init(context.Background()) testOutput := colexectestutils.NewOpTestOutput(a, expectedTuples) @@ -1134,7 +1131,7 @@ func benchmarkAggregateFunction( b.SetBytes(int64(argumentsSize * numInputRows)) b.ResetTimer() for i := 0; i < b.N; i++ { - a, err := agg.new(&colexecagg.NewAggregatorArgs{ + a := agg.new(&colexecagg.NewAggregatorArgs{ Allocator: testAllocator, MemAccount: testMemAcc, Input: source, @@ -1145,9 +1142,6 @@ func benchmarkAggregateFunction( ConstArguments: constArguments, OutputTypes: outputTypes, }) - if err != nil { - b.Fatal(err) - } a.Init(ctx) // Exhaust aggregator until all batches have been read or limit, if // non-zero, is reached. diff --git a/pkg/sql/colexec/colbuilder/execplan.go b/pkg/sql/colexec/colbuilder/execplan.go index 007601d622c5..ebfda889fc35 100644 --- a/pkg/sql/colexec/colbuilder/execplan.go +++ b/pkg/sql/colexec/colbuilder/execplan.go @@ -345,17 +345,16 @@ func (r opResult) createDiskBackedSort( processorID int32, opNamePrefix string, factory coldata.ColumnFactory, -) (colexecop.Operator, error) { +) colexecop.Operator { streamingMemAccount := args.StreamingMemAccount useStreamingMemAccountForBuffering := args.TestingKnobs.UseStreamingMemAccountForBuffering var ( sorterMemMonitorName string inMemorySorter colexecop.Operator - err error ) if len(ordering.Columns) == int(matchLen) { // The input is already fully ordered, so there is nothing to sort. - return input, nil + return input } totalMemLimit := execinfra.GetWorkMemLimit(flowCtx) spoolMemLimit := totalMemLimit * 4 / 5 @@ -372,7 +371,7 @@ func (r opResult) createDiskBackedSort( ctx, flowCtx, spoolMemLimit, opNamePrefix+"topk-sort", processorID, ) } - inMemorySorter, err = colexec.NewTopKSorter( + inMemorySorter = colexec.NewTopKSorter( colmem.NewAllocator(ctx, topKSorterMemAccount, factory), input, inputTypes, ordering.Columns, int(matchLen), uint64(limit), maxOutputBatchMemSize, ) @@ -387,7 +386,7 @@ func (r opResult) createDiskBackedSort( ctx, flowCtx, spoolMemLimit, opNamePrefix+"sort-chunks", processorID, ) } - inMemorySorter, err = colexec.NewSortChunks( + inMemorySorter = colexec.NewSortChunks( colmem.NewAllocator(ctx, sortChunksMemAccount, factory), input, inputTypes, ordering.Columns, int(matchLen), maxOutputBatchMemSize, ) @@ -401,21 +400,18 @@ func (r opResult) createDiskBackedSort( ctx, flowCtx, spoolMemLimit, opNamePrefix+"sort-all", processorID, ) } - inMemorySorter, err = colexec.NewSorter( + inMemorySorter = colexec.NewSorter( colmem.NewAllocator(ctx, sorterMemAccount, factory), input, inputTypes, ordering.Columns, maxOutputBatchMemSize, ) } - if err != nil { - return nil, err - } if inMemorySorter == nil { - return nil, errors.AssertionFailedf("unexpectedly inMemorySorter is nil") + colexecerror.InternalError(errors.AssertionFailedf("unexpectedly inMemorySorter is nil")) } if useStreamingMemAccountForBuffering || args.TestingKnobs.DiskSpillingDisabled { // In some testing scenarios we actually don't want to create a // disk-backed sort. - return inMemorySorter, nil + return inMemorySorter } // NOTE: when spilling to disk, we're using the same general external // sorter regardless of which sorter variant we have instantiated (i.e. @@ -460,7 +456,7 @@ func (r opResult) createDiskBackedSort( return es }, args.TestingKnobs.SpillingCallbackFn, - ), nil + ) } // makeDiskBackedSorterConstructor creates a colexec.DiskBackedSorterConstructor @@ -487,16 +483,12 @@ func (r opResult) makeDiskBackedSorterConstructor( // acquired. The hash-based partitioner will do this up front. sortArgs.FDSemaphore = nil } - sorter, err := r.createDiskBackedSort( + return r.createDiskBackedSort( ctx, flowCtx, &sortArgs, input, inputTypes, execinfrapb.Ordering{Columns: orderingCols}, 0, /* limit */ 0 /* matchLen */, maxNumberPartitions, args.Spec.ProcessorID, opNamePrefix+"-", factory, ) - if err != nil { - colexecerror.InternalError(err) - } - return sorter } } @@ -885,7 +877,7 @@ func NewColOperator( maxOutputBatchMemSize := execinfra.GetWorkMemLimit(flowCtx) // The second argument is nil because we disable the // tracking of the input tuples. - result.Root, err = colexec.NewHashAggregator( + result.Root = colexec.NewHashAggregator( newAggArgs, nil, /* newSpillingQueueArgs */ outputUnlimitedAllocator, maxOutputBatchMemSize, ) @@ -912,8 +904,7 @@ func NewColOperator( spillingQueueCfg.SetDefaultBufferSizeBytesForCacheMode() newAggArgs.Allocator = colmem.NewAllocator(ctx, hashAggregatorMemAccount, factory) newAggArgs.MemAccount = hashAggregatorMemAccount - var inMemoryHashAggregator colexecop.Operator - inMemoryHashAggregator, err = colexec.NewHashAggregator( + inMemoryHashAggregator := colexec.NewHashAggregator( newAggArgs, &colexecutils.NewSpillingQueueArgs{ UnlimitedAllocator: colmem.NewAllocator(ctx, spillingQueueMemAccount, factory), @@ -926,9 +917,6 @@ func NewColOperator( outputUnlimitedAllocator, maxOutputBatchMemSize, ) - if err != nil { - return r, err - } ehaOpName := "external-hash-aggregator" ehaMemAccount := result.createUnlimitedMemAccount(ctx, flowCtx, ehaOpName, spec.ProcessorID) // Note that we will use an unlimited memory account here @@ -972,7 +960,7 @@ func NewColOperator( evalCtx.SingleDatumAggMemAccount = streamingMemAccount newAggArgs.Allocator = streamingAllocator newAggArgs.MemAccount = streamingMemAccount - result.Root, err = colexec.NewOrderedAggregator(newAggArgs) + result.Root = colexec.NewOrderedAggregator(newAggArgs) } result.ToClose = append(result.ToClose, result.Root.(colexecop.Closer)) @@ -983,7 +971,7 @@ func NewColOperator( result.ColumnTypes = make([]*types.T, len(spec.Input[0].ColumnTypes)) copy(result.ColumnTypes, spec.Input[0].ColumnTypes) if len(core.Distinct.OrderedColumns) == len(core.Distinct.DistinctColumns) { - result.Root, err = colexecbase.NewOrderedDistinct( + result.Root = colexecbase.NewOrderedDistinct( inputs[0].Root, core.Distinct.OrderedColumns, result.ColumnTypes, core.Distinct.NullsAreDistinct, core.Distinct.ErrorOnDup, ) @@ -1168,16 +1156,13 @@ func NewColOperator( ctx, flowCtx, opName, spec.ProcessorID, ), factory) diskAccount := result.createDiskAccount(ctx, flowCtx, opName, spec.ProcessorID) - mj, err := colexecjoin.NewMergeJoinOp( + mj := colexecjoin.NewMergeJoinOp( unlimitedAllocator, execinfra.GetWorkMemLimit(flowCtx), args.DiskQueueCfg, args.FDSemaphore, joinType, inputs[0].Root, inputs[1].Root, leftTypes, rightTypes, core.MergeJoiner.LeftOrdering.Columns, core.MergeJoiner.RightOrdering.Columns, diskAccount, evalCtx, ) - if err != nil { - return r, err - } result.Root = mj result.ToClose = append(result.ToClose, mj.(colexecop.Closer)) @@ -1201,7 +1186,7 @@ func NewColOperator( ordering := core.Sorter.OutputOrdering matchLen := core.Sorter.OrderingMatchLen limit := core.Sorter.Limit - result.Root, err = result.createDiskBackedSort( + result.Root = result.createDiskBackedSort( ctx, flowCtx, args, input, result.ColumnTypes, ordering, limit, matchLen, 0, /* maxNumberPartitions */ spec.ProcessorID, "" /* opNamePrefix */, factory, ) @@ -1256,10 +1241,10 @@ func NewColOperator( // distribute). The decision about which kind of partitioner // to use should come from the optimizer. partitionColIdx = int(wf.OutputColIdx + tempColOffset) - input, err = colexecwindow.NewWindowSortingPartitioner( + input = colexecwindow.NewWindowSortingPartitioner( streamingAllocator, input, typs, core.Windower.PartitionBy, wf.Ordering.Columns, partitionColIdx, - func(input colexecop.Operator, inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column) (colexecop.Operator, error) { + func(input colexecop.Operator, inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column) colexecop.Operator { return result.createDiskBackedSort( ctx, flowCtx, args, input, inputTypes, execinfrapb.Ordering{Columns: orderingCols}, 0 /*limit */, 0, /* matchLen */ @@ -1273,19 +1258,16 @@ func NewColOperator( typs[len(typs)-1] = types.Bool } else { if len(wf.Ordering.Columns) > 0 { - input, err = result.createDiskBackedSort( + input = result.createDiskBackedSort( ctx, flowCtx, args, input, typs, wf.Ordering, 0 /* limit */, 0 /* matchLen */, 0, /* maxNumberPartitions */ spec.ProcessorID, opNamePrefix, factory, ) } } - if err != nil { - return r, err - } if colexecwindow.WindowFnNeedsPeersInfo(&wf) { peersColIdx = int(wf.OutputColIdx + tempColOffset) - input, err = colexecwindow.NewWindowPeerGrouper( + input = colexecwindow.NewWindowPeerGrouper( streamingAllocator, input, typs, wf.Ordering.Columns, partitionColIdx, peersColIdx, ) diff --git a/pkg/sql/colexec/colexecbase/distinct.eg.go b/pkg/sql/colexec/colexecbase/distinct.eg.go index c6b3786cc727..83f311027de6 100644 --- a/pkg/sql/colexec/colexecbase/distinct.eg.go +++ b/pkg/sql/colexec/colexecbase/distinct.eg.go @@ -31,7 +31,7 @@ import ( func newSingleDistinct( input colexecop.Operator, distinctColIdx int, outputCol []bool, t *types.T, nullsAreDistinct bool, -) (colexecop.Operator, error) { +) colexecop.Operator { switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { case types.BoolFamily: switch t.Width() { @@ -42,7 +42,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.BytesFamily: switch t.Width() { @@ -53,7 +53,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.DecimalFamily: switch t.Width() { @@ -64,7 +64,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.IntFamily: switch t.Width() { @@ -74,14 +74,14 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } case 32: return &distinctInt32Op{ OneInputHelper: colexecop.MakeOneInputHelper(input), distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } case -1: default: return &distinctInt64Op{ @@ -89,7 +89,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.FloatFamily: switch t.Width() { @@ -100,7 +100,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.TimestampTZFamily: switch t.Width() { @@ -111,7 +111,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.IntervalFamily: switch t.Width() { @@ -122,7 +122,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case types.JsonFamily: switch t.Width() { @@ -133,7 +133,7 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } case typeconv.DatumVecCanonicalTypeFamily: switch t.Width() { @@ -144,10 +144,12 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } } } - return nil, errors.Errorf("unsupported distinct type %s", t) + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) + // This code is unreachable, but the compiler cannot infer that. + return nil } // distinctBoolOp runs a distinct on the column in distinctColIdx, writing diff --git a/pkg/sql/colexec/colexecbase/distinct.go b/pkg/sql/colexec/colexecbase/distinct.go index 179c977aabfc..b6171da6da7f 100644 --- a/pkg/sql/colexec/colexecbase/distinct.go +++ b/pkg/sql/colexec/colexecbase/distinct.go @@ -28,31 +28,24 @@ import ( // last distinct operator in that chain as well as its output column. func OrderedDistinctColsToOperators( input colexecop.Operator, distinctCols []uint32, typs []*types.T, nullsAreDistinct bool, -) (colexecop.ResettableOperator, []bool, error) { +) (colexecop.ResettableOperator, []bool) { distinctCol := make([]bool, coldata.BatchSize()) // zero the boolean column on every iteration. input = &fnOp{ OneInputHelper: colexecop.MakeOneInputHelper(input), fn: func() { copy(distinctCol, colexecutils.ZeroBoolColumn) }, } - var ( - err error - r colexecop.ResettableOperator - ok bool - ) for i := range distinctCols { - input, err = newSingleDistinct(input, int(distinctCols[i]), distinctCol, typs[distinctCols[i]], nullsAreDistinct) - if err != nil { - return nil, nil, err - } + input = newSingleDistinct(input, int(distinctCols[i]), distinctCol, typs[distinctCols[i]], nullsAreDistinct) } - if r, ok = input.(colexecop.ResettableOperator); !ok { + r, ok := input.(colexecop.ResettableOperator) + if !ok { colexecerror.InternalError(errors.AssertionFailedf("unexpectedly an ordered distinct is not a Resetter")) } distinctChain := &distinctChainOps{ ResettableOperator: r, } - return distinctChain, distinctCol, nil + return distinctChain, distinctCol } type distinctChainOps struct { @@ -70,15 +63,12 @@ func NewOrderedDistinct( typs []*types.T, nullsAreDistinct bool, errorOnDup string, -) (colexecop.ResettableOperator, error) { +) colexecop.ResettableOperator { od := &orderedDistinct{ OneInputNode: colexecop.NewOneInputNode(input), UpsertDistinctHelper: UpsertDistinctHelper{ErrorOnDup: errorOnDup}, } - op, outputCol, err := OrderedDistinctColsToOperators(&od.distinctChainInput, distinctCols, typs, nullsAreDistinct) - if err != nil { - return nil, err - } + op, outputCol := OrderedDistinctColsToOperators(&od.distinctChainInput, distinctCols, typs, nullsAreDistinct) op = &colexecutils.BoolVecToSelOp{ OneInputHelper: colexecop.MakeOneInputHelper(op), OutputCol: outputCol, @@ -90,7 +80,7 @@ func NewOrderedDistinct( ProcessOnlyOneBatch: true, } od.distinctChain = op - return od, nil + return od } type orderedDistinct struct { diff --git a/pkg/sql/colexec/colexecbase/distinct_tmpl.go b/pkg/sql/colexec/colexecbase/distinct_tmpl.go index d90244cd2ced..41a5f9039cff 100644 --- a/pkg/sql/colexec/colexecbase/distinct_tmpl.go +++ b/pkg/sql/colexec/colexecbase/distinct_tmpl.go @@ -70,7 +70,7 @@ const _TYPE_WIDTH = 0 func newSingleDistinct( input colexecop.Operator, distinctColIdx int, outputCol []bool, t *types.T, nullsAreDistinct bool, -) (colexecop.Operator, error) { +) colexecop.Operator { switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { // {{range .}} case _CANONICAL_TYPE_FAMILY: @@ -82,12 +82,14 @@ func newSingleDistinct( distinctColIdx: distinctColIdx, outputCol: outputCol, nullsAreDistinct: nullsAreDistinct, - }, nil + } // {{end}} } // {{end}} } - return nil, errors.Errorf("unsupported distinct type %s", t) + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) + // This code is unreachable, but the compiler cannot infer that. + return nil } // {{end}} @@ -112,19 +114,21 @@ type partitioner interface { } // newPartitioner returns a new partitioner on type t. -func newPartitioner(t *types.T, nullsAreDistinct bool) (partitioner, error) { +func newPartitioner(t *types.T, nullsAreDistinct bool) partitioner { switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { // {{range .}} case _CANONICAL_TYPE_FAMILY: switch t.Width() { // {{range .WidthOverloads}} case _TYPE_WIDTH: - return partitioner_TYPE{nullsAreDistinct: nullsAreDistinct}, nil + return partitioner_TYPE{nullsAreDistinct: nullsAreDistinct} // {{end}} } // {{end}} } - return nil, errors.Errorf("unsupported partition type %s", t) + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) + // This code is unreachable, but the compiler cannot infer that. + return nil } // {{end}} diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner.go b/pkg/sql/colexec/colexecjoin/mergejoiner.go index 83f5b944aedb..45ddc5ad89bb 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner.go @@ -21,6 +21,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/colcontainer" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecbase" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecutils" + "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/colexecop" "github.com/cockroachdb/cockroach/pkg/sql/colmem" "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" @@ -226,7 +227,7 @@ func NewMergeJoinOp( rightOrdering []execinfrapb.Ordering_Column, diskAcc *mon.BoundAccount, evalCtx *tree.EvalContext, -) (colexecop.ResettableOperator, error) { +) colexecop.ResettableOperator { // Merge joiner only supports the case when the physical types in the // equality columns in both inputs are the same. We, however, also need to // support joining on numeric columns of different types or widths. If we @@ -277,7 +278,7 @@ func NewMergeJoinOp( castColumnIdx := len(actualLeftTypes) left, err = colexecbase.GetCastOperator(unlimitedAllocator, left, int(leftColIdx), castColumnIdx, leftType, rightType, evalCtx) if err != nil { - return nil, err + colexecerror.InternalError(err) } actualLeftTypes = append(actualLeftTypes, rightType) actualLeftOrdering[i].ColIdx = uint32(castColumnIdx) @@ -285,7 +286,7 @@ func NewMergeJoinOp( castColumnIdx := len(actualRightTypes) right, err = colexecbase.GetCastOperator(unlimitedAllocator, right, int(rightColIdx), castColumnIdx, rightType, leftType, evalCtx) if err != nil { - return nil, err + colexecerror.InternalError(err) } actualRightTypes = append(actualRightTypes, leftType) actualRightOrdering[i].ColIdx = uint32(castColumnIdx) @@ -293,13 +294,10 @@ func NewMergeJoinOp( needProjection = true } } - base, err := newMergeJoinBase( + base := newMergeJoinBase( unlimitedAllocator, memoryLimit, diskQueueCfg, fdSemaphore, joinType, left, right, actualLeftTypes, actualRightTypes, actualLeftOrdering, actualRightOrdering, diskAcc, ) - if err != nil { - return nil, err - } var mergeJoinerOp colexecop.ResettableOperator switch joinType { case descpb.InnerJoin: @@ -323,12 +321,12 @@ func NewMergeJoinOp( case descpb.ExceptAllJoin: mergeJoinerOp = &mergeJoinExceptAllOp{base} default: - return nil, errors.AssertionFailedf("merge join of type %s not supported", joinType) + colexecerror.InternalError(errors.AssertionFailedf("merge join of type %s not supported", joinType)) } if !needProjection { // We didn't add any cast operators, so we can just return the operator // right away. - return mergeJoinerOp, nil + return mergeJoinerOp } // We need to add a projection to remove all the cast columns we have added // above. Note that all extra columns were appended to the corresponding @@ -359,7 +357,7 @@ func NewMergeJoinOp( } return colexecbase.NewSimpleProjectOp( mergeJoinerOp, numActualLeftTypes+numActualRightTypes, projection, - ).(colexecop.ResettableOperator), nil + ).(colexecop.ResettableOperator) } // Const declarations for the merge joiner cross product (MJCP) zero state. @@ -402,7 +400,7 @@ func newMergeJoinBase( leftOrdering []execinfrapb.Ordering_Column, rightOrdering []execinfrapb.Ordering_Column, diskAcc *mon.BoundAccount, -) (*mergeJoinBase, error) { +) *mergeJoinBase { lEqCols := make([]uint32, len(leftOrdering)) lDirections := make([]execinfrapb.Ordering_Column_Direction, len(leftOrdering)) for i, c := range leftOrdering { @@ -442,22 +440,15 @@ func newMergeJoinBase( }, diskAcc: diskAcc, } - var err error base.left.distincterInput = &colexecop.FeedOperator{} - base.left.distincter, base.left.distinctOutput, err = colexecbase.OrderedDistinctColsToOperators( + base.left.distincter, base.left.distinctOutput = colexecbase.OrderedDistinctColsToOperators( base.left.distincterInput, lEqCols, leftTypes, false, /* nullsAreDistinct */ ) - if err != nil { - return base, err - } base.right.distincterInput = &colexecop.FeedOperator{} - base.right.distincter, base.right.distinctOutput, err = colexecbase.OrderedDistinctColsToOperators( + base.right.distincter, base.right.distinctOutput = colexecbase.OrderedDistinctColsToOperators( base.right.distincterInput, rEqCols, rightTypes, false, /* nullsAreDistinct */ ) - if err != nil { - return base, err - } - return base, err + return base } // mergeJoinBase extracts the common logic between all merge join operators. diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_test.go b/pkg/sql/colexec/colexecjoin/mergejoiner_test.go index a6bfb13a6d25..f000396a0539 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_test.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_test.go @@ -95,7 +95,7 @@ func TestMergeJoinCrossProduct(t *testing.T) { rightMJSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, colsRight, nTuples) leftHJSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, colsLeft, nTuples) rightHJSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, colsRight, nTuples) - mj, err := NewMergeJoinOp( + mj := NewMergeJoinOp( testAllocator, execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(mjFDLimit), descpb.InnerJoin, leftMJSource, rightMJSource, typs, typs, @@ -103,9 +103,6 @@ func TestMergeJoinCrossProduct(t *testing.T) { []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}, testDiskAcc, evalCtx, ) - if err != nil { - t.Fatal("error in merge join op constructor", err) - } mj.Init(ctx) hj := NewHashJoiner( testAllocator, testAllocator, HashJoinerSpec{ @@ -131,7 +128,7 @@ func TestMergeJoinCrossProduct(t *testing.T) { hjOutputTuples = append(hjOutputTuples, colexectestutils.GetTupleFromBatch(b, i)) } } - err = colexectestutils.AssertTuplesSetsEqual(hjOutputTuples, mjOutputTuples, evalCtx) + err := colexectestutils.AssertTuplesSetsEqual(hjOutputTuples, mjOutputTuples, evalCtx) // Note that the error message can be extremely verbose (it // might contain all output tuples), so we manually check that // comparing err to nil returns true (if we were to use @@ -188,14 +185,13 @@ func BenchmarkMergeJoiner(b *testing.B) { getNewMergeJoiner := func(leftSource, rightSource colexecop.Operator) colexecop.Operator { benchMemAccount.Clear(ctx) - base, err := newMergeJoinBase( + base := newMergeJoinBase( colmem.NewAllocator(ctx, &benchMemAccount, testColumnFactory), execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(mjFDLimit), descpb.InnerJoin, leftSource, rightSource, sourceTypes, sourceTypes, []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}, []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}, testDiskAcc, ) - require.NoError(b, err) return &mergeJoinInnerOp{mergeJoinBase: base} } diff --git a/pkg/sql/colexec/colexectestutils/utils.go b/pkg/sql/colexec/colexectestutils/utils.go index cc59e515decd..050a7b0398d3 100644 --- a/pkg/sql/colexec/colexectestutils/utils.go +++ b/pkg/sql/colexec/colexectestutils/utils.go @@ -53,7 +53,7 @@ import ( // that tests can use OrderedDistinctColsToOperators without invoking an import // dependency cycle. var OrderedDistinctColsToOperators func(input colexecop.Operator, distinctCols []uint32, typs []*types.T, nullsAreDistinct bool, -) (colexecop.ResettableOperator, []bool, error) +) (colexecop.ResettableOperator, []bool) // Tuple represents a row with any-type columns. type Tuple []interface{} @@ -1318,12 +1318,9 @@ func (r *OpTestOutput) VerifyAnyOrder() error { // order (so set comparison behavior is used). func (r *OpTestOutput) VerifyPartialOrder() error { distincterInput := &colexecop.FeedOperator{} - distincter, distinctOutput, err := OrderedDistinctColsToOperators( + distincter, distinctOutput := OrderedDistinctColsToOperators( distincterInput, r.orderedCols, r.typs, false, /* nullsAreDistinct */ ) - if err != nil { - return err - } var actual, expected Tuples start := 0 diff --git a/pkg/sql/colexec/colexecwindow/partitioner.go b/pkg/sql/colexec/colexecwindow/partitioner.go index 252b6bfb3e21..3320f552c699 100644 --- a/pkg/sql/colexec/colexecwindow/partitioner.go +++ b/pkg/sql/colexec/colexecwindow/partitioner.go @@ -32,23 +32,17 @@ func NewWindowSortingPartitioner( partitionIdxs []uint32, ordCols []execinfrapb.Ordering_Column, partitionColIdx int, - createDiskBackedSorter func(input colexecop.Operator, inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column) (colexecop.Operator, error), -) (op colexecop.Operator, err error) { + createDiskBackedSorter func(input colexecop.Operator, inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column) colexecop.Operator, +) colexecop.Operator { partitionAndOrderingCols := make([]execinfrapb.Ordering_Column, len(partitionIdxs)+len(ordCols)) for i, idx := range partitionIdxs { partitionAndOrderingCols[i] = execinfrapb.Ordering_Column{ColIdx: idx} } copy(partitionAndOrderingCols[len(partitionIdxs):], ordCols) - input, err = createDiskBackedSorter(input, inputTyps, partitionAndOrderingCols) - if err != nil { - return nil, err - } + input = createDiskBackedSorter(input, inputTyps, partitionAndOrderingCols) var distinctCol []bool - input, distinctCol, err = colexecbase.OrderedDistinctColsToOperators(input, partitionIdxs, inputTyps, false /* nullsAreDistinct */) - if err != nil { - return nil, err - } + input, distinctCol = colexecbase.OrderedDistinctColsToOperators(input, partitionIdxs, inputTyps, false /* nullsAreDistinct */) input = colexecutils.NewVectorTypeEnforcer(allocator, input, types.Bool, partitionColIdx) return &windowSortingPartitioner{ @@ -56,7 +50,7 @@ func NewWindowSortingPartitioner( allocator: allocator, distinctCol: distinctCol, partitionColIdx: partitionColIdx, - }, nil + } } type windowSortingPartitioner struct { diff --git a/pkg/sql/colexec/colexecwindow/window_peer_grouper.eg.go b/pkg/sql/colexec/colexecwindow/window_peer_grouper.eg.go index 81678629ec2a..da7f29c46cde 100644 --- a/pkg/sql/colexec/colexecwindow/window_peer_grouper.eg.go +++ b/pkg/sql/colexec/colexecwindow/window_peer_grouper.eg.go @@ -35,7 +35,7 @@ func NewWindowPeerGrouper( orderingCols []execinfrapb.Ordering_Column, partitionColIdx int, outputColIdx int, -) (op colexecop.Operator, err error) { +) colexecop.Operator { allPeers := len(orderingCols) == 0 var distinctCol []bool if !allPeers { @@ -43,12 +43,9 @@ func NewWindowPeerGrouper( for i, ordCol := range orderingCols { orderIdxs[i] = ordCol.ColIdx } - input, distinctCol, err = colexecbase.OrderedDistinctColsToOperators( + input, distinctCol = colexecbase.OrderedDistinctColsToOperators( input, orderIdxs, typs, false, /* nullsAreDistinct */ ) - if err != nil { - return nil, err - } } input = colexecutils.NewVectorTypeEnforcer(allocator, input, types.Bool, outputColIdx) initFields := windowPeerGrouperInitFields{ @@ -62,20 +59,20 @@ func NewWindowPeerGrouper( if partitionColIdx != tree.NoColumnIdx { return &windowPeerGrouperAllPeersWithPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } return &windowPeerGrouperAllPeersNoPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } if partitionColIdx != tree.NoColumnIdx { return &windowPeerGrouperWithPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } return &windowPeerGrouperNoPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } type windowPeerGrouperInitFields struct { diff --git a/pkg/sql/colexec/colexecwindow/window_peer_grouper_tmpl.go b/pkg/sql/colexec/colexecwindow/window_peer_grouper_tmpl.go index 1673a54a1b0f..ed1b2a010533 100644 --- a/pkg/sql/colexec/colexecwindow/window_peer_grouper_tmpl.go +++ b/pkg/sql/colexec/colexecwindow/window_peer_grouper_tmpl.go @@ -47,7 +47,7 @@ func NewWindowPeerGrouper( orderingCols []execinfrapb.Ordering_Column, partitionColIdx int, outputColIdx int, -) (op colexecop.Operator, err error) { +) colexecop.Operator { allPeers := len(orderingCols) == 0 var distinctCol []bool if !allPeers { @@ -55,12 +55,9 @@ func NewWindowPeerGrouper( for i, ordCol := range orderingCols { orderIdxs[i] = ordCol.ColIdx } - input, distinctCol, err = colexecbase.OrderedDistinctColsToOperators( + input, distinctCol = colexecbase.OrderedDistinctColsToOperators( input, orderIdxs, typs, false, /* nullsAreDistinct */ ) - if err != nil { - return nil, err - } } input = colexecutils.NewVectorTypeEnforcer(allocator, input, types.Bool, outputColIdx) initFields := windowPeerGrouperInitFields{ @@ -74,20 +71,20 @@ func NewWindowPeerGrouper( if partitionColIdx != tree.NoColumnIdx { return &windowPeerGrouperAllPeersWithPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } return &windowPeerGrouperAllPeersNoPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } if partitionColIdx != tree.NoColumnIdx { return &windowPeerGrouperWithPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } return &windowPeerGrouperNoPartitionOp{ windowPeerGrouperInitFields: initFields, - }, nil + } } type windowPeerGrouperInitFields struct { diff --git a/pkg/sql/colexec/default_agg_test.go b/pkg/sql/colexec/default_agg_test.go index a288cadb9db9..992640cd32ba 100644 --- a/pkg/sql/colexec/default_agg_test.go +++ b/pkg/sql/colexec/default_agg_test.go @@ -157,7 +157,7 @@ func TestDefaultAggregateFunc(t *testing.T) { Constructors: constructors, ConstArguments: constArguments, OutputTypes: outputTypes, - }) + }), nil }) }) } diff --git a/pkg/sql/colexec/distinct_test.go b/pkg/sql/colexec/distinct_test.go index 971a3348eabb..700197b03faf 100644 --- a/pkg/sql/colexec/distinct_test.go +++ b/pkg/sql/colexec/distinct_test.go @@ -389,7 +389,7 @@ func TestDistinct(t *testing.T) { } log.Info(context.Background(), "ordered") tc.runTests(t, colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { - return colexecbase.NewOrderedDistinct(input[0], tc.distinctCols, tc.typs, tc.nullsAreDistinct, tc.errorOnDup) + return colexecbase.NewOrderedDistinct(input[0], tc.distinctCols, tc.typs, tc.nullsAreDistinct, tc.errorOnDup), nil }) } } @@ -563,7 +563,7 @@ func BenchmarkDistinct(b *testing.B) { return newPartiallyOrderedDistinct(allocator, input, distinctCols, distinctCols[:numOrderedCols], typs, false /* nullsAreDistinct */, "" /* errorOnDup */) }, func(allocator *colmem.Allocator, input colexecop.Operator, distinctCols []uint32, numOrderedCols int, typs []*types.T) (colexecop.Operator, error) { - return colexecbase.NewOrderedDistinct(input, distinctCols, typs, false /* nullsAreDistinct */, "" /* errorOnDup */) + return colexecbase.NewOrderedDistinct(input, distinctCols, typs, false /* nullsAreDistinct */, "" /* errorOnDup */), nil }, } distinctNames := []string{"Unordered", "PartiallyOrdered", "Ordered"} diff --git a/pkg/sql/colexec/external_distinct.go b/pkg/sql/colexec/external_distinct.go index e6f3d6fd0691..8e475fd3f7f9 100644 --- a/pkg/sql/colexec/external_distinct.go +++ b/pkg/sql/colexec/external_distinct.go @@ -13,7 +13,6 @@ package colexec import ( "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecbase" - "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/colexecop" "github.com/cockroachdb/cockroach/pkg/sql/colmem" "github.com/cockroachdb/cockroach/pkg/sql/execinfra" @@ -74,14 +73,10 @@ func NewExternalDistinct( projection[i] = uint32(i) } diskBackedWithoutOrdinality := colexecbase.NewSimpleProjectOp(diskBackedSorter, len(sortTypes), projection) - diskBackedFallbackOp, err := colexecbase.NewOrderedDistinct( + return colexecbase.NewOrderedDistinct( diskBackedWithoutOrdinality, distinctCols, inputTypes, distinctSpec.NullsAreDistinct, distinctSpec.ErrorOnDup, ) - if err != nil { - colexecerror.InternalError(err) - } - return diskBackedFallbackOp } // We have to be careful to not emit duplicates of already emitted by the // in-memory operator tuples, so we plan a special filterer operator to diff --git a/pkg/sql/colexec/external_hash_aggregator.go b/pkg/sql/colexec/external_hash_aggregator.go index e29418de73ab..fb81354cc664 100644 --- a/pkg/sql/colexec/external_hash_aggregator.go +++ b/pkg/sql/colexec/external_hash_aggregator.go @@ -14,7 +14,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecagg" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" - "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/colexecop" "github.com/cockroachdb/cockroach/pkg/sql/colmem" "github.com/cockroachdb/cockroach/pkg/sql/execinfra" @@ -51,11 +50,7 @@ func NewExternalHashAggregator( newAggArgs.Input = partitionedInputs[0] // We don't need to track the input tuples when we have already spilled. // TODO(yuzefovich): it might be worth increasing the number of buckets. - op, err := NewHashAggregator(&newAggArgs, nil /* newSpillingQueueArgs */, outputUnlimitedAllocator, maxOutputBatchMemSize) - if err != nil { - colexecerror.InternalError(err) - } - return op + return NewHashAggregator(&newAggArgs, nil /* newSpillingQueueArgs */, outputUnlimitedAllocator, maxOutputBatchMemSize) } spec := newAggArgs.Spec diskBackedFallbackOpConstructor := func( @@ -68,11 +63,7 @@ func NewExternalHashAggregator( partitionedInputs[0], newAggArgs.InputTypes, makeOrdering(spec.GroupCols), maxNumberActivePartitions, ) - diskBackedFallbackOp, err := NewOrderedAggregator(&newAggArgs) - if err != nil { - colexecerror.InternalError(err) - } - return diskBackedFallbackOp + return NewOrderedAggregator(&newAggArgs) } eha := newHashBasedPartitioner( newAggArgs.Allocator, diff --git a/pkg/sql/colexec/external_hash_aggregator_test.go b/pkg/sql/colexec/external_hash_aggregator_test.go index 51fc0c129c5e..32b06ce82bae 100644 --- a/pkg/sql/colexec/external_hash_aggregator_test.go +++ b/pkg/sql/colexec/external_hash_aggregator_test.go @@ -204,11 +204,14 @@ func BenchmarkExternalHashAggregator(b *testing.B) { for _, groupSize := range groupSizes { benchmarkAggregateFunction( b, aggType{ - new: func(args *colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) { + new: func(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { op, accs, mons, _, err := createExternalHashAggregator( ctx, flowCtx, args, queueCfg, &colexecop.TestingSemaphore{}, 0, /* numForcedRepartitions */ ) + if err != nil { + b.Fatal(err) + } memAccounts = append(memAccounts, accs...) memMonitors = append(memMonitors, mons...) // The hash-based partitioner is not a @@ -216,7 +219,7 @@ func BenchmarkExternalHashAggregator(b *testing.B) { // signatures of the aggregator constructors, we // wrap it with a noop operator. It is ok for the // purposes of this benchmark. - return colexecop.NewNoop(op), err + return colexecop.NewNoop(op) }, name: fmt.Sprintf("spilled=%t", spillForced), }, diff --git a/pkg/sql/colexec/external_hash_joiner.go b/pkg/sql/colexec/external_hash_joiner.go index 940c5f358a45..834063455a3c 100644 --- a/pkg/sql/colexec/external_hash_joiner.go +++ b/pkg/sql/colexec/external_hash_joiner.go @@ -14,7 +14,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/col/coldata" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecargs" "github.com/cockroachdb/cockroach/pkg/sql/colexec/colexecjoin" - "github.com/cockroachdb/cockroach/pkg/sql/colexecerror" "github.com/cockroachdb/cockroach/pkg/sql/colexecop" "github.com/cockroachdb/cockroach/pkg/sql/colmem" "github.com/cockroachdb/cockroach/pkg/sql/execinfra" @@ -130,15 +129,11 @@ func NewExternalHashJoiner( rightPartitionSorter := createDiskBackedSorter( partitionedInputs[1], spec.Right.SourceTypes, rightOrdering, externalSorterMaxNumberPartitions, ) - diskBackedSortMerge, err := colexecjoin.NewMergeJoinOp( + return colexecjoin.NewMergeJoinOp( unlimitedAllocator, memoryLimit, args.DiskQueueCfg, fdSemaphore, spec.JoinType, leftPartitionSorter, rightPartitionSorter, spec.Left.SourceTypes, spec.Right.SourceTypes, leftOrdering, rightOrdering, diskAcc, flowCtx.EvalCtx, ) - if err != nil { - colexecerror.InternalError(err) - } - return diskBackedSortMerge } return newHashBasedPartitioner( unlimitedAllocator, diff --git a/pkg/sql/colexec/external_sort.go b/pkg/sql/colexec/external_sort.go index df80fc2ae01c..79bc9baf96ff 100644 --- a/pkg/sql/colexec/external_sort.go +++ b/pkg/sql/colexec/external_sort.go @@ -272,18 +272,14 @@ func NewExternalSorter( } inputPartitioner := newInputPartitioningOperator(sortUnlimitedAllocator, input, inputTypes, inMemSortPartitionLimit) var inMemSorter colexecop.ResettableOperator - var err error if topK > 0 { - inMemSorter, err = NewTopKSorter(sortUnlimitedAllocator, inputPartitioner, inputTypes, ordering.Columns, matchLen, topK, inMemSortOutputLimit) + inMemSorter = NewTopKSorter(sortUnlimitedAllocator, inputPartitioner, inputTypes, ordering.Columns, matchLen, topK, inMemSortOutputLimit) } else { - inMemSorter, err = newSorter( + inMemSorter = newSorter( sortUnlimitedAllocator, newAllSpooler(sortUnlimitedAllocator, inputPartitioner, inputTypes), inputTypes, ordering.Columns, inMemSortOutputLimit, ) } - if err != nil { - colexecerror.InternalError(err) - } partitionedDiskQueueSemaphore := fdSemaphore if !delegateFDAcquisitions { // To avoid deadlocks with other disk queues, we manually attempt to diff --git a/pkg/sql/colexec/hash_aggregator.go b/pkg/sql/colexec/hash_aggregator.go index 0c1c62c95c54..c675dfec5441 100644 --- a/pkg/sql/colexec/hash_aggregator.go +++ b/pkg/sql/colexec/hash_aggregator.go @@ -202,12 +202,12 @@ func NewHashAggregator( newSpillingQueueArgs *colexecutils.NewSpillingQueueArgs, outputUnlimitedAllocator *colmem.Allocator, maxOutputBatchMemSize int64, -) (colexecop.ResettableOperator, error) { +) colexecop.ResettableOperator { aggFnsAlloc, inputArgsConverter, toClose, err := colexecagg.NewAggregateFuncsAlloc( args, args.Spec.Aggregations, hashAggregatorAllocSize, colexecagg.HashAggKind, ) if err != nil { - return nil, err + colexecerror.InternalError(err) } hashAgg := &hashAggregator{ OneInputNode: colexecop.NewOneInputNode(args.Input), @@ -231,11 +231,11 @@ func NewHashAggregator( } if len(args.Spec.OrderedGroupCols) > 0 { hashAgg.distincterInput = &colexecop.FeedOperator{} - hashAgg.distincter, hashAgg.distinctOutput, err = colexecbase.OrderedDistinctColsToOperators( + hashAgg.distincter, hashAgg.distinctOutput = colexecbase.OrderedDistinctColsToOperators( hashAgg.distincterInput, args.Spec.OrderedGroupCols, args.InputTypes, false, /* nullsAreDistinct */ ) } - return hashAgg, err + return hashAgg } func (op *hashAggregator) Init(ctx context.Context) { diff --git a/pkg/sql/colexec/hash_aggregator_test.go b/pkg/sql/colexec/hash_aggregator_test.go index 3c7c3aff1c9c..a4e2edf640ca 100644 --- a/pkg/sql/colexec/hash_aggregator_test.go +++ b/pkg/sql/colexec/hash_aggregator_test.go @@ -445,7 +445,7 @@ func TestHashAggregator(t *testing.T) { nil, /* newSpillingQueueArgs */ testAllocator, math.MaxInt64, - ) + ), nil }) } } @@ -475,14 +475,14 @@ func BenchmarkHashAggregatorInputTuplesTracking(b *testing.B) { for _, groupSize := range groupSizes { for _, agg := range []aggType{ { - new: func(args *colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) { + new: func(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { return NewHashAggregator(args, nil /* newSpillingQueueArgs */, testAllocator, math.MaxInt64) }, name: "tracking=false", order: unordered, }, { - new: func(args *colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) { + new: func(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { spillingQueueMemAcc := testMemMonitor.MakeBoundAccount() memAccounts = append(memAccounts, &spillingQueueMemAcc) return NewHashAggregator(args, &colexecutils.NewSpillingQueueArgs{ @@ -539,7 +539,7 @@ func BenchmarkHashAggregatorPartialOrder(b *testing.B) { groupSizes = []int{1, coldata.BatchSize()} } var memAccounts []*mon.BoundAccount - f := func(args *colexecagg.NewAggregatorArgs) (colexecop.ResettableOperator, error) { + f := func(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { spillingQueueMemAcc := testMemMonitor.MakeBoundAccount() memAccounts = append(memAccounts, &spillingQueueMemAcc) return NewHashAggregator(args, &colexecutils.NewSpillingQueueArgs{ diff --git a/pkg/sql/colexec/mergejoiner_test.go b/pkg/sql/colexec/mergejoiner_test.go index 7b79c75f48d6..77006a79d576 100644 --- a/pkg/sql/colexec/mergejoiner_test.go +++ b/pkg/sql/colexec/mergejoiner_test.go @@ -1718,7 +1718,7 @@ func TestFullOuterMergeJoinWithMaximumNumberOfGroups(t *testing.T) { } leftSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, colsLeft, nTuples) rightSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, colsRight, nTuples) - a, err := colexecjoin.NewMergeJoinOp( + a := colexecjoin.NewMergeJoinOp( testAllocator, execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(mjFDLimit), descpb.FullOuterJoin, leftSource, rightSource, typs, typs, @@ -1726,9 +1726,6 @@ func TestFullOuterMergeJoinWithMaximumNumberOfGroups(t *testing.T) { []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}, testDiskAcc, &evalCtx, ) - if err != nil { - t.Fatal("error in merge join op constructor", err) - } a.Init(ctx) i, count, expVal := 0, 0, int64(0) for b := a.Next(); b.Length() != 0; b = a.Next() { @@ -1791,7 +1788,7 @@ func TestMergeJoinerMultiBatch(t *testing.T) { } leftSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, cols, nTuples) rightSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, cols, nTuples) - a, err := colexecjoin.NewMergeJoinOp( + a := colexecjoin.NewMergeJoinOp( testAllocator, execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(mjFDLimit), descpb.InnerJoin, leftSource, rightSource, typs, typs, @@ -1799,9 +1796,6 @@ func TestMergeJoinerMultiBatch(t *testing.T) { []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}, testDiskAcc, &evalCtx, ) - if err != nil { - t.Fatal("error in merge join op constructor", err) - } a.Init(ctx) i := 0 count := 0 @@ -1869,7 +1863,7 @@ func TestMergeJoinerMultiBatchRuns(t *testing.T) { } leftSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, cols, nTuples) rightSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, cols, nTuples) - a, err := colexecjoin.NewMergeJoinOp( + a := colexecjoin.NewMergeJoinOp( testAllocator, execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(mjFDLimit), descpb.InnerJoin, leftSource, rightSource, typs, typs, @@ -1877,9 +1871,6 @@ func TestMergeJoinerMultiBatchRuns(t *testing.T) { []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}, {ColIdx: 1, Direction: execinfrapb.Ordering_Column_ASC}}, testDiskAcc, &evalCtx, ) - if err != nil { - t.Fatal("error in merge join op constructor", err) - } a.Init(ctx) i := 0 count := 0 @@ -1999,7 +1990,7 @@ func TestMergeJoinerRandomized(t *testing.T) { leftSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, lCols, nTuples) rightSource := colexectestutils.NewChunkingBatchSource(testAllocator, typs, rCols, nTuples) - a, err := colexecjoin.NewMergeJoinOp( + a := colexecjoin.NewMergeJoinOp( testAllocator, execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(mjFDLimit), descpb.InnerJoin, leftSource, rightSource, typs, typs, @@ -2007,9 +1998,6 @@ func TestMergeJoinerRandomized(t *testing.T) { []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}, testDiskAcc, &evalCtx, ) - if err != nil { - t.Fatal("error in merge join op constructor", err) - } a.Init(ctx) i := 0 count := 0 diff --git a/pkg/sql/colexec/ordered_aggregator.go b/pkg/sql/colexec/ordered_aggregator.go index b33dbceb8a76..7ab8ff6bec00 100644 --- a/pkg/sql/colexec/ordered_aggregator.go +++ b/pkg/sql/colexec/ordered_aggregator.go @@ -141,20 +141,15 @@ var _ colexecop.ResettableOperator = &orderedAggregator{} var _ colexecop.ClosableOperator = &orderedAggregator{} // NewOrderedAggregator creates an ordered aggregator. -func NewOrderedAggregator( - args *colexecagg.NewAggregatorArgs, -) (colexecop.ResettableOperator, error) { +func NewOrderedAggregator(args *colexecagg.NewAggregatorArgs) colexecop.ResettableOperator { for _, aggFn := range args.Spec.Aggregations { if aggFn.FilterColIdx != nil { - return nil, errors.AssertionFailedf("filtering ordered aggregation is not supported") + colexecerror.InternalError(errors.AssertionFailedf("filtering ordered aggregation is not supported")) } } - op, groupCol, err := colexecbase.OrderedDistinctColsToOperators( + op, groupCol := colexecbase.OrderedDistinctColsToOperators( args.Input, args.Spec.GroupCols, args.InputTypes, false, /* nullsAreDistinct */ ) - if err != nil { - return nil, err - } // We will be reusing the same aggregate functions, so we use 1 as the // allocation size. @@ -162,9 +157,7 @@ func NewOrderedAggregator( args, args.Spec.Aggregations, 1 /* allocSize */, colexecagg.OrderedAggKind, ) if err != nil { - return nil, errors.AssertionFailedf( - "this error should have been checked in isAggregateSupported\n%+v", err, - ) + colexecerror.InternalError(err) } a := &orderedAggregator{ @@ -178,7 +171,7 @@ func NewOrderedAggregator( toClose: toClose, } a.aggHelper = newAggregatorHelper(args, &a.datumAlloc, false /* isHashAgg */, coldata.BatchSize()) - return a, nil + return a } func (a *orderedAggregator) Init(ctx context.Context) { diff --git a/pkg/sql/colexec/partially_ordered_distinct.go b/pkg/sql/colexec/partially_ordered_distinct.go index 8c6fd89a3a4d..cf3422a2aeb0 100644 --- a/pkg/sql/colexec/partially_ordered_distinct.go +++ b/pkg/sql/colexec/partially_ordered_distinct.go @@ -39,10 +39,7 @@ func newPartiallyOrderedDistinct( "partially ordered distinct wrongfully planned: numDistinctCols=%d "+ "numOrderedCols=%d", len(distinctCols), len(orderedCols)) } - chunker, err := newChunker(allocator, input, typs, orderedCols, nullsAreDistinct) - if err != nil { - return nil, err - } + chunker := newChunker(allocator, input, typs, orderedCols, nullsAreDistinct) chunkerOperator := newChunkerOperator(allocator, chunker, typs) // distinctUnorderedCols will contain distinct columns that are not present // among orderedCols. The unordered distinct operator will use these columns diff --git a/pkg/sql/colexec/sort.eg.go b/pkg/sql/colexec/sort.eg.go index e03af48e4f13..034ab339403e 100644 --- a/pkg/sql/colexec/sort.eg.go +++ b/pkg/sql/colexec/sort.eg.go @@ -34,134 +34,6 @@ var ( _ tree.AggType ) -func isSorterSupported(t *types.T, dir execinfrapb.Ordering_Column_Direction) bool { - switch dir { - case execinfrapb.Ordering_Column_ASC: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - case types.BoolFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.BytesFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.DecimalFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.IntFamily: - switch t.Width() { - case 16: - return true - case 32: - return true - case -1: - default: - return true - } - case types.FloatFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.TimestampTZFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.IntervalFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.JsonFamily: - switch t.Width() { - case -1: - default: - return true - } - case typeconv.DatumVecCanonicalTypeFamily: - switch t.Width() { - case -1: - default: - return true - } - } - case execinfrapb.Ordering_Column_DESC: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - case types.BoolFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.BytesFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.DecimalFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.IntFamily: - switch t.Width() { - case 16: - return true - case 32: - return true - case -1: - default: - return true - } - case types.FloatFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.TimestampTZFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.IntervalFamily: - switch t.Width() { - case -1: - default: - return true - } - case types.JsonFamily: - switch t.Width() { - case -1: - default: - return true - } - case typeconv.DatumVecCanonicalTypeFamily: - switch t.Width() { - case -1: - default: - return true - } - } - } - return false -} - func newSingleSorterWithNulls(t *types.T, dir execinfrapb.Ordering_Column_Direction) colSorter { switch dir { case execinfrapb.Ordering_Column_ASC: diff --git a/pkg/sql/colexec/sort.go b/pkg/sql/colexec/sort.go index ed7f73f157b4..2e88e3230465 100644 --- a/pkg/sql/colexec/sort.go +++ b/pkg/sql/colexec/sort.go @@ -34,7 +34,7 @@ func NewSorter( inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column, maxOutputBatchMemSize int64, -) (colexecop.Operator, error) { +) colexecop.Operator { return newSorter( allocator, newAllSpooler(allocator, input, inputTypes), inputTypes, orderingCols, maxOutputBatchMemSize, @@ -47,22 +47,11 @@ func newSorter( inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column, maxOutputBatchMemSize int64, -) (colexecop.ResettableOperator, error) { +) colexecop.ResettableOperator { partitioners := make([]partitioner, len(orderingCols)-1) - - var err error - for i, ord := range orderingCols { - if !isSorterSupported(inputTypes[ord.ColIdx], ord.Direction) { - return nil, errors.Errorf("sorter for type: %s and direction: %s not supported", inputTypes[ord.ColIdx], ord.Direction) - } - if i < len(orderingCols)-1 { - partitioners[i], err = newPartitioner(inputTypes[ord.ColIdx], false /* nullsAreDistinct */) - if err != nil { - return nil, err - } - } + for i, ord := range orderingCols[:len(orderingCols)-1] { + partitioners[i] = newPartitioner(inputTypes[ord.ColIdx], false /* nullsAreDistinct */) } - return &sortOp{ allocator: allocator, input: input, @@ -72,7 +61,7 @@ func newSorter( orderingCols: orderingCols, state: sortSpooling, maxOutputBatchMemSize: maxOutputBatchMemSize, - }, nil + } } // spooler is a column vector operator that spools the data from its input. diff --git a/pkg/sql/colexec/sort_chunks.go b/pkg/sql/colexec/sort_chunks.go index aff25222ca50..41e854f75a13 100644 --- a/pkg/sql/colexec/sort_chunks.go +++ b/pkg/sql/colexec/sort_chunks.go @@ -35,7 +35,7 @@ func NewSortChunks( orderingCols []execinfrapb.Ordering_Column, matchLen int, maxOutputBatchMemSize int64, -) (colexecop.Operator, error) { +) colexecop.Operator { if matchLen < 1 || matchLen == len(orderingCols) { colexecerror.InternalError(errors.AssertionFailedf( "sort chunks should only be used when the input is "+ @@ -46,15 +46,9 @@ func NewSortChunks( for i := range alreadySortedCols { alreadySortedCols[i] = orderingCols[i].ColIdx } - chunker, err := newChunker(allocator, input, inputTypes, alreadySortedCols, false /* nullsAreDistinct */) - if err != nil { - return nil, err - } - sorter, err := newSorter(allocator, chunker, inputTypes, orderingCols[matchLen:], maxOutputBatchMemSize) - if err != nil { - return nil, err - } - return &sortChunksOp{allocator: allocator, input: chunker, sorter: sorter}, nil + chunker := newChunker(allocator, input, inputTypes, alreadySortedCols, false /* nullsAreDistinct */) + sorter := newSorter(allocator, chunker, inputTypes, orderingCols[matchLen:], maxOutputBatchMemSize) + return &sortChunksOp{allocator: allocator, input: chunker, sorter: sorter} } type sortChunksOp struct { @@ -267,14 +261,10 @@ func newChunker( inputTypes []*types.T, alreadySortedCols []uint32, nullsAreDistinct bool, -) (*chunker, error) { - var err error +) *chunker { partitioners := make([]partitioner, len(alreadySortedCols)) for i, col := range alreadySortedCols { - partitioners[i], err = newPartitioner(inputTypes[col], nullsAreDistinct) - if err != nil { - return nil, err - } + partitioners[i] = newPartitioner(inputTypes[col], nullsAreDistinct) } deselector := colexecutils.NewDeselectorOp(allocator, input, inputTypes) return &chunker{ @@ -285,7 +275,7 @@ func newChunker( nullsAreDistinct: nullsAreDistinct, partitioners: partitioners, state: chunkerReading, - }, nil + } } func (s *chunker) init(ctx context.Context) { diff --git a/pkg/sql/colexec/sort_chunks_test.go b/pkg/sql/colexec/sort_chunks_test.go index f7e80d1052c7..2702e32c4b3d 100644 --- a/pkg/sql/colexec/sort_chunks_test.go +++ b/pkg/sql/colexec/sort_chunks_test.go @@ -189,7 +189,7 @@ func TestSortChunks(t *testing.T) { for _, tc := range sortChunksTestCases { colexectestutils.RunTests(t, testAllocator, []colexectestutils.Tuples{tc.tuples}, tc.expected, colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { - return NewSortChunks(testAllocator, input[0], tc.typs, tc.ordCols, tc.matchLen, execinfra.DefaultMemoryLimit) + return NewSortChunks(testAllocator, input[0], tc.typs, tc.ordCols, tc.matchLen, execinfra.DefaultMemoryLimit), nil }) } } @@ -231,7 +231,7 @@ func TestSortChunksRandomized(t *testing.T) { sort.Slice(expected, less(expected, ordCols)) colexectestutils.RunTests(t, testAllocator, []colexectestutils.Tuples{sortedTups}, expected, colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { - return NewSortChunks(testAllocator, input[0], typs[:nCols], ordCols, matchLen, execinfra.DefaultMemoryLimit) + return NewSortChunks(testAllocator, input[0], typs[:nCols], ordCols, matchLen, execinfra.DefaultMemoryLimit), nil }) } } @@ -243,9 +243,9 @@ func BenchmarkSortChunks(b *testing.B) { rng, _ := randutil.NewTestRand() ctx := context.Background() - sorterConstructors := []func(*colmem.Allocator, colexecop.Operator, []*types.T, []execinfrapb.Ordering_Column, int, int64) (colexecop.Operator, error){ + sorterConstructors := []func(*colmem.Allocator, colexecop.Operator, []*types.T, []execinfrapb.Ordering_Column, int, int64) colexecop.Operator{ NewSortChunks, - func(allocator *colmem.Allocator, input colexecop.Operator, inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column, _ int, maxOutputBatchMemSize int64) (colexecop.Operator, error) { + func(allocator *colmem.Allocator, input colexecop.Operator, inputTypes []*types.T, orderingCols []execinfrapb.Ordering_Column, _ int, maxOutputBatchMemSize int64) colexecop.Operator { return NewSorter(allocator, input, inputTypes, orderingCols, maxOutputBatchMemSize) }, } @@ -275,10 +275,7 @@ func BenchmarkSortChunks(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { source := colexectestutils.NewFiniteChunksSource(testAllocator, batch, typs, nBatches, matchLen) - sorter, err := sorterConstructor(testAllocator, source, typs, ordCols, matchLen, execinfra.DefaultMemoryLimit) - if err != nil { - b.Fatal(err) - } + sorter := sorterConstructor(testAllocator, source, typs, ordCols, matchLen, execinfra.DefaultMemoryLimit) sorter.Init(ctx) for out := sorter.Next(); out.Length() != 0; out = sorter.Next() { diff --git a/pkg/sql/colexec/sort_partitioner.eg.go b/pkg/sql/colexec/sort_partitioner.eg.go index 4aedf3726edc..5b3c7dc2f5e8 100644 --- a/pkg/sql/colexec/sort_partitioner.eg.go +++ b/pkg/sql/colexec/sort_partitioner.eg.go @@ -45,68 +45,70 @@ type partitioner interface { } // newPartitioner returns a new partitioner on type t. -func newPartitioner(t *types.T, nullsAreDistinct bool) (partitioner, error) { +func newPartitioner(t *types.T, nullsAreDistinct bool) partitioner { switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { case types.BoolFamily: switch t.Width() { case -1: default: - return partitionerBool{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerBool{nullsAreDistinct: nullsAreDistinct} } case types.BytesFamily: switch t.Width() { case -1: default: - return partitionerBytes{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerBytes{nullsAreDistinct: nullsAreDistinct} } case types.DecimalFamily: switch t.Width() { case -1: default: - return partitionerDecimal{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerDecimal{nullsAreDistinct: nullsAreDistinct} } case types.IntFamily: switch t.Width() { case 16: - return partitionerInt16{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerInt16{nullsAreDistinct: nullsAreDistinct} case 32: - return partitionerInt32{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerInt32{nullsAreDistinct: nullsAreDistinct} case -1: default: - return partitionerInt64{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerInt64{nullsAreDistinct: nullsAreDistinct} } case types.FloatFamily: switch t.Width() { case -1: default: - return partitionerFloat64{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerFloat64{nullsAreDistinct: nullsAreDistinct} } case types.TimestampTZFamily: switch t.Width() { case -1: default: - return partitionerTimestamp{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerTimestamp{nullsAreDistinct: nullsAreDistinct} } case types.IntervalFamily: switch t.Width() { case -1: default: - return partitionerInterval{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerInterval{nullsAreDistinct: nullsAreDistinct} } case types.JsonFamily: switch t.Width() { case -1: default: - return partitionerJSON{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerJSON{nullsAreDistinct: nullsAreDistinct} } case typeconv.DatumVecCanonicalTypeFamily: switch t.Width() { case -1: default: - return partitionerDatum{nullsAreDistinct: nullsAreDistinct}, nil + return partitionerDatum{nullsAreDistinct: nullsAreDistinct} } } - return nil, errors.Errorf("unsupported partition type %s", t) + colexecerror.InternalError(errors.AssertionFailedf("unsupported type %s", t)) + // This code is unreachable, but the compiler cannot infer that. + return nil } // partitionerBool partitions an arbitrary-length colVec by running a distinct diff --git a/pkg/sql/colexec/sort_test.go b/pkg/sql/colexec/sort_test.go index 91fb0d7fa84a..a39192ddb99c 100644 --- a/pkg/sql/colexec/sort_test.go +++ b/pkg/sql/colexec/sort_test.go @@ -146,7 +146,7 @@ func TestSort(t *testing.T) { for _, tc := range sortAllTestCases { colexectestutils.RunTestsWithTyps(t, testAllocator, []colexectestutils.Tuples{tc.tuples}, [][]*types.T{tc.typs}, tc.expected, colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { - return NewSorter(testAllocator, input[0], tc.typs, tc.ordCols, execinfra.DefaultMemoryLimit) + return NewSorter(testAllocator, input[0], tc.typs, tc.ordCols, execinfra.DefaultMemoryLimit), nil }) } } @@ -178,9 +178,9 @@ func TestSortRandomized(t *testing.T) { } colexectestutils.RunTests(t, testAllocator, []colexectestutils.Tuples{tups}, expected, colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { if topK { - return NewTopKSorter(testAllocator, input[0], typs[:nCols], ordCols, matchLen, uint64(k), execinfra.DefaultMemoryLimit) + return NewTopKSorter(testAllocator, input[0], typs[:nCols], ordCols, matchLen, uint64(k), execinfra.DefaultMemoryLimit), nil } - return NewSorter(testAllocator, input[0], typs[:nCols], ordCols, execinfra.DefaultMemoryLimit) + return NewSorter(testAllocator, input[0], typs[:nCols], ordCols, execinfra.DefaultMemoryLimit), nil }) } } @@ -321,14 +321,10 @@ func BenchmarkSort(b *testing.B) { for n := 0; n < b.N; n++ { source := colexectestutils.NewFiniteBatchSource(testAllocator, batch, typs, nBatches) var sorter colexecop.Operator - var err error if topK { - sorter, err = NewTopKSorter(testAllocator, source, typs, ordCols, 0 /* matchLen */, k, execinfra.DefaultMemoryLimit) + sorter = NewTopKSorter(testAllocator, source, typs, ordCols, 0 /* matchLen */, k, execinfra.DefaultMemoryLimit) } else { - sorter, err = NewSorter(testAllocator, source, typs, ordCols, execinfra.DefaultMemoryLimit) - } - if err != nil { - b.Fatal(err) + sorter = NewSorter(testAllocator, source, typs, ordCols, execinfra.DefaultMemoryLimit) } sorter.Init(ctx) for out := sorter.Next(); out.Length() != 0; out = sorter.Next() { @@ -394,10 +390,7 @@ func BenchmarkSortUUID(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { source := colexectestutils.NewFiniteBatchSource(testAllocator, batch, typs, nBatches) - sorter, err := NewSorter(testAllocator, source, typs, ordCols, execinfra.DefaultMemoryLimit) - if err != nil { - b.Fatal(err) - } + sorter := NewSorter(testAllocator, source, typs, ordCols, execinfra.DefaultMemoryLimit) sorter.Init(ctx) for out := sorter.Next(); out.Length() != 0; out = sorter.Next() { } diff --git a/pkg/sql/colexec/sort_tmpl.go b/pkg/sql/colexec/sort_tmpl.go index c79e12e5e2fc..42bc2cb9a309 100644 --- a/pkg/sql/colexec/sort_tmpl.go +++ b/pkg/sql/colexec/sort_tmpl.go @@ -68,30 +68,6 @@ func _ASSIGN_LT(_, _, _, _, _, _ string) bool { // */}} -func isSorterSupported(t *types.T, dir execinfrapb.Ordering_Column_Direction) bool { - // {{range .}} - // {{if .Nulls}} - switch dir { - // {{range .DirOverloads}} - case _DIR_ENUM: - switch typeconv.TypeFamilyToCanonicalTypeFamily(t.Family()) { - // {{range .FamilyOverloads}} - case _CANONICAL_TYPE_FAMILY: - switch t.Width() { - // {{range .WidthOverloads}} - case _TYPE_WIDTH: - return true - // {{end}} - } - // {{end}} - } - // {{end}} - } - // {{end}} - // {{end}} - return false -} - // {{range .}} // {{$nulls := .Nulls}} func newSingleSorter_WITH_NULLS(t *types.T, dir execinfrapb.Ordering_Column_Direction) colSorter { diff --git a/pkg/sql/colexec/sorttopk.go b/pkg/sql/colexec/sorttopk.go index 28a39ac87896..96b69f1af7eb 100644 --- a/pkg/sql/colexec/sorttopk.go +++ b/pkg/sql/colexec/sorttopk.go @@ -42,9 +42,9 @@ func NewTopKSorter( matchLen int, k uint64, maxOutputBatchMemSize int64, -) (colexecop.ResettableOperator, error) { +) colexecop.ResettableOperator { if matchLen < 0 { - return nil, errors.AssertionFailedf("invalid matchLen %v", matchLen) + colexecerror.InternalError(errors.AssertionFailedf("invalid matchLen %v", matchLen)) } base := &topKSorter{ allocator: allocator, @@ -62,18 +62,14 @@ func NewTopKSorter( for i := range partialOrderCols { partialOrderCols[i] = orderingCols[i].ColIdx } - var err error base.orderState.distincterInput = &colexecop.FeedOperator{} - base.orderState.distincter, base.orderState.distinctOutput, err = colexecbase.OrderedDistinctColsToOperators( + base.orderState.distincter, base.orderState.distinctOutput = colexecbase.OrderedDistinctColsToOperators( base.orderState.distincterInput, partialOrderCols, inputTypes, false, /* nullsAreDistinct */ ) - if err != nil { - return base, err - } } else { base.heaper = &topKHeaper{base} } - return base, nil + return base } var _ colexecop.BufferingInMemoryOperator = &topKSorter{} diff --git a/pkg/sql/colexec/sorttopk_test.go b/pkg/sql/colexec/sorttopk_test.go index 0669b0cf41b7..2d210a90c15e 100644 --- a/pkg/sql/colexec/sorttopk_test.go +++ b/pkg/sql/colexec/sorttopk_test.go @@ -113,7 +113,7 @@ func TestTopKSorter(t *testing.T) { for _, tc := range topKSortTestCases { log.Infof(context.Background(), "%s", tc.description) colexectestutils.RunTests(t, testAllocator, []colexectestutils.Tuples{tc.tuples}, tc.expected, colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { - return NewTopKSorter(testAllocator, input[0], tc.typs, tc.ordCols, tc.matchLen, tc.k, execinfra.DefaultMemoryLimit) + return NewTopKSorter(testAllocator, input[0], tc.typs, tc.ordCols, tc.matchLen, tc.k, execinfra.DefaultMemoryLimit), nil }) } } @@ -140,10 +140,7 @@ func TestTopKSortRandomized(t *testing.T) { input := colexectestutils.NewOpTestInput(testAllocator, coldata.BatchSize(), tups, typs[:nCols]) // Use a normal sorter that sorts on the ordered columns as an oracle to // compare with the top k sorter's output. - oracle, err := NewSorter(testAllocator, input, typs[:nCols], ordCols, execinfra.DefaultMemoryLimit) - if err != nil { - t.Fatal(err) - } + oracle := NewSorter(testAllocator, input, typs[:nCols], ordCols, execinfra.DefaultMemoryLimit) oracle.Init(ctx) var expected colexectestutils.Tuples for expectedOut := oracle.Next(); expectedOut.Length() != 0; expectedOut = oracle.Next() { @@ -157,8 +154,7 @@ func TestTopKSortRandomized(t *testing.T) { colexectestutils.RunTests(t, testAllocator, []colexectestutils.Tuples{tups}, expected[:k], colexectestutils.OrderedVerifier, func(input []colexecop.Operator) (colexecop.Operator, error) { - return NewTopKSorter(testAllocator, input[0], typs[:nCols], ordCols, matchLen, uint64(k), - execinfra.DefaultMemoryLimit) + return NewTopKSorter(testAllocator, input[0], typs[:nCols], ordCols, matchLen, uint64(k), execinfra.DefaultMemoryLimit), nil }) } } @@ -196,12 +192,8 @@ func BenchmarkSortTopK(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { var sorter colexecop.Operator - var err error source := colexectestutils.NewFiniteChunksSource(testAllocator, batch, typs, nBatches, matchLen) - sorter, err = NewTopKSorter(testAllocator, source, typs, ordCols, matchLen, k, execinfra.DefaultMemoryLimit) - if err != nil { - b.Fatal(err) - } + sorter = NewTopKSorter(testAllocator, source, typs, ordCols, matchLen, k, execinfra.DefaultMemoryLimit) sorter.Init(ctx) for out := sorter.Next(); out.Length() != 0; out = sorter.Next() { } diff --git a/pkg/sql/colflow/stats_test.go b/pkg/sql/colflow/stats_test.go index 01ac67a2e787..6639a064c82a 100644 --- a/pkg/sql/colflow/stats_test.go +++ b/pkg/sql/colflow/stats_test.go @@ -116,7 +116,7 @@ func TestVectorizedStatsCollector(t *testing.T) { timeutil.NewTestStopWatch(timeSource.Now), nil /* memMonitors */, nil, /* diskMonitors */ nil, /* inputStatsCollectors */ ) - mergeJoiner, err := colexecjoin.NewMergeJoinOp( + mergeJoiner := colexecjoin.NewMergeJoinOp( tu.testAllocator, execinfra.DefaultMemoryLimit, queueCfg, colexecop.NewTestingSemaphore(4), descpb.InnerJoin, leftInput, rightInput, []*types.T{types.Int}, []*types.T{types.Int}, @@ -124,9 +124,6 @@ func TestVectorizedStatsCollector(t *testing.T) { []execinfrapb.Ordering_Column{{ColIdx: 0}}, tu.testDiskAcc, tu.evalCtx, ) - if err != nil { - t.Fatal(err) - } timeAdvancingMergeJoiner := &timeAdvancingOperator{ OneInputHelper: colexecop.MakeOneInputHelper(mergeJoiner), timeSource: timeSource, From 3f0a6ce52c0f781e6079cad27601e84f1ab6d36e Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 27 Oct 2021 10:02:45 +1100 Subject: [PATCH 079/205] sql: fix setting DateStyle/IntervalStyle on -c Release note (bug fix): Previously, specifying IntervalStyle or DateStyle on `options=-c...` in a pgurl would fail, even if the sql.defaults.datestyle.enabled and sql.defaults.intervalstyle.enabled cluster settings were set. This has now been resolved. --- .../interactive_tests/test_style_enabled.tcl | 39 +++++++++++++++++++ pkg/sql/discard.go | 30 ++++++++++---- 2 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 pkg/cli/interactive_tests/test_style_enabled.tcl diff --git a/pkg/cli/interactive_tests/test_style_enabled.tcl b/pkg/cli/interactive_tests/test_style_enabled.tcl new file mode 100644 index 000000000000..b421e36d3ec5 --- /dev/null +++ b/pkg/cli/interactive_tests/test_style_enabled.tcl @@ -0,0 +1,39 @@ +#! /usr/bin/env expect -f + +source [file join [file dirname $argv0] common.tcl] + +start_server $argv + +# Setup. +spawn $argv sql +eexpect root@ +send "SET CLUSTER SETTING sql.defaults.intervalstyle.enabled = true;\r" +eexpect "SET CLUSTER SETTING" +eexpect root@ +send "SET CLUSTER SETTING sql.defaults.datestyle.enabled = true;\r" +eexpect "SET CLUSTER SETTING" +eexpect root@ +interrupt +eexpect eof + + +# Try connect and check the session variables match. + +spawn $argv sql --url "postgresql://test@localhost:26257?options=-cintervalstyle%3Diso_8601" +eexpect root@ +send "SHOW intervalstyle;\r" +eexpect "iso_8601" +eexpect root@ +interrupt +eexpect eof + +# TODO(#72065): uncomment +#spawn $argv sql --url "postgresql://test@localhost:26257?options=-cdatestyle%3Dymd" +#eexpect root@ +#send "SHOW datestyle;\r" +#eexpect "ISO, YMD" +#eexpect root@ +#interrupt +#eexpect eof + +stop_server $argv diff --git a/pkg/sql/discard.go b/pkg/sql/discard.go index 67abaa9c7936..258987d26048 100644 --- a/pkg/sql/discard.go +++ b/pkg/sql/discard.go @@ -47,14 +47,30 @@ func (p *planner) Discard(ctx context.Context, s *tree.Discard) (planNode, error } func resetSessionVars(ctx context.Context, m sessionDataMutator) error { + // Always do intervalstyle_enabled and datestyle_enabled first so that + // IntervalStyle and DateStyle which depend on these flags are correctly + // configured. + if err := resetSessionVar(ctx, m, "datestyle_enabled"); err != nil { + return err + } + if err := resetSessionVar(ctx, m, "intervalstyle_enabled"); err != nil { + return err + } for _, varName := range varNames { - v := varGen[varName] - if v.Set != nil { - hasDefault, defVal := getSessionVarDefaultString(varName, v, m.sessionDataMutatorBase) - if hasDefault { - if err := v.Set(ctx, m, defVal); err != nil { - return err - } + if err := resetSessionVar(ctx, m, varName); err != nil { + return err + } + } + return nil +} + +func resetSessionVar(ctx context.Context, m sessionDataMutator, varName string) error { + v := varGen[varName] + if v.Set != nil { + hasDefault, defVal := getSessionVarDefaultString(varName, v, m.sessionDataMutatorBase) + if hasDefault { + if err := v.Set(ctx, m, defVal); err != nil { + return err } } } From a638b70125a8ecf567005b08ca3880c0b0823a0a Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Thu, 28 Oct 2021 07:56:21 +1100 Subject: [PATCH 080/205] sql: fix datestyle/intervalstyle passed in connection string Release note (bug fix): Previously, session variables passed in the connection string were case-sensitive. Now they are all correctly normalized to lower case. --- pkg/sql/pgwire/conn_test.go | 56 ++++++++++++++++++++++++++----------- pkg/sql/pgwire/server.go | 1 + 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/pkg/sql/pgwire/conn_test.go b/pkg/sql/pgwire/conn_test.go index 262ee148311e..52eb79e8255c 100644 --- a/pkg/sql/pgwire/conn_test.go +++ b/pkg/sql/pgwire/conn_test.go @@ -1278,13 +1278,6 @@ func TestParseClientProvidedSessionParameters(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) - // The test server is used only incidentally by this test: this is not the - // server that the client will connect to; we just use it on the side to - // execute some metadata queries that pgx sends whenever it opens a - // connection. - s, _, _ := serverutils.StartServer(t, base.TestServerArgs{Insecure: true, UseDatabase: "system"}) - defer s.Stopper().Stop(context.Background()) - // Start a pgwire "server". addr := util.TestAddr ln, err := net.Listen(addr.Network(), addr.String()) @@ -1447,6 +1440,22 @@ func TestParseClientProvidedSessionParameters(t *testing.T) { require.Equal(t, "2.3.4.5:5432", args.RemoteAddr.String()) }, }, + { + desc: "normalize to lower case in options parameter", + query: "user=root&options=-c DateStyle=YMD,ISO", + assert: func(t *testing.T, args sql.SessionArgs, err error) { + require.NoError(t, err) + require.Equal(t, "YMD,ISO", args.SessionDefaults["datestyle"]) + }, + }, + { + desc: "normalize to lower case in query parameters", + query: "user=root&DateStyle=ISO,YMD", + assert: func(t *testing.T, args sql.SessionArgs, err error) { + require.NoError(t, err) + require.Equal(t, "ISO,YMD", args.SessionDefaults["datestyle"]) + }, + }, } baseURL := fmt.Sprintf("postgres://%s/system?sslmode=disable", serverAddr) @@ -1454,20 +1463,22 @@ func TestParseClientProvidedSessionParameters(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { + var connErr error go func() { - url := fmt.Sprintf("%s&%s", baseURL, tc.query) - c, err := gosql.Open("postgres", url) - require.NoError(t, err) - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() + url := fmt.Sprintf("%s&%s", baseURL, tc.query) + var c *pgx.Conn + c, connErr = pgx.Connect(ctx, url) + if connErr != nil { + return + } // ignore the error because there is no answer from the server, we are // interested in parsing session arguments only - _ = c.PingContext(ctx) + _ = c.Ping(ctx) // closing connection immediately, since getSessionArgs is blocking - _ = c.Close() + _ = c.Close(ctx) }() - // Wait for the client to connect and perform the handshake. _, args, err := getSessionArgs(ln, true /* trustRemoteAddr */) tc.assert(t, args, err) @@ -1478,9 +1489,15 @@ func TestParseClientProvidedSessionParameters(t *testing.T) { func TestSetSessionArguments(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) - s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) + s, db, _ := serverutils.StartServer(t, base.TestServerArgs{}) ctx := context.Background() defer s.Stopper().Stop(ctx) + defer db.Close() + + _, err := db.Exec("SET CLUSTER SETTING sql.defaults.datestyle.enabled = true") + require.NoError(t, err) + _, err = db.Exec("SET CLUSTER SETTING sql.defaults.intervalstyle.enabled = true") + require.NoError(t, err) pgURL, cleanupFunc := sqlutils.PGUrl( t, s.ServingSQLAddr(), "testConnClose" /* prefix */, url.User(security.RootUser), @@ -1488,7 +1505,11 @@ func TestSetSessionArguments(t *testing.T) { defer cleanupFunc() q := pgURL.Query() - q.Add("options", " --user=test -c search_path=public,testsp %20 --default-transaction-isolation=read\\ uncommitted -capplication_name=test --datestyle=iso\\ ,\\ mdy\\ ") + q.Add("options", " --user=test -c search_path=public,testsp %20 "+ + "--default-transaction-isolation=read\\ uncommitted "+ + "-capplication_name=test "+ + "--DateStyle=ymd\\ ,\\ iso\\ "+ + "-c intervalstyle%3DISO_8601") pgURL.RawQuery = q.Encode() noBufferDB, err := gosql.Open("postgres", pgURL.String()) @@ -1518,7 +1539,8 @@ func TestSetSessionArguments(t *testing.T) { // all transactions execute with serializable isolation. "default_transaction_isolation": "serializable", "application_name": "test", - "datestyle": "ISO, MDY", + "datestyle": "ISO, YMD", + "intervalstyle": "iso_8601", } expectedFoundOptions := len(expectedOptions) diff --git a/pkg/sql/pgwire/server.go b/pkg/sql/pgwire/server.go index 8e42f3dc1fe2..ec6450a411a8 100644 --- a/pkg/sql/pgwire/server.go +++ b/pkg/sql/pgwire/server.go @@ -848,6 +848,7 @@ func parseClientProvidedSessionParameters( } func loadParameter(ctx context.Context, key, value string, args *sql.SessionArgs) error { + key = strings.ToLower(key) exists, configurable := sql.IsSessionVariableConfigurable(key) switch { From 3b3f7e738e70da63dbbdeb238e81ab212f7aa78e Mon Sep 17 00:00:00 2001 From: Jordan Lewis Date: Fri, 15 Oct 2021 19:02:35 -0300 Subject: [PATCH 081/205] sqlsmith: create constant for "rand-tables" setup This commit is a pure refactor that pulls out a named constant for the string "rand-tables", which was used to denote a particular SQLSmith test Setup object. Release note: None --- pkg/cmd/roachtest/tests/sqlsmith.go | 6 +++--- pkg/cmd/roachtest/tests/tlp.go | 2 +- pkg/compose/compare/compare/compare_test.go | 4 ++-- pkg/internal/sqlsmith/setup.go | 7 +++++-- pkg/sql/tests/rsg_test.go | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/roachtest/tests/sqlsmith.go b/pkg/cmd/roachtest/tests/sqlsmith.go index 6dc9662a7167..402426591110 100644 --- a/pkg/cmd/roachtest/tests/sqlsmith.go +++ b/pkg/cmd/roachtest/tests/sqlsmith.go @@ -31,9 +31,9 @@ import ( func registerSQLSmith(r registry.Registry) { const numNodes = 4 setups := map[string]sqlsmith.Setup{ - "empty": sqlsmith.Setups["empty"], - "seed": sqlsmith.Setups["seed"], - "rand-tables": sqlsmith.Setups["rand-tables"], + "empty": sqlsmith.Setups["empty"], + "seed": sqlsmith.Setups["seed"], + sqlsmith.RandTableSetupName: sqlsmith.Setups[sqlsmith.RandTableSetupName], "tpch-sf1": func(r *rand.Rand) string { return `RESTORE TABLE tpch.* FROM 'gs://cockroach-fixtures/workload/tpch/scalefactor=1/backup?AUTH=implicit' WITH into_db = 'defaultdb';` }, diff --git a/pkg/cmd/roachtest/tests/tlp.go b/pkg/cmd/roachtest/tests/tlp.go index 4847992bd6c1..66fb83ebdf98 100644 --- a/pkg/cmd/roachtest/tests/tlp.go +++ b/pkg/cmd/roachtest/tests/tlp.go @@ -75,7 +75,7 @@ func runTLP(ctx context.Context, t test.Test, c cluster.Cluster) { } c.Start(ctx) - setup := sqlsmith.Setups["rand-tables"](rnd) + setup := sqlsmith.Setups[sqlsmith.RandTableSetupName](rnd) t.Status("executing setup") t.L().Printf("setup:\n%s", setup) diff --git a/pkg/compose/compare/compare/compare_test.go b/pkg/compose/compare/compare/compare_test.go index aa0f0c7018a5..156e03e9b1fe 100644 --- a/pkg/compose/compare/compare/compare_test.go +++ b/pkg/compose/compare/compare/compare_test.go @@ -69,7 +69,7 @@ func TestCompare(t *testing.T) { } configs := map[string]testConfig{ "postgres": { - setup: sqlsmith.Setups["rand-tables"], + setup: sqlsmith.Setups[sqlsmith.RandTableSetupName], setupMutators: []randgen.Mutator{randgen.PostgresCreateTableMutator}, opts: []sqlsmith.SmitherOption{sqlsmith.PostgresMode()}, ignoreSQLErrors: true, @@ -85,7 +85,7 @@ func TestCompare(t *testing.T) { }, }, "mutators": { - setup: sqlsmith.Setups["rand-tables"], + setup: sqlsmith.Setups[sqlsmith.RandTableSetupName], opts: []sqlsmith.SmitherOption{sqlsmith.CompareMode()}, ignoreSQLErrors: true, conns: []testConn{ diff --git a/pkg/internal/sqlsmith/setup.go b/pkg/internal/sqlsmith/setup.go index df95e613996f..7b8aabfd156e 100644 --- a/pkg/internal/sqlsmith/setup.go +++ b/pkg/internal/sqlsmith/setup.go @@ -24,6 +24,9 @@ import ( // for smithing. type Setup func(*rand.Rand) string +// RandTableSetupName is the name of the table setup that creates random tables. +const RandTableSetupName = "rand-tables" + // Setups is a collection of useful initial table states. var Setups = map[string]Setup{ "empty": wrapCommonSetup(stringSetup("")), @@ -32,8 +35,8 @@ var Setups = map[string]Setup{ "seed": wrapCommonSetup(stringSetup(seedTable)), // seed-vec is like seed except only types supported by vectorized // execution are used. - "seed-vec": wrapCommonSetup(stringSetup(vecSeedTable)), - "rand-tables": wrapCommonSetup(randTables), + "seed-vec": wrapCommonSetup(stringSetup(vecSeedTable)), + RandTableSetupName: wrapCommonSetup(randTables), } // wrapCommonSetup wraps setup steps common to all SQLSmith setups around the diff --git a/pkg/sql/tests/rsg_test.go b/pkg/sql/tests/rsg_test.go index 3016af98c3c1..152bd9fd0249 100644 --- a/pkg/sql/tests/rsg_test.go +++ b/pkg/sql/tests/rsg_test.go @@ -556,7 +556,7 @@ func TestRandomSyntaxSQLSmith(t *testing.T) { tableStmts := make([]string, 0) testRandomSyntax(t, true, "defaultdb", func(ctx context.Context, db *verifyFormatDB, r *rsg.RSG) error { - setups := []string{"rand-tables", "seed"} + setups := []string{sqlsmith.RandTableSetupName, "seed"} for _, s := range setups { randTables := sqlsmith.Setups[s](r.Rnd) if err := db.exec(t, ctx, randTables); err != nil { From bc8aecac09a6adbd1b930915b73571d052b75663 Mon Sep 17 00:00:00 2001 From: Jordan Lewis Date: Fri, 15 Oct 2021 19:03:33 -0300 Subject: [PATCH 082/205] backupccl: add random backup/restore test This test creates random tables using SQLSmith's random table generator, then backs up and restores them, checking for schema roundtrippability. It checks both full DB backup/restore as well as all per-table backup/restore for all subsets of the random tables. Release note: None --- pkg/ccl/backupccl/BUILD.bazel | 2 + pkg/ccl/backupccl/backup_rand_test.go | 173 ++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 pkg/ccl/backupccl/backup_rand_test.go diff --git a/pkg/ccl/backupccl/BUILD.bazel b/pkg/ccl/backupccl/BUILD.bazel index 7692afaaa7a9..6a1fd0ccc470 100644 --- a/pkg/ccl/backupccl/BUILD.bazel +++ b/pkg/ccl/backupccl/BUILD.bazel @@ -132,6 +132,7 @@ go_test( "backup_cloud_test.go", "backup_destination_test.go", "backup_intents_test.go", + "backup_rand_test.go", "backup_test.go", "bench_test.go", "create_scheduled_backup_test.go", @@ -169,6 +170,7 @@ go_test( "//pkg/cloud/nodelocal", "//pkg/config", "//pkg/config/zonepb", + "//pkg/internal/sqlsmith", "//pkg/jobs", "//pkg/jobs/jobspb", "//pkg/jobs/jobsprotectedts", diff --git a/pkg/ccl/backupccl/backup_rand_test.go b/pkg/ccl/backupccl/backup_rand_test.go new file mode 100644 index 000000000000..886b907381bc --- /dev/null +++ b/pkg/ccl/backupccl/backup_rand_test.go @@ -0,0 +1,173 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package backupccl + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/internal/sqlsmith" + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/skip" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/randutil" + "github.com/stretchr/testify/assert" +) + +// TestBackupRestoreRandomDataRoundtrips creates random tables using SQLSmith +// and backs up and restores them, ensuring that the schema is properly +// preserved across the roundtrip. It tests that full database backup as well +// as all subsets of per-table backup roundtrip properly. +func TestBackupRestoreRandomDataRoundtrips(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + skip.UnderRace(t, "takes >1 min under race") + rng, _ := randutil.NewPseudoRand() + dir, dirCleanupFn := testutils.TempDir(t) + defer dirCleanupFn() + params := base.TestClusterArgs{ + ServerArgs: base.TestServerArgs{ + UseDatabase: "rand", + ExternalIODir: dir, + }, + } + ctx := context.Background() + tc := testcluster.StartTestCluster(t, singleNode, params) + defer tc.Stopper().Stop(ctx) + InitManualReplication(tc) + sqlDB := sqlutils.MakeSQLRunner(tc.Conns[0]) + sqlDB.Exec(t, "CREATE DATABASE rand") + + setup := sqlsmith.Setups[sqlsmith.RandTableSetupName](rng) + if _, err := tc.Conns[0].Exec(setup); err != nil { + t.Fatal(err) + } + + tables := sqlDB.Query(t, `SELECT name FROM crdb_internal.tables WHERE +database_name = 'rand' AND schema_name = 'public'`) + var tableNames []string + for tables.Next() { + var tableName string + if err := tables.Scan(&tableName); err != nil { + t.Fatal(err) + } + tableNames = append(tableNames, tableName) + } + + getCreateStatementForTable := func(tableName string) string { + var ignored, createStatement string + table := sqlDB.QueryRow(t, fmt.Sprintf("SHOW CREATE TABLE %s", tableName)) + table.Scan(&ignored, &createStatement) + return createStatement + } + + expectedCreateTableStmt := make(map[string]string) + for _, tableName := range tableNames { + createStatement := getCreateStatementForTable(fmt.Sprintf("rand.%s", tableName)) + expectedCreateTableStmt[tableName] = createStatement + } + + // TODO(jordan): we should insert random data using SQLSmith mutation + // statements here. + + // Now that we've created our random tables, backup and restore the whole DB + // and compare all table descriptors for equality. + + dbBackup := LocalFoo + "wholedb" + tablesBackup := LocalFoo + "alltables" + dbBackups := []string{dbBackup, tablesBackup} + + if err := verifyBackupRestoreStatementResult( + t, sqlDB, "BACKUP DATABASE rand TO $1", dbBackup, + ); err != nil { + t.Fatal(err) + } + if err := verifyBackupRestoreStatementResult( + t, sqlDB, "BACKUP TABLE rand.* TO $1", tablesBackup, + ); err != nil { + t.Fatal(err) + } + + // verifyTables asserts that the list of input tables in the restored + // database, restoredb, contains the same schema as the original randomly + // generated tables. + verifyTables := func(t *testing.T, tableNames []string) { + for _, tableName := range tableNames { + t.Logf("Verifying table %s", tableName) + createStatement := getCreateStatementForTable("restoredb." + tableName) + assert.Equal(t, expectedCreateTableStmt[tableName], createStatement, "SHOW CREATE %s not equal after RESTORE", tableName) + } + } + + // This loop tests that two kinds of table restores (full database restore + // and per-table restores) work properly with two kinds of table backups + // (full database backups and per-table backups). + for _, backup := range dbBackups { + sqlDB.Exec(t, "DROP DATABASE IF EXISTS restoredb; CREATE DATABASE restoredb") + if err := verifyBackupRestoreStatementResult( + t, sqlDB, "RESTORE rand.* FROM $1 WITH OPTIONS (into_db='restoredb')", backup, + ); err != nil { + t.Fatal(err) + } + verifyTables(t, tableNames) + sqlDB.Exec(t, "DROP DATABASE IF EXISTS restoredb") + + if err := verifyBackupRestoreStatementResult( + t, sqlDB, "RESTORE DATABASE rand FROM $1 WITH OPTIONS (new_db_name='restoredb')", backup, + ); err != nil { + t.Fatal(err) + } + verifyTables(t, tableNames) + } + + tableNameCombos := powerset(tableNames) + + for i, combo := range tableNameCombos { + sqlDB.Exec(t, "DROP DATABASE IF EXISTS restoredb; CREATE DATABASE restoredb") + backupTarget := fmt.Sprintf("%s%d", LocalFoo, i) + if len(combo) == 0 { + continue + } + tables := strings.Join(combo, ", ") + t.Logf("Testing subset backup/restore %s", tables) + sqlDB.Exec(t, fmt.Sprintf(`BACKUP TABLE %s TO $1`, tables), backupTarget) + _, err := tc.Conns[0].Exec(fmt.Sprintf("RESTORE TABLE %s FROM $1 WITH OPTIONS (into_db='restoredb')", tables), + backupTarget) + if err != nil { + if strings.Contains(err.Error(), "skip_missing_foreign_keys") { + // Ignore subset, since we can't restore subsets that don't include the + // full foreign key graph for any of the contained tables. + continue + } + t.Fatal(err) + } + verifyTables(t, combo) + t.Log("combo", i, combo) + } +} + +// powerset returns the powerset of the input slice of strings - all subsets, +// including the empty subset. +func powerset(input []string) [][]string { + return powersetHelper(input, []string{}) +} + +func powersetHelper(ps, new []string) [][]string { + if len(ps) == 0 { + return [][]string{new} + } + res := powersetHelper(ps[1:], new[:len(new):len(new)]) + return append(res, powersetHelper(ps[1:], append(new, ps[0]))...) +} From 70155bcb93275c5dbfbe397582506ec87d6fbf3a Mon Sep 17 00:00:00 2001 From: Jackson Owens Date: Thu, 28 Oct 2021 09:40:17 -0400 Subject: [PATCH 083/205] ccl/changefeedccl: skip TestChangefeedColumnFamily Refs: #71796 Reason: flaky test Generated by bin/skip-test. Release justification: non-production code changes Release note: None --- pkg/ccl/changefeedccl/changefeed_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/ccl/changefeedccl/changefeed_test.go b/pkg/ccl/changefeedccl/changefeed_test.go index 83a9e2d7b1ff..2c436edc014c 100644 --- a/pkg/ccl/changefeedccl/changefeed_test.go +++ b/pkg/ccl/changefeedccl/changefeed_test.go @@ -1315,6 +1315,7 @@ func TestChangefeedAfterSchemaChangeBackfill(t *testing.T) { func TestChangefeedColumnFamily(t *testing.T) { defer leaktest.AfterTest(t)() + skip.WithIssue(t, 71796, "flaky test") defer log.Scope(t).Close(t) testFn := func(t *testing.T, db *gosql.DB, f cdctest.TestFeedFactory) { From 07913070dd071297e4e3c38a680029d338f1531d Mon Sep 17 00:00:00 2001 From: Jackson Owens Date: Thu, 28 Oct 2021 09:46:49 -0400 Subject: [PATCH 084/205] storage/metamorphic: skip TestPebbleEquivalence Refs: #72077 Reason: flaky test Generated by bin/skip-test. Release justification: non-production code changes Release note: None --- pkg/storage/metamorphic/meta_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index cc1fdbf339d3..177d60a79cca 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -162,6 +162,7 @@ func runMetaTest(run testRun) { // for matching outputs by the test suite between different options of Pebble. func TestPebbleEquivalence(t *testing.T) { defer leaktest.AfterTest(t)() + skip.WithIssue(t, 72077, "flaky test") defer log.Scope(t).Close(t) ctx := context.Background() From 4192c24e394b58b4730886b0bb8f692e1538c00c Mon Sep 17 00:00:00 2001 From: Jackson Owens Date: Thu, 28 Oct 2021 09:47:57 -0400 Subject: [PATCH 085/205] storage/metamorphic: skip TestPebbleRestarts Refs: #72078 Reason: flaky test Generated by bin/skip-test. Release justification: non-production code changes Release note: None --- pkg/storage/metamorphic/meta_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index cc1fdbf339d3..c20f37760c18 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -209,6 +209,7 @@ func TestPebbleEquivalence(t *testing.T) { // engine sequences with restarts in between. func TestPebbleRestarts(t *testing.T) { defer leaktest.AfterTest(t)() + skip.WithIssue(t, 72078, "flaky test") defer log.Scope(t).Close(t) // This test times out with the race detector enabled. skip.UnderRace(t) From 7e5741bfc663e11427eac9f6618804562aec2351 Mon Sep 17 00:00:00 2001 From: sumeerbhola Date: Fri, 6 Aug 2021 10:33:37 -0400 Subject: [PATCH 086/205] admission: enable admission control Also updated existing roachtests that explicitly enabled admission control to now disable admission control, so that we can continue to compare performance of the two settings. Release note (ops change): The cluster settings affecting the admission control system enablement are now set to defaults that enable admission control. --- .../settings/settings-for-tenants.txt | 6 +-- docs/generated/settings/settings.html | 6 +-- pkg/cmd/roachtest/tests/kv.go | 47 +++++++++---------- pkg/cmd/roachtest/tests/tpcc.go | 30 +++++------- pkg/cmd/roachtest/tests/util.go | 14 ++++-- pkg/util/admission/work_queue.go | 6 +-- 6 files changed, 52 insertions(+), 57 deletions(-) diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index 75d1eee94387..7c24361ae39e 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -1,7 +1,7 @@ Setting Type Default Description -admission.kv.enabled boolean false when true, work performed by the KV layer is subject to admission control -admission.sql_kv_response.enabled boolean false when true, work performed by the SQL layer when receiving a KV response is subject to admission control -admission.sql_sql_response.enabled boolean false when true, work performed by the SQL layer when receiving a DistSQL response is subject to admission control +admission.kv.enabled boolean true when true, work performed by the KV layer is subject to admission control +admission.sql_kv_response.enabled boolean true when true, work performed by the SQL layer when receiving a KV response is subject to admission control +admission.sql_sql_response.enabled boolean true when true, work performed by the SQL layer when receiving a DistSQL response is subject to admission control bulkio.backup.file_size byte size 128 MiB target size for individual data files produced during BACKUP bulkio.backup.read_timeout duration 5m0s amount of time after which a read attempt is considered timed out, which causes the backup to fail bulkio.backup.read_with_priority_after duration 1m0s amount of time since the read-as-of time above which a BACKUP should use priority when retrying reads diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 648554482454..8924c5c2b7f6 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -1,9 +1,9 @@ - - - + + + diff --git a/pkg/cmd/roachtest/tests/kv.go b/pkg/cmd/roachtest/tests/kv.go index 77790420a44e..1fc59cdfb4fb 100644 --- a/pkg/cmd/roachtest/tests/kv.go +++ b/pkg/cmd/roachtest/tests/kv.go @@ -48,15 +48,15 @@ func registerKV(r registry.Registry) { blockSize int splits int // 0 implies default, negative implies 0 // If true, load-based splitting will be disabled. - disableLoadSplits bool - encryption bool - sequential bool - admissionControlEnabled bool - concMultiplier int - duration time.Duration - tracing bool // `trace.debug.enable` - tags []string - owner registry.Owner // defaults to KV + disableLoadSplits bool + encryption bool + sequential bool + admissionControlDisabled bool + concMultiplier int + duration time.Duration + tracing bool // `trace.debug.enable` + tags []string + owner registry.Owner // defaults to KV } computeNumSplits := func(opts kvOptions) int { // TODO(ajwerner): set this default to a more sane value or remove it and @@ -89,9 +89,7 @@ func registerKV(r registry.Registry) { t.Fatalf("failed to enable tracing: %v", err) } } - if opts.admissionControlEnabled { - EnableAdmissionControl(ctx, t, c) - } + SetAdmissionControl(ctx, t, c, !opts.admissionControlDisabled) t.Status("running workload") m := c.NewMonitor(ctx, c.Range(1, nodes)) @@ -150,13 +148,8 @@ func registerKV(r registry.Registry) { // CPU overload test, to stress admission control. {nodes: 1, cpus: 8, readPercent: 50, concMultiplier: 8192, duration: 20 * time.Minute}, // IO write overload test, to stress admission control. - // - // TODO(sumeerbhola): re-enable when admission control is enabled by default. This - // test crashes the cluster every now and then. See: - // https://github.com/cockroachdb/cockroach/issues/70247 - // - // {nodes: 1, cpus: 8, readPercent: 0, concMultiplier: 4096, blockSize: 1 << 16, /* 64 KB */ - // duration: 20 * time.Minute}, + {nodes: 1, cpus: 8, readPercent: 0, concMultiplier: 4096, blockSize: 1 << 16, /* 64 KB */ + duration: 20 * time.Minute}, {nodes: 1, cpus: 8, readPercent: 95}, {nodes: 1, cpus: 32, readPercent: 0}, {nodes: 1, cpus: 32, readPercent: 95}, @@ -167,8 +160,8 @@ func registerKV(r registry.Registry) { {nodes: 3, cpus: 8, readPercent: 95, splits: -1 /* no splits */}, {nodes: 3, cpus: 32, readPercent: 0}, {nodes: 3, cpus: 32, readPercent: 95}, - {nodes: 3, cpus: 32, readPercent: 0, admissionControlEnabled: true}, - {nodes: 3, cpus: 32, readPercent: 95, admissionControlEnabled: true}, + {nodes: 3, cpus: 32, readPercent: 0, admissionControlDisabled: true}, + {nodes: 3, cpus: 32, readPercent: 95, admissionControlDisabled: true}, {nodes: 3, cpus: 32, readPercent: 0, splits: -1 /* no splits */}, {nodes: 3, cpus: 32, readPercent: 95, splits: -1 /* no splits */}, @@ -182,9 +175,9 @@ func registerKV(r registry.Registry) { {nodes: 3, cpus: 32, readPercent: 0, blockSize: 1 << 16 /* 64 KB */}, {nodes: 3, cpus: 32, readPercent: 95, blockSize: 1 << 16 /* 64 KB */}, {nodes: 3, cpus: 32, readPercent: 0, blockSize: 1 << 16, /* 64 KB */ - admissionControlEnabled: true}, + admissionControlDisabled: true}, {nodes: 3, cpus: 32, readPercent: 95, blockSize: 1 << 16, /* 64 KB */ - admissionControlEnabled: true}, + admissionControlDisabled: true}, // Configs with large batch sizes. {nodes: 3, cpus: 8, readPercent: 0, batchSize: 16}, @@ -241,8 +234,8 @@ func registerKV(r registry.Registry) { if opts.sequential { nameParts = append(nameParts, "seq") } - if opts.admissionControlEnabled { - nameParts = append(nameParts, "admission") + if opts.admissionControlDisabled { + nameParts = append(nameParts, "no-admission") } if opts.concMultiplier != 0 { // support legacy test name which didn't include this multiplier nameParts = append(nameParts, fmt.Sprintf("conc=%d", opts.concMultiplier)) @@ -873,7 +866,9 @@ func registerKVMultiStoreWithOverload(r registry.Registry) { t.Fatalf("failed to configure zone for %s: %v", name, err) } } - EnableAdmissionControl(ctx, t, c) + // Defensive, since admission control is enabled by default. This test can + // fail if admission control is disabled. + SetAdmissionControl(ctx, t, c, true) if _, err := db.ExecContext(ctx, "SET CLUSTER SETTING kv.range_split.by_load_enabled = 'false'"); err != nil { t.Fatalf("failed to disable load based splitting: %v", err) diff --git a/pkg/cmd/roachtest/tests/tpcc.go b/pkg/cmd/roachtest/tests/tpcc.go index 5ffc88fe7e4e..d915d3b54146 100644 --- a/pkg/cmd/roachtest/tests/tpcc.go +++ b/pkg/cmd/roachtest/tests/tpcc.go @@ -693,9 +693,9 @@ func registerTPCC(r registry.Registry) { EstimatedMax: gceOrAws(cloud, 2400, 3000), }) registerTPCCBenchSpec(r, tpccBenchSpec{ - Nodes: 3, - CPUs: 16, - AdmissionControlEnabled: true, + Nodes: 3, + CPUs: 16, + AdmissionControlDisabled: true, LoadWarehouses: gceOrAws(cloud, 3000, 3500), EstimatedMax: gceOrAws(cloud, 2400, 3000), @@ -801,12 +801,12 @@ func (l tpccBenchLoadConfig) numLoadNodes(d tpccBenchDistribution) int { } type tpccBenchSpec struct { - Nodes int - CPUs int - Chaos bool - AdmissionControlEnabled bool - Distribution tpccBenchDistribution - LoadConfig tpccBenchLoadConfig + Nodes int + CPUs int + Chaos bool + AdmissionControlDisabled bool + Distribution tpccBenchDistribution + LoadConfig tpccBenchLoadConfig // The number of warehouses to load into the cluster before beginning // benchmarking. Should be larger than EstimatedMax and should be a @@ -857,8 +857,8 @@ func registerTPCCBenchSpec(r registry.Registry, b tpccBenchSpec) { if b.Chaos { nameParts = append(nameParts, "chaos") } - if b.AdmissionControlEnabled { - nameParts = append(nameParts, "admission") + if b.AdmissionControlDisabled { + nameParts = append(nameParts, "no-admission") } opts := []spec.Option{spec.CPU(b.CPUs)} @@ -1034,9 +1034,7 @@ func runTPCCBench(ctx context.Context, t test.Test, c cluster.Cluster, b tpccBen c.EncryptDefault(false) c.EncryptAtRandom(false) c.Start(ctx, append(b.startOpts(), roachNodes)...) - if b.AdmissionControlEnabled { - EnableAdmissionControl(ctx, t, c) - } + SetAdmissionControl(ctx, t, c, !b.AdmissionControlDisabled) useHAProxy := b.Chaos const restartWait = 15 * time.Second { @@ -1125,9 +1123,7 @@ func runTPCCBench(ctx context.Context, t test.Test, c cluster.Cluster, b tpccBen } c.Start(ctx, append(b.startOpts(), roachNodes)...) - if b.AdmissionControlEnabled { - EnableAdmissionControl(ctx, t, c) - } + SetAdmissionControl(ctx, t, c, !b.AdmissionControlDisabled) } s := search.NewLineSearcher(1, b.LoadWarehouses, b.EstimatedMax, initStepSize, precision) diff --git a/pkg/cmd/roachtest/tests/util.go b/pkg/cmd/roachtest/tests/util.go index 7cdb6e1684d7..5e177b217a37 100644 --- a/pkg/cmd/roachtest/tests/util.go +++ b/pkg/cmd/roachtest/tests/util.go @@ -94,16 +94,20 @@ func WaitForUpdatedReplicationReport(ctx context.Context, t test.Test, db *gosql } } -// EnableAdmissionControl enables the admission control cluster settings on -// the given cluster. -func EnableAdmissionControl(ctx context.Context, t test.Test, c cluster.Cluster) { +// SetAdmissionControl sets the admission control cluster settings on the +// given cluster. +func SetAdmissionControl(ctx context.Context, t test.Test, c cluster.Cluster, enabled bool) { db := c.Conn(ctx, 1) defer db.Close() + val := "true" + if !enabled { + val = "false" + } for _, setting := range []string{"admission.kv.enabled", "admission.sql_kv_response.enabled", "admission.sql_sql_response.enabled"} { if _, err := db.ExecContext( - ctx, "SET CLUSTER SETTING "+setting+" = 'true'"); err != nil { - t.Fatalf("failed to enable admission control: %v", err) + ctx, "SET CLUSTER SETTING "+setting+" = '"+val+"'"); err != nil { + t.Fatalf("failed to set admission control to %t: %v", enabled, err) } } } diff --git a/pkg/util/admission/work_queue.go b/pkg/util/admission/work_queue.go index 5b7089da8fce..6a112ccf9b18 100644 --- a/pkg/util/admission/work_queue.go +++ b/pkg/util/admission/work_queue.go @@ -36,7 +36,7 @@ import ( var KVAdmissionControlEnabled = settings.RegisterBoolSetting( "admission.kv.enabled", "when true, work performed by the KV layer is subject to admission control", - false).WithPublic() + true).WithPublic() // SQLKVResponseAdmissionControlEnabled controls whether response processing // in SQL, for KV requests, is enabled. @@ -44,7 +44,7 @@ var SQLKVResponseAdmissionControlEnabled = settings.RegisterBoolSetting( "admission.sql_kv_response.enabled", "when true, work performed by the SQL layer when receiving a KV response is subject to "+ "admission control", - false).WithPublic() + true).WithPublic() // SQLSQLResponseAdmissionControlEnabled controls whether response processing // in SQL, for DistSQL requests, is enabled. @@ -52,7 +52,7 @@ var SQLSQLResponseAdmissionControlEnabled = settings.RegisterBoolSetting( "admission.sql_sql_response.enabled", "when true, work performed by the SQL layer when receiving a DistSQL response is subject "+ "to admission control", - false).WithPublic() + true).WithPublic() var admissionControlEnabledSettings = [numWorkKinds]*settings.BoolSetting{ KVWork: KVAdmissionControlEnabled, From 5ecc973541856c87bd2fedf5fc130ca552405c56 Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Mon, 25 Oct 2021 11:40:30 +0100 Subject: [PATCH 087/205] roachtest: switch import roachtest to use IMPORT INTO Release note: None --- pkg/cmd/roachtest/tests/import.go | 40 +++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/roachtest/tests/import.go b/pkg/cmd/roachtest/tests/import.go index 9b86470dffbb..768ba1731a54 100644 --- a/pkg/cmd/roachtest/tests/import.go +++ b/pkg/cmd/roachtest/tests/import.go @@ -12,6 +12,7 @@ package tests import ( "context" + gosql "database/sql" "fmt" "path/filepath" "strings" @@ -26,6 +27,15 @@ import ( "github.com/cockroachdb/errors" ) +func readCreateTableFromFixture(fixtureURI string, gatewayDB *gosql.DB) (string, error) { + row := make([]byte, 0) + err := gatewayDB.QueryRow(fmt.Sprintf(`SELECT crdb_internal.read_file('%s')`, fixtureURI)).Scan(&row) + if err != nil { + return "", err + } + return string(row), err +} + func registerImportNodeShutdown(r registry.Registry) { getImportRunner := func(ctx context.Context, gatewayNode int) jobStarter { startImport := func(c cluster.Cluster) (jobID string, err error) { @@ -36,8 +46,7 @@ func registerImportNodeShutdown(r registry.Registry) { tableName = "part" } importStmt := fmt.Sprintf(` - IMPORT TABLE %[1]s - CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/%[1]s.sql?AUTH=implicit' + IMPORT INTO %[1]s CSV DATA ( 'gs://cockroach-fixtures/tpch-csv/sf-100/%[1]s.tbl.1?AUTH=implicit', 'gs://cockroach-fixtures/tpch-csv/sf-100/%[1]s.tbl.2?AUTH=implicit', @@ -52,6 +61,17 @@ func registerImportNodeShutdown(r registry.Registry) { gatewayDB := c.Conn(ctx, gatewayNode) defer gatewayDB.Close() + createStmt, err := readCreateTableFromFixture( + fmt.Sprintf("gs://cockroach-fixtures/tpch-csv/schema/%s.sql?AUTH=implicit", tableName), gatewayDB) + if err != nil { + return "", err + } + + // Create the table to be imported into. + if _, err = gatewayDB.ExecContext(ctx, createStmt); err != nil { + return jobID, err + } + err = gatewayDB.QueryRowContext(ctx, importStmt).Scan(&jobID) return } @@ -239,13 +259,23 @@ func registerImportTPCH(r registry.Registry) { t.WorkerStatus(`running import`) defer t.WorkerStatus() + createStmt, err := readCreateTableFromFixture( + "gs://cockroach-fixtures/tpch-csv/schema/lineitem.sql?AUTH=implicit", conn) + if err != nil { + return err + } + + // Create table to import into. + if _, err := conn.ExecContext(ctx, createStmt); err != nil { + return err + } + // Tick once before starting the import, and once after to capture the // total elapsed time. This is used by roachperf to compute and display // the average MB/sec per node. tick() - _, err := conn.Exec(` - IMPORT TABLE csv.lineitem - CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/lineitem.sql?AUTH=implicit' + _, err = conn.Exec(` + IMPORT INTO csv.lineitem CSV DATA ( 'gs://cockroach-fixtures/tpch-csv/sf-100/lineitem.tbl.1?AUTH=implicit', 'gs://cockroach-fixtures/tpch-csv/sf-100/lineitem.tbl.2?AUTH=implicit', From 1b4f9885460ee97ebff83eed976dc905413d6ae9 Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Mon, 25 Oct 2021 13:42:19 +0100 Subject: [PATCH 088/205] workload: switch workload SQL files to use IMPORT INTO Release note: None --- pkg/workload/tpch/import-sf1.sql | 117 ++++++++++++++++++++++++++++-- pkg/workload/tpch/import-sf10.sql | 117 ++++++++++++++++++++++++++++-- 2 files changed, 218 insertions(+), 16 deletions(-) diff --git a/pkg/workload/tpch/import-sf1.sql b/pkg/workload/tpch/import-sf1.sql index 439bd975fd26..138cbfb4a208 100644 --- a/pkg/workload/tpch/import-sf1.sql +++ b/pkg/workload/tpch/import-sf1.sql @@ -15,17 +15,43 @@ -- BACKUP DATABASE tpch TO 'gs://cockroach-fixtures/workload/tpch/scalefactor=1/backup'; -- -IMPORT TABLE region CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/region.sql' CSV DATA( +CREATE TABLE region ( + r_regionkey INTEGER NOT NULL PRIMARY KEY, + r_name CHAR(25) NOT NULL, + r_comment VARCHAR(152) +); + +IMPORT INTO region CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/region.tbl' ) WITH delimiter='|'; -IMPORT TABLE nation CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/nation.sql' CSV DATA( +CREATE TABLE nation ( + n_nationkey INTEGER NOT NULL PRIMARY KEY, + n_name CHAR(25) NOT NULL, + n_regionkey INTEGER NOT NULL, + n_comment VARCHAR(152), + INDEX n_rk (n_regionkey ASC) +); + +IMPORT INTO nation CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/nation.tbl' ) WITH delimiter='|'; ALTER TABLE nation ADD CONSTRAINT nation_fkey_region FOREIGN KEY (n_regionkey) references region (r_regionkey); -IMPORT TABLE part CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/part.sql' CSV DATA( +CREATE TABLE part ( + p_partkey INTEGER NOT NULL PRIMARY KEY, + p_name VARCHAR(55) NOT NULL, + p_mfgr CHAR(25) NOT NULL, + p_brand CHAR(10) NOT NULL, + p_type VARCHAR(25) NOT NULL, + p_size INTEGER NOT NULL, + p_container CHAR(10) NOT NULL, + p_retailprice FLOAT NOT NULL, + p_comment VARCHAR(23) NOT NULL +); + +IMPORT INTO part CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/part.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-1/part.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-1/part.tbl.3', @@ -36,7 +62,18 @@ IMPORT TABLE part CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/part.sql 'gs://cockroach-fixtures/tpch-csv/sf-1/part.tbl.8' ) WITH delimiter='|'; -IMPORT TABLE supplier CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/supplier.sql' CSV DATA( +CREATE TABLE supplier ( + s_suppkey INTEGER NOT NULL PRIMARY KEY, + s_name CHAR(25) NOT NULL, + s_address VARCHAR(40) NOT NULL, + s_nationkey INTEGER NOT NULL, + s_phone CHAR(15) NOT NULL, + s_acctbal FLOAT NOT NULL, + s_comment VARCHAR(101) NOT NULL, + INDEX s_nk (s_nationkey ASC) +); + +IMPORT INTO supplier CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/supplier.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-1/supplier.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-1/supplier.tbl.3', @@ -49,7 +86,17 @@ IMPORT TABLE supplier CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/supp ALTER TABLE supplier ADD CONSTRAINT supplier_fkey_nation FOREIGN KEY (s_nationkey) references nation (n_nationkey); -IMPORT TABLE partsupp CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/partsupp.sql' CSV DATA( +CREATE TABLE partsupp ( + ps_partkey INTEGER NOT NULL, + ps_suppkey INTEGER NOT NULL, + ps_availqty INTEGER NOT NULL, + ps_supplycost FLOAT NOT NULL, + ps_comment VARCHAR(199) NOT NULL, + PRIMARY KEY (ps_partkey, ps_suppkey), + INDEX ps_sk (ps_suppkey ASC) +); + +IMPORT INTO partsupp CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/partsupp.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-1/partsupp.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-1/partsupp.tbl.3', @@ -63,7 +110,19 @@ IMPORT TABLE partsupp CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/part ALTER TABLE partsupp ADD CONSTRAINT partsupp_fkey_part FOREIGN KEY (ps_partkey) references part (p_partkey); ALTER TABLE partsupp ADD CONSTRAINT partsupp_fkey_supplier FOREIGN KEY (ps_suppkey) references supplier (s_suppkey); -IMPORT TABLE customer CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/customer.sql' CSV DATA( +CREATE TABLE customer ( + c_custkey INTEGER NOT NULL PRIMARY KEY, + c_name VARCHAR(25) NOT NULL, + c_address VARCHAR(40) NOT NULL, + c_nationkey INTEGER NOT NULL, + c_phone CHAR(15) NOT NULL, + c_acctbal FLOAT NOT NULL, + c_mktsegment CHAR(10) NOT NULL, + c_comment VARCHAR(117) NOT NULL, + INDEX c_nk (c_nationkey ASC) +); + +IMPORT INTO customer CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/customer.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-1/customer.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-1/customer.tbl.3', @@ -76,7 +135,21 @@ IMPORT TABLE customer CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/cust ALTER TABLE customer ADD CONSTRAINT customer_fkey_nation FOREIGN KEY (c_nationkey) references nation (n_nationkey); -IMPORT TABLE orders CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/orders.sql' CSV DATA( +CREATE TABLE orders ( + o_orderkey INTEGER NOT NULL PRIMARY KEY, + o_custkey INTEGER NOT NULL, + o_orderstatus CHAR(1) NOT NULL, + o_totalprice FLOAT NOT NULL, + o_orderdate DATE NOT NULL, + o_orderpriority CHAR(15) NOT NULL, + o_clerk CHAR(15) NOT NULL, + o_shippriority INTEGER NOT NULL, + o_comment VARCHAR(79) NOT NULL, + INDEX o_ck (o_custkey ASC), + INDEX o_od (o_orderdate ASC) +); + +IMPORT INTO orders CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/orders.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-1/orders.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-1/orders.tbl.3', @@ -89,7 +162,35 @@ IMPORT TABLE orders CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/orders ALTER TABLE orders ADD CONSTRAINT orders_fkey_customer FOREIGN KEY (o_custkey) references customer (c_custkey); -IMPORT TABLE lineitem CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/lineitem.sql' CSV DATA( +CREATE TABLE lineitem ( + l_orderkey INTEGER NOT NULL, + l_partkey INTEGER NOT NULL, + l_suppkey INTEGER NOT NULL, + l_linenumber INTEGER NOT NULL, + l_quantity FLOAT NOT NULL, + l_extendedprice FLOAT NOT NULL, + l_discount FLOAT NOT NULL, + l_tax FLOAT NOT NULL, + l_returnflag CHAR(1) NOT NULL, + l_linestatus CHAR(1) NOT NULL, + l_shipdate DATE NOT NULL, + l_commitdate DATE NOT NULL, + l_receiptdate DATE NOT NULL, + l_shipinstruct CHAR(25) NOT NULL, + l_shipmode CHAR(10) NOT NULL, + l_comment VARCHAR(44) NOT NULL, + PRIMARY KEY (l_orderkey, l_linenumber), + INDEX l_ok (l_orderkey ASC), + INDEX l_pk (l_partkey ASC), + INDEX l_sk (l_suppkey ASC), + INDEX l_sd (l_shipdate ASC), + INDEX l_cd (l_commitdate ASC), + INDEX l_rd (l_receiptdate ASC), + INDEX l_pk_sk (l_partkey ASC, l_suppkey ASC), + INDEX l_sk_pk (l_suppkey ASC, l_partkey ASC) +); + +IMPORT INTO lineitem CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-1/lineitem.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-1/lineitem.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-1/lineitem.tbl.3', diff --git a/pkg/workload/tpch/import-sf10.sql b/pkg/workload/tpch/import-sf10.sql index 7cd43c064141..e039c057c2c3 100644 --- a/pkg/workload/tpch/import-sf10.sql +++ b/pkg/workload/tpch/import-sf10.sql @@ -15,17 +15,43 @@ -- BACKUP DATABASE tpch TO 'gs://cockroach-fixtures/workload/tpch/scalefactor=10/backup'; -- -IMPORT TABLE region CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/region.sql' CSV DATA( +CREATE TABLE region ( + r_regionkey INTEGER NOT NULL PRIMARY KEY, + r_name CHAR(25) NOT NULL, + r_comment VARCHAR(152) +); + +IMPORT INTO region CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/region.tbl' ) WITH delimiter='|'; -IMPORT TABLE nation CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/nation.sql' CSV DATA( +CREATE TABLE nation ( + n_nationkey INTEGER NOT NULL PRIMARY KEY, + n_name CHAR(25) NOT NULL, + n_regionkey INTEGER NOT NULL, + n_comment VARCHAR(152), + INDEX n_rk (n_regionkey ASC) +); + +IMPORT INTO nation CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/nation.tbl' ) WITH delimiter='|'; ALTER TABLE nation ADD CONSTRAINT nation_fkey_region FOREIGN KEY (n_regionkey) references region (r_regionkey); -IMPORT TABLE part CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/part.sql' CSV DATA( +CREATE TABLE part ( + p_partkey INTEGER NOT NULL PRIMARY KEY, + p_name VARCHAR(55) NOT NULL, + p_mfgr CHAR(25) NOT NULL, + p_brand CHAR(10) NOT NULL, + p_type VARCHAR(25) NOT NULL, + p_size INTEGER NOT NULL, + p_container CHAR(10) NOT NULL, + p_retailprice FLOAT NOT NULL, + p_comment VARCHAR(23) NOT NULL +); + +IMPORT INTO part CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/part.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-10/part.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-10/part.tbl.3', @@ -36,7 +62,18 @@ IMPORT TABLE part CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/part.sql 'gs://cockroach-fixtures/tpch-csv/sf-10/part.tbl.8' ) WITH delimiter='|'; -IMPORT TABLE supplier CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/supplier.sql' CSV DATA( +CREATE TABLE supplier ( + s_suppkey INTEGER NOT NULL PRIMARY KEY, + s_name CHAR(25) NOT NULL, + s_address VARCHAR(40) NOT NULL, + s_nationkey INTEGER NOT NULL, + s_phone CHAR(15) NOT NULL, + s_acctbal FLOAT NOT NULL, + s_comment VARCHAR(101) NOT NULL, + INDEX s_nk (s_nationkey ASC) +); + +IMPORT INTO supplier CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/supplier.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-10/supplier.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-10/supplier.tbl.3', @@ -49,7 +86,17 @@ IMPORT TABLE supplier CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/supp ALTER TABLE supplier ADD CONSTRAINT supplier_fkey_nation FOREIGN KEY (s_nationkey) references nation (n_nationkey); -IMPORT TABLE partsupp CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/partsupp.sql' CSV DATA( +CREATE TABLE partsupp ( + ps_partkey INTEGER NOT NULL, + ps_suppkey INTEGER NOT NULL, + ps_availqty INTEGER NOT NULL, + ps_supplycost FLOAT NOT NULL, + ps_comment VARCHAR(199) NOT NULL, + PRIMARY KEY (ps_partkey, ps_suppkey), + INDEX ps_sk (ps_suppkey ASC) +); + +IMPORT INTO partsupp CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/partsupp.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-10/partsupp.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-10/partsupp.tbl.3', @@ -63,7 +110,19 @@ IMPORT TABLE partsupp CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/part ALTER TABLE partsupp ADD CONSTRAINT partsupp_fkey_part FOREIGN KEY (ps_partkey) references part (p_partkey); ALTER TABLE partsupp ADD CONSTRAINT partsupp_fkey_supplier FOREIGN KEY (ps_suppkey) references supplier (s_suppkey); -IMPORT TABLE customer CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/customer.sql' CSV DATA( +CREATE TABLE customer ( + c_custkey INTEGER NOT NULL PRIMARY KEY, + c_name VARCHAR(25) NOT NULL, + c_address VARCHAR(40) NOT NULL, + c_nationkey INTEGER NOT NULL, + c_phone CHAR(15) NOT NULL, + c_acctbal FLOAT NOT NULL, + c_mktsegment CHAR(10) NOT NULL, + c_comment VARCHAR(117) NOT NULL, + INDEX c_nk (c_nationkey ASC) +); + +IMPORT INTO customer CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/customer.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-10/customer.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-10/customer.tbl.3', @@ -76,7 +135,21 @@ IMPORT TABLE customer CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/cust ALTER TABLE customer ADD CONSTRAINT customer_fkey_nation FOREIGN KEY (c_nationkey) references nation (n_nationkey); -IMPORT TABLE orders CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/orders.sql' CSV DATA( +CREATE TABLE orders ( + o_orderkey INTEGER NOT NULL PRIMARY KEY, + o_custkey INTEGER NOT NULL, + o_orderstatus CHAR(1) NOT NULL, + o_totalprice FLOAT NOT NULL, + o_orderdate DATE NOT NULL, + o_orderpriority CHAR(15) NOT NULL, + o_clerk CHAR(15) NOT NULL, + o_shippriority INTEGER NOT NULL, + o_comment VARCHAR(79) NOT NULL, + INDEX o_ck (o_custkey ASC), + INDEX o_od (o_orderdate ASC) +); + +IMPORT INTO orders CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/orders.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-10/orders.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-10/orders.tbl.3', @@ -89,7 +162,35 @@ IMPORT TABLE orders CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/orders ALTER TABLE orders ADD CONSTRAINT orders_fkey_customer FOREIGN KEY (o_custkey) references customer (c_custkey); -IMPORT TABLE lineitem CREATE USING 'gs://cockroach-fixtures/tpch-csv/schema/lineitem.sql' CSV DATA( +CREATE TABLE lineitem ( + l_orderkey INTEGER NOT NULL, + l_partkey INTEGER NOT NULL, + l_suppkey INTEGER NOT NULL, + l_linenumber INTEGER NOT NULL, + l_quantity FLOAT NOT NULL, + l_extendedprice FLOAT NOT NULL, + l_discount FLOAT NOT NULL, + l_tax FLOAT NOT NULL, + l_returnflag CHAR(1) NOT NULL, + l_linestatus CHAR(1) NOT NULL, + l_shipdate DATE NOT NULL, + l_commitdate DATE NOT NULL, + l_receiptdate DATE NOT NULL, + l_shipinstruct CHAR(25) NOT NULL, + l_shipmode CHAR(10) NOT NULL, + l_comment VARCHAR(44) NOT NULL, + PRIMARY KEY (l_orderkey, l_linenumber), + INDEX l_ok (l_orderkey ASC), + INDEX l_pk (l_partkey ASC), + INDEX l_sk (l_suppkey ASC), + INDEX l_sd (l_shipdate ASC), + INDEX l_cd (l_commitdate ASC), + INDEX l_rd (l_receiptdate ASC), + INDEX l_pk_sk (l_partkey ASC, l_suppkey ASC), + INDEX l_sk_pk (l_suppkey ASC, l_partkey ASC) +); + +IMPORT INTO lineitem CSV DATA( 'gs://cockroach-fixtures/tpch-csv/sf-10/lineitem.tbl.1', 'gs://cockroach-fixtures/tpch-csv/sf-10/lineitem.tbl.2', 'gs://cockroach-fixtures/tpch-csv/sf-10/lineitem.tbl.3', From 6a1b9c891f20b32d985a451118f690de01dc6e37 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Thu, 28 Oct 2021 09:49:09 -0700 Subject: [PATCH 089/205] colexec: fix BenchmarkExternalHashAggregator Previously, the external hash aggregator benchmark had two flaws: - it didn't specify explicitly `ordering` property, so the input was actually ordered - similarly, it didn't specify `unorderedInput` field, so we planned a sort on top of the aggregation to preserve the order. Both of these things are undesirable and are now fixed. Release note: None --- pkg/sql/colexec/aggregators_test.go | 10 +++++----- pkg/sql/colexec/external_hash_aggregator_test.go | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/sql/colexec/aggregators_test.go b/pkg/sql/colexec/aggregators_test.go index ca71edc4e49d..ed2c8fddd579 100644 --- a/pkg/sql/colexec/aggregators_test.go +++ b/pkg/sql/colexec/aggregators_test.go @@ -1073,10 +1073,11 @@ func benchmarkAggregateFunction( aggCols[i] = uint32(numGroupCol + i) } tc := aggregatorTestCase{ - typs: typs, - groupCols: groupCols, - aggCols: [][]uint32{aggCols}, - aggFns: []execinfrapb.AggregatorSpec_Func{aggFn}, + typs: typs, + groupCols: groupCols, + aggCols: [][]uint32{aggCols}, + aggFns: []execinfrapb.AggregatorSpec_Func{aggFn}, + unorderedInput: agg.order == unordered, } if distinctProb > 0 { if !typs[0].Identical(types.Int) { @@ -1090,7 +1091,6 @@ func benchmarkAggregateFunction( } } if agg.order == partial { - tc.unorderedInput = false tc.orderedCols = []uint32{0} } require.NoError(b, tc.init()) diff --git a/pkg/sql/colexec/external_hash_aggregator_test.go b/pkg/sql/colexec/external_hash_aggregator_test.go index 51fc0c129c5e..e5bf2dff5a7c 100644 --- a/pkg/sql/colexec/external_hash_aggregator_test.go +++ b/pkg/sql/colexec/external_hash_aggregator_test.go @@ -218,7 +218,8 @@ func BenchmarkExternalHashAggregator(b *testing.B) { // purposes of this benchmark. return colexecop.NewNoop(op), err }, - name: fmt.Sprintf("spilled=%t", spillForced), + name: fmt.Sprintf("spilled=%t", spillForced), + order: unordered, }, aggFn, []*types.T{types.Int}, 1 /* numGroupCol */, groupSize, 0 /* distinctProb */, numInputRows, 0 /* chunkSize */, 0 /* limit */) From 8aa75a5d50faf4ffc975c814cc17aea3bfe30ae9 Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Thu, 28 Oct 2021 13:40:12 -0400 Subject: [PATCH 090/205] descs: honor AvoidSynthetic flag when getting by name Previously, the AvoidSynthetic object selection flag was only respected in descriptor lookups by ID. This commit fixes this by extending the same logic to name lookups. Presently, there are no lookups by name that use this flag, so the impact is nil, and this change is purely defensive. Release note: None --- pkg/sql/catalog/descs/database.go | 2 +- pkg/sql/catalog/descs/descriptor.go | 10 +++++----- pkg/sql/catalog/descs/object.go | 2 +- pkg/sql/catalog/descs/schema.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/sql/catalog/descs/database.go b/pkg/sql/catalog/descs/database.go index c4c9617b7db3..8e7846f04307 100644 --- a/pkg/sql/catalog/descs/database.go +++ b/pkg/sql/catalog/descs/database.go @@ -61,7 +61,7 @@ func (tc *Collection) getDatabaseByName( ctx context.Context, txn *kv.Txn, name string, flags tree.DatabaseLookupFlags, ) (catalog.DatabaseDescriptor, error) { found, desc, err := tc.getByName( - ctx, txn, nil, nil, name, flags.AvoidCached, flags.RequireMutable, + ctx, txn, nil, nil, name, flags.AvoidCached, flags.RequireMutable, flags.AvoidSynthetic, ) if err != nil { return nil, err diff --git a/pkg/sql/catalog/descs/descriptor.go b/pkg/sql/catalog/descs/descriptor.go index 8675faa64e6f..3ed8c98f2d7f 100644 --- a/pkg/sql/catalog/descs/descriptor.go +++ b/pkg/sql/catalog/descs/descriptor.go @@ -158,7 +158,7 @@ func (tc *Collection) getByName( db catalog.DatabaseDescriptor, sc catalog.SchemaDescriptor, name string, - avoidCached, mutable bool, + avoidCached, mutable, avoidSynthetic bool, ) (found bool, desc catalog.Descriptor, err error) { var parentID, parentSchemaID descpb.ID @@ -166,12 +166,12 @@ func (tc *Collection) getByName( if sc == nil { // Schema descriptors are handled in a special way, see getSchemaByName // function declaration for details. - return getSchemaByName(ctx, tc, txn, db, name, avoidCached, mutable) + return getSchemaByName(ctx, tc, txn, db, name, avoidCached, mutable, avoidSynthetic) } parentID, parentSchemaID = db.GetID(), sc.GetID() } - if found, sd := tc.synthetic.getByName(parentID, parentSchemaID, name); found { + if found, sd := tc.synthetic.getByName(parentID, parentSchemaID, name); found && !avoidSynthetic { if mutable { return false, nil, newMutableSyntheticDescriptorAssertionError(sd.GetID()) } @@ -255,8 +255,7 @@ func getSchemaByName( txn *kv.Txn, db catalog.DatabaseDescriptor, name string, - avoidCached bool, - mutable bool, + avoidCached, mutable, avoidSynthetic bool, ) (bool, catalog.Descriptor, error) { if name == tree.PublicSchema { return true, schemadesc.GetPublicSchema(), nil @@ -277,6 +276,7 @@ func getSchemaByName( sc, err := tc.getSchemaByID(ctx, txn, id, tree.SchemaLookupFlags{ RequireMutable: mutable, AvoidCached: avoidCached, + AvoidSynthetic: avoidSynthetic, }) // Deal with the fact that ByID retrieval always uses required and the // logic here never returns an error if the descriptor does not exist. diff --git a/pkg/sql/catalog/descs/object.go b/pkg/sql/catalog/descs/object.go index 9977e84f146c..7f53dba11a75 100644 --- a/pkg/sql/catalog/descs/object.go +++ b/pkg/sql/catalog/descs/object.go @@ -192,7 +192,7 @@ func (tc *Collection) getObjectByNameIgnoringRequiredAndType( prefix.Schema = sc found, obj, err := tc.getByName( - ctx, txn, db, sc, objectName, flags.AvoidCached, flags.RequireMutable, + ctx, txn, db, sc, objectName, flags.AvoidCached, flags.RequireMutable, flags.AvoidSynthetic, ) if !found || err != nil { return prefix, nil, err diff --git a/pkg/sql/catalog/descs/schema.go b/pkg/sql/catalog/descs/schema.go index 2e98793880e1..42255b7aab85 100644 --- a/pkg/sql/catalog/descs/schema.go +++ b/pkg/sql/catalog/descs/schema.go @@ -67,7 +67,7 @@ func (tc *Collection) getSchemaByName( flags tree.SchemaLookupFlags, ) (catalog.SchemaDescriptor, error) { found, desc, err := tc.getByName( - ctx, txn, db, nil, schemaName, flags.AvoidCached, flags.RequireMutable, + ctx, txn, db, nil, schemaName, flags.AvoidCached, flags.RequireMutable, flags.AvoidSynthetic, ) if err != nil { return nil, err From 01d7a63bf5152df868934e7266bde01ded302ea0 Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Thu, 28 Oct 2021 13:43:02 -0400 Subject: [PATCH 091/205] catalog: add NewBuilder method to Descriptor Previously, creating a builder for an arbitrary descriptor object required importing the catalogkv package, because that's where the descriptor proto de/serialization mostly happens. This is awkward when all we want to do is deep-copy a descriptor. This commit addresses this growing need. Release note: None --- pkg/ccl/backupccl/restore_planning.go | 2 +- pkg/sql/catalog/dbdesc/database_desc.go | 5 +++++ pkg/sql/catalog/descriptor.go | 3 +++ pkg/sql/catalog/descs/uncommitted_descriptors.go | 3 +-- pkg/sql/catalog/schemadesc/schema_desc.go | 5 +++++ pkg/sql/catalog/schemadesc/synthetic_schema_desc.go | 5 +++++ pkg/sql/catalog/tabledesc/table_desc.go | 5 +++++ pkg/sql/catalog/typedesc/table_implicit_record_type.go | 6 ++++++ pkg/sql/catalog/typedesc/type_desc.go | 5 +++++ 9 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pkg/ccl/backupccl/restore_planning.go b/pkg/ccl/backupccl/restore_planning.go index ef9a818452a0..a71c99fdce9e 100644 --- a/pkg/ccl/backupccl/restore_planning.go +++ b/pkg/ccl/backupccl/restore_planning.go @@ -959,7 +959,7 @@ func maybeUpgradeDescriptors( if tableDesc, isTable := desc.(catalog.TableDescriptor); isTable { b = tabledesc.NewBuilderForFKUpgrade(tableDesc.TableDesc(), skipFKsWithNoMatchingTable) } else { - b = catalogkv.NewBuilder(desc.DescriptorProto()) + b = desc.NewBuilder() } err := b.RunPostDeserializationChanges(ctx, descGetter) if err != nil { diff --git a/pkg/sql/catalog/dbdesc/database_desc.go b/pkg/sql/catalog/dbdesc/database_desc.go index 6dd414d60859..260404491ac2 100644 --- a/pkg/sql/catalog/dbdesc/database_desc.go +++ b/pkg/sql/catalog/dbdesc/database_desc.go @@ -135,6 +135,11 @@ func (desc *immutable) DescriptorProto() *descpb.Descriptor { } } +// NewBuilder implements the catalog.Descriptor interface. +func (desc *immutable) NewBuilder() catalog.DescriptorBuilder { + return NewBuilder(desc.DatabaseDesc()) +} + // IsMultiRegion implements the DatabaseDescriptor interface. func (desc *immutable) IsMultiRegion() bool { return desc.RegionConfig != nil diff --git a/pkg/sql/catalog/descriptor.go b/pkg/sql/catalog/descriptor.go index 2d6a77c6a27b..84e62a206a91 100644 --- a/pkg/sql/catalog/descriptor.go +++ b/pkg/sql/catalog/descriptor.go @@ -182,6 +182,9 @@ type Descriptor interface { // DescriptorProto prepares this descriptor for serialization. DescriptorProto() *descpb.Descriptor + // NewBuilder initializes a DescriptorBuilder with this descriptor. + NewBuilder() DescriptorBuilder + // GetReferencedDescIDs returns the IDs of all descriptors directly referenced // by this descriptor, including itself. GetReferencedDescIDs() (DescriptorIDSet, error) diff --git a/pkg/sql/catalog/descs/uncommitted_descriptors.go b/pkg/sql/catalog/descs/uncommitted_descriptors.go index c2a58fb61c80..fa22659e1377 100644 --- a/pkg/sql/catalog/descs/uncommitted_descriptors.go +++ b/pkg/sql/catalog/descs/uncommitted_descriptors.go @@ -12,7 +12,6 @@ package descs import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/catalog/dbdesc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/lease" @@ -63,7 +62,7 @@ func (u *uncommittedDescriptor) getMutable() catalog.MutableDescriptor { if u.mutable != nil { return u.mutable } - return catalogkv.NewBuilder(u.immutable.DescriptorProto()).BuildExistingMutable() + return u.immutable.NewBuilder().BuildExistingMutable() } var _ catalog.NameEntry = (*uncommittedDescriptor)(nil) diff --git a/pkg/sql/catalog/schemadesc/schema_desc.go b/pkg/sql/catalog/schemadesc/schema_desc.go index 55dc683a21aa..94a356e6a53a 100644 --- a/pkg/sql/catalog/schemadesc/schema_desc.go +++ b/pkg/sql/catalog/schemadesc/schema_desc.go @@ -141,6 +141,11 @@ func (desc *immutable) DescriptorProto() *descpb.Descriptor { } } +// NewBuilder implements the catalog.Descriptor interface. +func (desc *immutable) NewBuilder() catalog.DescriptorBuilder { + return NewBuilder(desc.SchemaDesc()) +} + // ValidateSelf implements the catalog.Descriptor interface. func (desc *immutable) ValidateSelf(vea catalog.ValidationErrorAccumulator) { // Validate local properties of the descriptor. diff --git a/pkg/sql/catalog/schemadesc/synthetic_schema_desc.go b/pkg/sql/catalog/schemadesc/synthetic_schema_desc.go index 5a3755fd16cc..9cb1977cd3e4 100644 --- a/pkg/sql/catalog/schemadesc/synthetic_schema_desc.go +++ b/pkg/sql/catalog/schemadesc/synthetic_schema_desc.go @@ -78,6 +78,11 @@ func (p synthetic) DescriptorProto() *descpb.Descriptor { "%s schema cannot be encoded", p.kindName()) return nil // unreachable } +func (p synthetic) NewBuilder() catalog.DescriptorBuilder { + log.Fatalf(context.TODO(), + "%s schema cannot create a builder", p.kindName()) + return nil // unreachable +} func (p synthetic) GetReferencedDescIDs() (catalog.DescriptorIDSet, error) { return catalog.DescriptorIDSet{}, nil } diff --git a/pkg/sql/catalog/tabledesc/table_desc.go b/pkg/sql/catalog/tabledesc/table_desc.go index e69e790a5382..282065f27da2 100644 --- a/pkg/sql/catalog/tabledesc/table_desc.go +++ b/pkg/sql/catalog/tabledesc/table_desc.go @@ -101,6 +101,11 @@ func (desc *wrapper) DescriptorProto() *descpb.Descriptor { } } +// NewBuilder implements the catalog.Descriptor interface. +func (desc *wrapper) NewBuilder() catalog.DescriptorBuilder { + return NewBuilder(desc.TableDesc()) +} + // GetPrimaryIndexID implements the TableDescriptor interface. func (desc *wrapper) GetPrimaryIndexID() descpb.IndexID { return desc.PrimaryIndex.ID diff --git a/pkg/sql/catalog/typedesc/table_implicit_record_type.go b/pkg/sql/catalog/typedesc/table_implicit_record_type.go index bfca4c3d0aa5..2f6c1c28b6d0 100644 --- a/pkg/sql/catalog/typedesc/table_implicit_record_type.go +++ b/pkg/sql/catalog/typedesc/table_implicit_record_type.go @@ -176,6 +176,12 @@ func (v TableImplicitRecordType) DescriptorProto() *descpb.Descriptor { return nil } +// NewBuilder implements the Descriptor interface. +func (v TableImplicitRecordType) NewBuilder() catalog.DescriptorBuilder { + v.panicNotSupported("NewBuilder") + return nil +} + // GetReferencedDescIDs implements the Descriptor interface. func (v TableImplicitRecordType) GetReferencedDescIDs() (catalog.DescriptorIDSet, error) { return catalog.DescriptorIDSet{}, errors.AssertionFailedf( diff --git a/pkg/sql/catalog/typedesc/type_desc.go b/pkg/sql/catalog/typedesc/type_desc.go index 2202af962144..cd05614eede0 100644 --- a/pkg/sql/catalog/typedesc/type_desc.go +++ b/pkg/sql/catalog/typedesc/type_desc.go @@ -177,6 +177,11 @@ func (desc *immutable) DescriptorProto() *descpb.Descriptor { } } +// NewBuilder implements the catalog.Descriptor interface. +func (desc *immutable) NewBuilder() catalog.DescriptorBuilder { + return NewBuilder(desc.TypeDesc()) +} + // PrimaryRegionName implements the TypeDescriptor interface. func (desc *immutable) PrimaryRegionName() (descpb.RegionName, error) { if desc.Kind != descpb.TypeDescriptor_MULTIREGION_ENUM { From f5c052c0f6e125006bb6c31d05564b56214c068e Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Fri, 22 Oct 2021 13:59:29 -0400 Subject: [PATCH 092/205] schemachanger: inject all dependencies Although the declarative schema changer had been written with dependency injection in mind, the level of abstraction was still too low for straightforward end-to-end testing. The schema changer doesn't care about kv.Txn, jobs.JobRegistry, etc. Fundamentally, here's how it interacts with the rest of the system: 1. The builder in scbuild: a. The builder queries the catalog for the current descriptor and namespace state. b. The builder also needs a valid tree.SemaContext and tree.EvalContex, but only in a limited way. DDLs don't require the evaluation of built-in functions or the like, meaning that suitable context objects can be built with few indirect dependencies like the codec, session data, etc. 2. The executor in scexec: a. Naturally the executor also needs access to the catalog for reading and writing. In the scmutationexec package these dependencies had already been abstract enough, meaning there was little left to do except formalize read-only vs. mutating dependencies a bit. b. The same holds for the backfill operations, with the IndexBackfiller and JobProgressTracker interfaces. c. The executor needs to know about testing knobs and other debug info. In addition to making dependencies explicit as described above in scbuild.Dependencies and scexec.Dependencies interfaces, this commit also makes dependencies explicit for the actual running of the schema changer, to allow end-to-end testing. Concretely, we added a layer of abstraction around the execution of each phase: 1. For scop.StatementPhase, the schema changer receives a target state, runs the planner to generate ops-stages, and executes these ops. 2. For scop.PreCommitPhase, it does exactly that, but also creates and enqueues a job, which will deal with the subsequent scop.PostCommitPhase asynchronously. This job creation depends on things like the job registry, which we don't care for if we're doing end-to-end testing: in that case we'll want to run the post-commit phase synchronously after this one. 3. For scop.PostCommitPhase, it's like the previous phases, except in the context of the Resume method of the schema change job, which requires a bit of plumbing to update the progress of the job itself, and also to update the descriptors affected by the job, because those hold a reference to this job which serves as a lock, blocking other concurrent schema changes. This new layer of abstraction is defined in the new scrun package. The actual implementations of the dependency interfaces are all moved to a new scdeps package. This commit also introduces test implementations of these dependency interfaces, which so far are used in the builder tests alongside the regular implementations. Adoption in the executor tests, as well as end-to-end testing of the declarative schema changer, will be added in a subsequent commit. Release note: None --- pkg/sql/BUILD.bazel | 3 +- pkg/sql/conn_executor.go | 114 +-- .../testdata/logic_test/new_schema_changer | 10 +- pkg/sql/schema_change_plan_node.go | 39 +- pkg/sql/schemachanger/scbuild/BUILD.bazel | 16 +- pkg/sql/schemachanger/scbuild/builder.go | 69 +- pkg/sql/schemachanger/scbuild/builder_test.go | 205 +++-- pkg/sql/schemachanger/scbuild/database.go | 71 +- pkg/sql/schemachanger/scbuild/dependencies.go | 151 ++++ .../schemachanger/scbuild/relation_common.go | 41 +- pkg/sql/schemachanger/scbuild/schema.go | 135 ++-- pkg/sql/schemachanger/scbuild/sequence.go | 37 +- pkg/sql/schemachanger/scbuild/table.go | 162 ++-- .../scbuild/testdata/drop_database | 1 - pkg/sql/schemachanger/scbuild/type.go | 70 +- pkg/sql/schemachanger/scbuild/view.go | 63 +- pkg/sql/schemachanger/scdeps/BUILD.bazel | 36 + pkg/sql/schemachanger/scdeps/build_deps.go | 225 ++++++ pkg/sql/schemachanger/scdeps/exec_deps.go | 243 ++++++ pkg/sql/schemachanger/scdeps/run_deps.go | 167 +++++ .../scdeps/sctestdeps/BUILD.bazel | 41 + .../scdeps/sctestdeps/test_deps.go | 705 ++++++++++++++++++ .../scdeps/sctestdeps/test_state.go | 176 +++++ .../scdeps/sctestutils/BUILD.bazel | 20 + .../scdeps/sctestutils/sctestutils.go | 63 ++ pkg/sql/schemachanger/scexec/BUILD.bazel | 19 +- pkg/sql/schemachanger/scexec/dependencies.go | 148 ++++ pkg/sql/schemachanger/scexec/exec_backfill.go | 66 ++ pkg/sql/schemachanger/scexec/exec_mutation.go | 140 ++++ .../schemachanger/scexec/exec_validation.go | 23 + pkg/sql/schemachanger/scexec/executor.go | 265 +------ .../scexec/executor_external_test.go | 106 ++- .../scexec/mutation_desc_getter.go | 145 ---- pkg/sql/schemachanger/scexec/mutation_jobs.go | 98 --- .../scexec/scmutationexec/BUILD.bazel | 2 + .../scexec/scmutationexec/scmutationexec.go | 198 +++-- pkg/sql/schemachanger/scjob/BUILD.bazel | 11 +- pkg/sql/schemachanger/scjob/job.go | 152 +--- pkg/sql/schemachanger/scplan/BUILD.bazel | 6 +- pkg/sql/schemachanger/scplan/plan_test.go | 87 +-- pkg/sql/schemachanger/scrun/BUILD.bazel | 25 + pkg/sql/schemachanger/scrun/dependencies.go | 68 ++ pkg/sql/schemachanger/scrun/scrun.go | 195 +++++ 43 files changed, 3126 insertions(+), 1491 deletions(-) create mode 100644 pkg/sql/schemachanger/scbuild/dependencies.go create mode 100644 pkg/sql/schemachanger/scdeps/BUILD.bazel create mode 100644 pkg/sql/schemachanger/scdeps/build_deps.go create mode 100644 pkg/sql/schemachanger/scdeps/exec_deps.go create mode 100644 pkg/sql/schemachanger/scdeps/run_deps.go create mode 100644 pkg/sql/schemachanger/scdeps/sctestdeps/BUILD.bazel create mode 100644 pkg/sql/schemachanger/scdeps/sctestdeps/test_deps.go create mode 100644 pkg/sql/schemachanger/scdeps/sctestdeps/test_state.go create mode 100644 pkg/sql/schemachanger/scdeps/sctestutils/BUILD.bazel create mode 100644 pkg/sql/schemachanger/scdeps/sctestutils/sctestutils.go create mode 100644 pkg/sql/schemachanger/scexec/dependencies.go create mode 100644 pkg/sql/schemachanger/scexec/exec_backfill.go create mode 100644 pkg/sql/schemachanger/scexec/exec_mutation.go create mode 100644 pkg/sql/schemachanger/scexec/exec_validation.go delete mode 100644 pkg/sql/schemachanger/scexec/mutation_desc_getter.go delete mode 100644 pkg/sql/schemachanger/scexec/mutation_jobs.go create mode 100644 pkg/sql/schemachanger/scrun/BUILD.bazel create mode 100644 pkg/sql/schemachanger/scrun/dependencies.go create mode 100644 pkg/sql/schemachanger/scrun/scrun.go diff --git a/pkg/sql/BUILD.bazel b/pkg/sql/BUILD.bazel index 2b25468c09dc..add74b174ddf 100644 --- a/pkg/sql/BUILD.bazel +++ b/pkg/sql/BUILD.bazel @@ -346,12 +346,13 @@ go_library( "//pkg/sql/rowinfra", "//pkg/sql/schemachange", "//pkg/sql/schemachanger/scbuild", + "//pkg/sql/schemachanger/scdeps", "//pkg/sql/schemachanger/scexec", "//pkg/sql/schemachanger/scgraphviz", "//pkg/sql/schemachanger/scop", "//pkg/sql/schemachanger/scpb", "//pkg/sql/schemachanger/scplan", - "//pkg/sql/schemachanger/screl", + "//pkg/sql/schemachanger/scrun", "//pkg/sql/scrub", "//pkg/sql/sem/builtins", "//pkg/sql/sem/transform", diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index 038cfee95415..ec8e40d6a99c 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -39,11 +39,9 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scplan" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/screl" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scrun" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" @@ -2915,95 +2913,35 @@ func (ex *connExecutor) notifyStatsRefresherOfNewTables(ctx context.Context) { // mutate descriptors prior to committing a SQL transaction. func (ex *connExecutor) runPreCommitStages(ctx context.Context) error { scs := &ex.extraTxnState.schemaChangerState - if len(scs.state) == 0 { - return nil - } - executor := scexec.NewExecutor( - ex.planner.txn, &ex.extraTxnState.descCollection, ex.server.cfg.Codec, - nil /* backfiller */, nil /* jobTracker */, ex.server.cfg.NewSchemaChangerTestingKnobs, - ex.server.cfg.JobRegistry, ex.planner.execCfg.InternalExecutor, - ) - after, err := runNewSchemaChanger( - ctx, - scop.PreCommitPhase, - ex.extraTxnState.schemaChangerState.state, - executor, + execDeps := scdeps.NewExecutorDependencies( + ex.server.cfg.Codec, + ex.planner.txn, + &ex.extraTxnState.descCollection, + ex.server.cfg.JobRegistry, + ex.server.cfg.IndexBackfiller, + ex.server.cfg.NewSchemaChangerTestingKnobs, scs.stmts, + scop.PreCommitPhase, ) - if err != nil { - return err - } - scs.state = after - targetSlice := make([]*scpb.Target, len(scs.state)) - states := make([]scpb.Status, len(scs.state)) - // TODO(ajwerner): It may be better in the future to have the builder be - // responsible for determining this set of descriptors. As of the time of - // writing, the descriptors to be "locked," descriptors that need schema - // change jobs, and descriptors with schema change mutations all coincide. But - // there are future schema changes to be implemented in the new schema changer - // (e.g., RENAME TABLE) for which this may no longer be true. - descIDSet := catalog.MakeDescriptorIDSet() - for i := range scs.state { - targetSlice[i] = scs.state[i].Target - states[i] = scs.state[i].Status - // Depending on the element type either a single descriptor ID - // will exist or multiple (i.e. foreign keys). - if id := screl.GetDescID(scs.state[i].Element()); id != descpb.InvalidID { - descIDSet.Add(id) - } - } - descIDs := descIDSet.Ordered() - job, err := ex.planner.extendedEvalCtx.QueueJob(ctx, jobs.Record{ - Description: "Schema change job", // TODO(ajwerner): use const - Statements: scs.stmts, - Username: ex.planner.User(), - DescriptorIDs: descIDs, - Details: jobspb.NewSchemaChangeDetails{ - Targets: targetSlice, - }, - Progress: jobspb.NewSchemaChangeProgress{States: states}, - RunningStatus: "", - NonCancelable: false, - }) - if err != nil { - return err - } - // Write the job ID to the affected descriptors. - if err := scexec.UpdateDescriptorJobIDs( - ctx, ex.planner.Txn(), &ex.extraTxnState.descCollection, descIDs, jobspb.InvalidJobID, job.ID(), - ); err != nil { - return err - } - log.Infof(ctx, "queued new schema change job %d using the new schema changer", job.ID()) - return nil -} - -func runNewSchemaChanger( - ctx context.Context, - phase scop.Phase, - state scpb.State, - executor *scexec.Executor, - stmts []string, -) (after scpb.State, _ error) { - sc, err := scplan.MakePlan(state, scplan.Params{ - ExecutionPhase: phase, - // TODO(ajwerner): Populate the set of new descriptors - }) - if err != nil { - return nil, err + { + after, err := scrun.RunSchemaChangesInTxn(ctx, execDeps, scs.state) + if err != nil { + return err + } + scs.state = after } - after = state - for _, s := range sc.Stages { - if err := executor.ExecuteOps(ctx, s.Ops, - scexec.TestingKnobMetadata{ - Statements: stmts, - Phase: phase, - }); err != nil { - return nil, err + { + jobDeps := scdeps.NewJobCreationDependencies(execDeps, ex.planner.User()) + jobID, err := scrun.CreateSchemaChangeJob(ctx, jobDeps, scs.state) + if jobID != jobspb.InvalidJobID { + ex.extraTxnState.jobs = append(ex.extraTxnState.jobs, jobID) + log.Infof(ctx, "queued new schema change job %d using the new schema changer", jobID) + } + if err != nil { + return err } - after = s.After } - return after, nil + return nil } // StatementCounters groups metrics for counting different types of diff --git a/pkg/sql/logictest/testdata/logic_test/new_schema_changer b/pkg/sql/logictest/testdata/logic_test/new_schema_changer index 449d8ec19214..4706e29f414a 100644 --- a/pkg/sql/logictest/testdata/logic_test/new_schema_changer +++ b/pkg/sql/logictest/testdata/logic_test/new_schema_changer @@ -363,7 +363,7 @@ statement error cannot drop type "typ6" because other objects \(\[test.public.t6 DROP TYPE typ6 -subtest view-sanity +subtest view_sanity statement ok CREATE TABLE t1 (id INT PRIMARY KEY, name varchar(256)); @@ -516,7 +516,7 @@ ALTER TABLE t8 DROP COLUMN x; statement ok DROP TYPE typ8 -subtest drop-type-in-single-transaction +subtest drop_type_in_single_transaction statement ok CREATE TYPE defaultdb.typ AS ENUM('a'); @@ -537,7 +537,7 @@ statement ok COMMIT; # Drop schema testing -subtest drop-schema +subtest drop_schema statement ok CREATE DATABASE db1; @@ -576,7 +576,7 @@ statement ok DROP DATABASE db1 # Drop database testing -subtest drop-database +subtest drop_database statement ok CREATE DATABASE db1; @@ -617,7 +617,7 @@ CREATE VIEW db1.sc1.v5 AS (SELECT 'a'::db1.sc1.typ::string AS k, n2, n1 from db1 statement error schema "sc1" is not empty and CASCADE was not specified DROP SCHEMA db1.sc1 -statement error database "db1" is not empty and CASCADE was not specified +statement error database "db1" has a non-empty schema "public" and CASCADE was not specified DROP DATABASE db1 statement ok diff --git a/pkg/sql/schema_change_plan_node.go b/pkg/sql/schema_change_plan_node.go index 6ae6e60f47c7..bbc09d6cb2c7 100644 --- a/pkg/sql/schema_change_plan_node.go +++ b/pkg/sql/schema_change_plan_node.go @@ -19,9 +19,10 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scrun" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/util/log" @@ -42,14 +43,17 @@ func (p *planner) SchemaChange(ctx context.Context, stmt tree.Statement) (planNo } scs := p.extendedEvalCtx.SchemaChangerState scs.stmts = append(scs.stmts, p.stmt.SQL) - buildDeps := scbuild.Dependencies{ - Res: p, - SemaCtx: p.SemaCtx(), - EvalCtx: p.EvalContext(), - Descs: p.Descriptors(), - AuthAccessor: p, - } - outputNodes, err := scbuild.Build(ctx, buildDeps, p.extendedEvalCtx.SchemaChangerState.state, stmt) + deps := scdeps.NewBuilderDependencies( + p.ExecCfg().Codec, + p.Txn(), + p.Descriptors(), + p, + p, + p.SessionData(), + p.ExecCfg().Settings, + scs.stmts, + ) + outputNodes, err := scbuild.Build(ctx, deps, scs.state, stmt) if scbuild.HasNotImplemented(err) && mode == sessiondatapb.UseNewSchemaChangerOn { return nil, false, nil } @@ -124,13 +128,18 @@ type schemaChangePlanNode struct { func (s *schemaChangePlanNode) startExec(params runParams) error { p := params.p - scs := p.extendedEvalCtx.SchemaChangerState - executor := scexec.NewExecutor(p.txn, p.Descriptors(), p.EvalContext().Codec, - nil /* backfiller */, nil /* jobTracker */, p.ExecCfg().NewSchemaChangerTestingKnobs, - params.extendedEvalCtx.ExecCfg.JobRegistry, params.p.execCfg.InternalExecutor) - after, err := runNewSchemaChanger( - params.ctx, scop.StatementPhase, s.plannedState, executor, scs.stmts, + scs := p.ExtendedEvalContext().SchemaChangerState + deps := scdeps.NewExecutorDependencies( + p.EvalContext().Codec, + p.Txn(), + p.Descriptors(), + p.ExecCfg().JobRegistry, + p.ExecCfg().IndexBackfiller, + p.ExecCfg().NewSchemaChangerTestingKnobs, + scs.stmts, + scop.StatementPhase, ) + after, err := scrun.RunSchemaChangesInTxn(params.ctx, deps, s.plannedState) if err != nil { return err } diff --git a/pkg/sql/schemachanger/scbuild/BUILD.bazel b/pkg/sql/schemachanger/scbuild/BUILD.bazel index cc42a488823d..9c787bf8bb8c 100644 --- a/pkg/sql/schemachanger/scbuild/BUILD.bazel +++ b/pkg/sql/schemachanger/scbuild/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "builder.go", "database.go", + "dependencies.go", "relation_common.go", "schema.go", "sequence.go", @@ -15,15 +16,16 @@ go_library( importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild", visibility = ["//visibility:public"], deps = [ + "//pkg/keys", + "//pkg/settings/cluster", "//pkg/sql/catalog", - "//pkg/sql/catalog/dbdesc", "//pkg/sql/catalog/descpb", - "//pkg/sql/catalog/descs", - "//pkg/sql/catalog/resolver", + "//pkg/sql/catalog/schemadesc", "//pkg/sql/catalog/schemaexpr", "//pkg/sql/catalog/seqexpr", "//pkg/sql/catalog/tabledesc", "//pkg/sql/catalog/typedesc", + "//pkg/sql/faketreeeval", "//pkg/sql/parser", "//pkg/sql/pgwire/pgcode", "//pkg/sql/pgwire/pgerror", @@ -31,6 +33,7 @@ go_library( "//pkg/sql/schemachanger/scpb", "//pkg/sql/schemachanger/screl", "//pkg/sql/sem/tree", + "//pkg/sql/sessiondata", "//pkg/sql/sqlerrors", "//pkg/sql/sqltelemetry", "//pkg/sql/types", @@ -52,19 +55,16 @@ go_test( deps = [ ":scbuild", "//pkg/base", - "//pkg/kv", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", - "//pkg/sql", "//pkg/sql/catalog/descpb", - "//pkg/sql/catalog/descs", - "//pkg/sql/catalog/resolver", "//pkg/sql/parser", + "//pkg/sql/schemachanger/scdeps/sctestdeps", + "//pkg/sql/schemachanger/scdeps/sctestutils", "//pkg/sql/schemachanger/scpb", "//pkg/sql/schemachanger/screl", "//pkg/sql/sem/tree", - "//pkg/sql/sessiondatapb", "//pkg/testutils/serverutils", "//pkg/testutils/sqlutils", "//pkg/testutils/testcluster", diff --git a/pkg/sql/schemachanger/scbuild/builder.go b/pkg/sql/schemachanger/scbuild/builder.go index 2cb037cb1f68..b31d69b706e9 100644 --- a/pkg/sql/schemachanger/scbuild/builder.go +++ b/pkg/sql/schemachanger/scbuild/builder.go @@ -17,9 +17,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" - "github.com/cockroachdb/cockroach/pkg/sql/privilege" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/screl" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" @@ -27,30 +24,6 @@ import ( "github.com/cockroachdb/errors" ) -// AuthorizationAccessor for checking authorization (e.g. desc privileges). -type AuthorizationAccessor interface { - // CheckPrivilege verifies that the current user has `privilege` on `descriptor`. - CheckPrivilege( - ctx context.Context, descriptor catalog.Descriptor, privilege privilege.Kind, - ) error - // HasAdminRole verifies if a user has an admin role - HasAdminRole(ctx context.Context) (bool, error) - // HasOwnership returns if the role or any role the role is a member of - // has ownership privilege of the desc. - HasOwnership(ctx context.Context, descriptor catalog.Descriptor) (bool, error) -} - -// Dependencies are non-stateful objects needed for planning schema -// changes. -type Dependencies struct { - // TODO(ajwerner): Inject a better interface than this. - Res resolver.SchemaResolver - SemaCtx *tree.SemaContext - EvalCtx *tree.EvalContext - Descs *descs.Collection - AuthAccessor AuthorizationAccessor -} - // buildContext is the entry point for planning schema changes. From AST nodes // for DDL statements, it constructs targets which represent schema changes to // be performed. @@ -202,42 +175,6 @@ func (b *buildContext) addNode(dir scpb.Target_Direction, elem scpb.Element) { }) } -// getTableDescriptorForLockingChange returns a table descriptor that is -// guaranteed to have no concurrent running schema changes and can therefore -// undergo a "locking" change, or else a ConcurrentSchemaChangeError if the -// table is not currently in the required state. Locking changes roughly -// correspond to schema changes with mutations, which must be serialized and -// (in the new schema changer) require mutual exclusion. -func (b *buildContext) getTableDescriptorForLockingChange( - ctx context.Context, tn *tree.TableName, -) (catalog.TableDescriptor, error) { - table, err := b.getTableDescriptor(ctx, tn) - if err != nil { - return nil, err - } - if HasConcurrentSchemaChanges(table) { - return nil, &ConcurrentSchemaChangeError{descID: table.GetID()} - } - return table, nil -} - -func (b *buildContext) getTableDescriptor( - ctx context.Context, tn *tree.TableName, -) (catalog.TableDescriptor, error) { - // This will return an error for dropped and offline tables, but it's possible - // that later iterations of the builder will want to handle those cases - // in a different way. - _, table, err := resolver.ResolveExistingTableObject(ctx, b.Res, tn, - tree.ObjectLookupFlags{ - CommonLookupFlags: tree.CommonLookupFlags{ - Required: true, - AvoidCached: true, - }, - }, - ) - return table, err -} - // HasConcurrentSchemaChanges returns whether the table descriptor is undergoing // concurrent schema changes. func HasConcurrentSchemaChanges(table catalog.TableDescriptor) bool { @@ -247,3 +184,9 @@ func HasConcurrentSchemaChanges(table catalog.TableDescriptor) bool { // written in this transaction. return len(table.AllMutations()) > 0 } + +func onErrPanic(err error) { + if err != nil { + panic(err) + } +} diff --git a/pkg/sql/schemachanger/scbuild/builder_test.go b/pkg/sql/schemachanger/scbuild/builder_test.go index 4111ee05a5ae..ca52f6feae8c 100644 --- a/pkg/sql/schemachanger/scbuild/builder_test.go +++ b/pkg/sql/schemachanger/scbuild/builder_test.go @@ -22,18 +22,14 @@ import ( "testing" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps/sctestdeps" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps/sctestutils" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/screl" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/util/leaktest" @@ -50,79 +46,109 @@ func TestBuilderAlterTable(t *testing.T) { ctx := context.Background() datadriven.Walk(t, filepath.Join("testdata"), func(t *testing.T, path string) { - s, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{}) - defer s.Stopper().Stop(ctx) - - tdb := sqlutils.MakeSQLRunner(sqlDB) - run := func(t *testing.T, d *datadriven.TestData) string { - switch d.Cmd { - case "create-table", "create-view", "create-type", "create-sequence", "create-schema", "create-database": - stmts, err := parser.Parse(d.Input) - require.NoError(t, err) - require.Len(t, stmts, 1) - tableName := "" - switch node := stmts[0].AST.(type) { - case *tree.CreateTable: - tableName = node.Table.String() - case *tree.CreateSequence: - tableName = node.Name.String() - case *tree.CreateView: - tableName = node.Name.String() - case *tree.CreateType: - tableName = "" - case *tree.CreateSchema: - tableName = "" - case *tree.CreateDatabase: - tableName = "" - default: - t.Fatal("not a supported CREATE statement") - } - tdb.Exec(t, d.Input) - - if len(tableName) > 0 { - var tableID descpb.ID - tdb.QueryRow(t, `SELECT $1::regclass::int`, tableName).Scan(&tableID) - if tableID == 0 { - t.Fatalf("failed to read ID of new table %s", tableName) - } - t.Logf("created relation with id %d", tableID) - } - - return "" - case "build": - deps, cleanup := newTestingBuilderDeps(s) - defer cleanup() - - stmts, err := parser.Parse(d.Input) - require.NoError(t, err) - var outputNodes scpb.State - for i := range stmts { - outputNodes, err = scbuild.Build(ctx, *deps, outputNodes, stmts[i].AST) - require.NoError(t, err) - } - - return marshalNodes(t, outputNodes) - case "unimplemented": - deps, cleanup := newTestingBuilderDeps(s) - defer cleanup() - - stmts, err := parser.Parse(d.Input) - require.NoError(t, err) - require.Len(t, stmts, 1) + for _, depsType := range []struct { + name string + dependenciesWrapper func(*testing.T, serverutils.TestServerInterface, *sqlutils.SQLRunner, func(scbuild.Dependencies)) + }{ + { + "sql_dependencies", + func(t *testing.T, s serverutils.TestServerInterface, tdb *sqlutils.SQLRunner, fn func(scbuild.Dependencies)) { + sctestutils.WithBuilderDependenciesFromTestServer(s, fn) + }, + }, + { + "test_dependencies", + func(t *testing.T, s serverutils.TestServerInterface, tdb *sqlutils.SQLRunner, fn func(scbuild.Dependencies)) { + fn(sctestdeps.NewTestDependencies(ctx, t, tdb, nil /* testingKnobs */, nil /* statements */)) + }, + }, + } { + t.Run(depsType.name, func(t *testing.T) { + s, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{}) + defer s.Stopper().Stop(ctx) + + tdb := sqlutils.MakeSQLRunner(sqlDB) + + datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { + return run(ctx, t, d, s, tdb, depsType.dependenciesWrapper) + }) + }) + } + }) +} - stmt := stmts[0] - alter, ok := stmt.AST.(*tree.AlterTable) - require.Truef(t, ok, "not an ALTER TABLE statement: %s", stmt.SQL) - _, err = scbuild.Build(ctx, *deps, nil, alter) - require.Truef(t, scbuild.HasNotImplemented(err), "expected unimplemented, got %v", err) - return "" +func run( + ctx context.Context, + t *testing.T, + d *datadriven.TestData, + s serverutils.TestServerInterface, + tdb *sqlutils.SQLRunner, + withDependencies func(*testing.T, serverutils.TestServerInterface, *sqlutils.SQLRunner, func(scbuild.Dependencies)), +) string { + switch d.Cmd { + case "create-table", "create-view", "create-type", "create-sequence", "create-schema", "create-database": + stmts, err := parser.Parse(d.Input) + require.NoError(t, err) + require.Len(t, stmts, 1) + tableName := "" + switch node := stmts[0].AST.(type) { + case *tree.CreateTable: + tableName = node.Table.String() + case *tree.CreateSequence: + tableName = node.Name.String() + case *tree.CreateView: + tableName = node.Name.String() + case *tree.CreateType: + tableName = "" + case *tree.CreateSchema: + tableName = "" + case *tree.CreateDatabase: + tableName = "" + default: + t.Fatal("not a supported CREATE statement") + } + tdb.Exec(t, d.Input) - default: - return fmt.Sprintf("unknown command: %s", d.Cmd) + if len(tableName) > 0 { + var tableID descpb.ID + tdb.QueryRow(t, fmt.Sprintf(`SELECT '%s'::REGCLASS::INT`, tableName)).Scan(&tableID) + if tableID == 0 { + t.Fatalf("failed to read ID of new table %s", tableName) } + t.Logf("created relation with id %d", tableID) } - datadriven.RunTest(t, path, run) - }) + + return "" + case "build": + var outputNodes scpb.State + withDependencies(t, s, tdb, func(deps scbuild.Dependencies) { + stmts, err := parser.Parse(d.Input) + require.NoError(t, err) + for i := range stmts { + outputNodes, err = scbuild.Build(ctx, deps, outputNodes, stmts[i].AST) + require.NoError(t, err) + } + }) + return marshalNodes(t, outputNodes) + + case "unimplemented": + withDependencies(t, s, tdb, func(deps scbuild.Dependencies) { + stmts, err := parser.Parse(d.Input) + require.NoError(t, err) + require.Len(t, stmts, 1) + + stmt := stmts[0] + alter, ok := stmt.AST.(*tree.AlterTable) + require.Truef(t, ok, "not an ALTER TABLE statement: %s", stmt.SQL) + + _, err = scbuild.Build(ctx, deps, nil, alter) + require.Truef(t, scbuild.HasNotImplemented(err), "expected unimplemented, got %v", err) + }) + return "" + + default: + return fmt.Sprintf("unknown command: %s", d.Cmd) + } } // indentText indents text for formatting out marshaled data. @@ -168,32 +194,3 @@ func marshalNodes(t *testing.T, nodes scpb.State) string { } return result.String() } - -func newTestingBuilderDeps(s serverutils.TestServerInterface) (*scbuild.Dependencies, func()) { - execCfg := s.ExecutorConfig().(sql.ExecutorConfig) - ip, cleanup := sql.NewInternalPlanner( - "test", - kv.NewTxn(context.Background(), s.DB(), s.NodeID()), - security.RootUserName(), - &sql.MemoryMetrics{}, - &execCfg, - // Setting the database on the session data to "defaultdb" in the obvious - // way doesn't seem to do what we want. - sessiondatapb.SessionData{}, - ) - planner := ip.(interface { - resolver.SchemaResolver - SemaCtx() *tree.SemaContext - EvalContext() *tree.EvalContext - scbuild.AuthorizationAccessor - Descriptors() *descs.Collection - }) - buildDeps := scbuild.Dependencies{ - Res: planner, - SemaCtx: planner.SemaCtx(), - EvalCtx: planner.EvalContext(), - Descs: planner.Descriptors(), - AuthAccessor: planner, - } - return &buildDeps, cleanup -} diff --git a/pkg/sql/schemachanger/scbuild/database.go b/pkg/sql/schemachanger/scbuild/database.go index 71dc8e2d0a98..9f5afb924a41 100644 --- a/pkg/sql/schemachanger/scbuild/database.go +++ b/pkg/sql/schemachanger/scbuild/database.go @@ -12,74 +12,67 @@ package scbuild import ( "context" - "sort" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemadesc" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/privilege" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" ) func (b *buildContext) dropDatabase(ctx context.Context, n *tree.DropDatabase) { // Check that the database exists. - dbDesc, err := b.Descs.GetMutableDatabaseByName(ctx, b.EvalCtx.Txn, string(n.Name), - tree.DatabaseLookupFlags{Required: !n.IfExists}) - if err != nil { - panic(err) - } - if dbDesc == nil { - // IfExists was specified and database was not found. - return + db := b.CatalogReader().MayResolveDatabase(ctx, n.Name) + if db == nil { + if n.IfExists { + return + } + panic(sqlerrors.NewUndefinedDatabaseError(n.Name.String())) } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, db, privilege.DROP)) - if err := b.AuthAccessor.CheckPrivilege(ctx, dbDesc, privilege.DROP); err != nil { - panic(err) - } - schemas, err := b.Descs.GetSchemasForDatabase(ctx, b.EvalCtx.Txn, dbDesc.GetID()) - if err != nil { - panic(err) - } - // Order schemas to guarantee the order in which operations - // are generated for testing. - schemaIDs := make([]descpb.ID, 0, len(schemas)) - for schemaID := range schemas { - schemaIDs = append(schemaIDs, schemaID) - } - sort.SliceStable(schemaIDs, func(i, j int) bool { - return schemaIDs[i] < schemaIDs[j] - }) dropIDs := catalog.DescriptorIDSet{} - for _, schemaID := range schemaIDs { - dropIDs.Add(schemaID) - schemaDesc, err := b.Descs.GetImmutableSchemaByID(ctx, b.EvalCtx.Txn, schemaID, tree.SchemaLookupFlags{Required: true}) - if err != nil { - panic(err) - } + doSchema := func(schema catalog.SchemaDescriptor) { // For public and temporary schemas the drop logic // will only drop the underlying objects and return // if that no drop schema node was added (nodeAdded). // The schemaDroppedIDs list will have the list of // dependent objects, which that database will add // direct dependencies on. - nodeAdded, schemaDroppedIDs := b.dropSchemaDesc(ctx, schemaDesc, dbDesc, tree.DropCascade) + nodeAdded, schemaDroppedIDs := b.dropSchemaDesc(ctx, db, schema, tree.DropCascade) // Block drops if cascade is not set. - if n.DropBehavior != tree.DropCascade && - (nodeAdded || len(schemaDroppedIDs) > 0) { + if n.DropBehavior != tree.DropCascade && (nodeAdded || !schemaDroppedIDs.Empty()) { panic(pgerror.Newf(pgcode.DependentObjectsStillExist, - "database %q is not empty and CASCADE was not specified", dbDesc.GetName())) + "database %q has a non-empty schema %q and CASCADE was not specified", db.GetName(), schema.GetName())) } // If no schema exists to depend on, then depend on dropped IDs if !nodeAdded { - for _, id := range schemaDroppedIDs { - dropIDs.Add(id) - } + schemaDroppedIDs.ForEach(dropIDs.Add) + } + } + + doSchema(schemadesc.GetPublicSchema()) + var schemaIDs catalog.DescriptorIDSet + _ = db.ForEachSchemaInfo(func(id descpb.ID, _ string, isDropped bool) error { + if !isDropped { + schemaIDs.Add(id) } + return nil + }) + for _, schemaID := range schemaIDs.Ordered() { + schema := mustReadSchema(ctx, b, schemaID) + if schema.Dropped() { + continue + } + dropIDs.Add(schemaID) + doSchema(schema) } b.addNode(scpb.Target_DROP, &scpb.Database{ - DatabaseID: dbDesc.ID, + DatabaseID: db.GetID(), DependentObjects: dropIDs.Ordered()}) } diff --git a/pkg/sql/schemachanger/scbuild/dependencies.go b/pkg/sql/schemachanger/scbuild/dependencies.go new file mode 100644 index 000000000000..e66c96a417dc --- /dev/null +++ b/pkg/sql/schemachanger/scbuild/dependencies.go @@ -0,0 +1,151 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scbuild + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/faketreeeval" + "github.com/cockroachdb/cockroach/pkg/sql/privilege" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" +) + +// Dependencies contains all the dependencies required by the builder. +type Dependencies interface { + CatalogReader() CatalogReader + AuthorizationAccessor() AuthorizationAccessor + + // Codec returns the current session data, as in execCfg. + // So far this is used only to build a tree.EvalContext. + Codec() keys.SQLCodec + + // SessionData returns the current session data, as in execCtx. + SessionData() *sessiondata.SessionData + + // ClusterSettings returns the current cluster settings, as in execCfg. + ClusterSettings() *cluster.Settings + + // Statements returns the statements behind this schema change. + Statements() []string +} + +// CatalogReader should implement descriptor resolution, namespace lookups, and +// all such catalog read operations for the builder. The following contract must +// apply: +// - errors are panicked; +// - caches are avoided at all times, we read straight from storage; +// - MayResolve* methods return zero values if nothing could be found; +// - MayResolve* methods ignore dropped or offline descriptors; +// - MustReadDescriptor does not; +// - MustReadDescriptor panics if the descriptor was not found. +type CatalogReader interface { + tree.TypeReferenceResolver + tree.QualifiedNameResolver + + // MayResolveDatabase looks up a database by name. + MayResolveDatabase(ctx context.Context, name tree.Name) catalog.DatabaseDescriptor + + // MayResolveSchema looks up a schema by name. + MayResolveSchema(ctx context.Context, name tree.ObjectNamePrefix) (catalog.DatabaseDescriptor, catalog.SchemaDescriptor) + + // MayResolveTable looks up a table by name. + MayResolveTable(ctx context.Context, name tree.UnresolvedObjectName) (catalog.ResolvedObjectPrefix, catalog.TableDescriptor) + + // MayResolveType looks up a type by name. + MayResolveType(ctx context.Context, name tree.UnresolvedObjectName) (catalog.ResolvedObjectPrefix, catalog.TypeDescriptor) + + // ReadObjectNamesAndIDs looks up the namespace entries for a schema. + ReadObjectNamesAndIDs(ctx context.Context, db catalog.DatabaseDescriptor, schema catalog.SchemaDescriptor) (tree.TableNames, descpb.IDs) + + // MustReadDescriptor looks up a descriptor by ID. + MustReadDescriptor(ctx context.Context, id descpb.ID) catalog.Descriptor +} + +// AuthorizationAccessor for checking authorization (e.g. desc privileges). +type AuthorizationAccessor interface { + + // CheckPrivilege verifies that the current user has `privilege` on + // `descriptor`. + CheckPrivilege( + ctx context.Context, descriptor catalog.Descriptor, privilege privilege.Kind, + ) error + + // HasAdminRole verifies if a user has an admin role. + HasAdminRole(ctx context.Context) (bool, error) + + // HasOwnership returns true iff the role, or any role the role is a member + // of, has ownership privilege of the desc. + HasOwnership(ctx context.Context, descriptor catalog.Descriptor) (bool, error) +} + +func mustReadDatabase( + ctx context.Context, d Dependencies, id descpb.ID, +) catalog.DatabaseDescriptor { + desc := d.CatalogReader().MustReadDescriptor(ctx, id) + db, err := catalog.AsDatabaseDescriptor(desc) + onErrPanic(err) + return db +} + +func mustReadSchema(ctx context.Context, d Dependencies, id descpb.ID) catalog.SchemaDescriptor { + desc := d.CatalogReader().MustReadDescriptor(ctx, id) + schema, err := catalog.AsSchemaDescriptor(desc) + onErrPanic(err) + return schema +} + +func mustReadTable(ctx context.Context, d Dependencies, id descpb.ID) catalog.TableDescriptor { + desc := d.CatalogReader().MustReadDescriptor(ctx, id) + table, err := catalog.AsTableDescriptor(desc) + onErrPanic(err) + return table +} + +func mustReadType(ctx context.Context, d Dependencies, id descpb.ID) catalog.TypeDescriptor { + desc := d.CatalogReader().MustReadDescriptor(ctx, id) + typ, err := catalog.AsTypeDescriptor(desc) + onErrPanic(err) + return typ +} + +func semaCtx(d Dependencies) *tree.SemaContext { + semaCtx := tree.MakeSemaContext() + semaCtx.Annotations = nil + semaCtx.SearchPath = d.SessionData().SearchPath + semaCtx.IntervalStyleEnabled = d.SessionData().IntervalStyleEnabled + semaCtx.DateStyleEnabled = d.SessionData().DateStyleEnabled + semaCtx.TypeResolver = d.CatalogReader() + semaCtx.TableNameResolver = d.CatalogReader() + semaCtx.DateStyle = d.SessionData().GetDateStyle() + semaCtx.IntervalStyle = d.SessionData().GetIntervalStyle() + return &semaCtx +} + +func evalCtx(ctx context.Context, d Dependencies) *tree.EvalContext { + return &tree.EvalContext{ + SessionDataStack: sessiondata.NewStack(d.SessionData()), + Context: ctx, + Planner: &faketreeeval.DummyEvalPlanner{}, + PrivilegedAccessor: &faketreeeval.DummyPrivilegedAccessor{}, + SessionAccessor: &faketreeeval.DummySessionAccessor{}, + ClientNoticeSender: &faketreeeval.DummyClientNoticeSender{}, + Sequence: &faketreeeval.DummySequenceOperators{}, + Tenant: &faketreeeval.DummyTenantOperator{}, + Regions: &faketreeeval.DummyRegionOperator{}, + Settings: d.ClusterSettings(), + Codec: d.Codec(), + } +} diff --git a/pkg/sql/schemachanger/scbuild/relation_common.go b/pkg/sql/schemachanger/scbuild/relation_common.go index dce2b2a334f2..1f89792391a1 100644 --- a/pkg/sql/schemachanger/scbuild/relation_common.go +++ b/pkg/sql/schemachanger/scbuild/relation_common.go @@ -22,36 +22,23 @@ import ( "github.com/lib/pq/oid" ) -func (b *buildContext) removeTypeBackRefDeps( - ctx context.Context, tableDesc catalog.TableDescriptor, -) { - // TODO(fqazi): Consider cleaning up all references by getting them using tableDesc.GetReferencedDescIDs(), +func (b *buildContext) removeTypeBackRefDeps(ctx context.Context, table catalog.TableDescriptor) { + // TODO(fqazi): Consider cleaning up all references by getting them using table.GetReferencedDescIDs(), // which would include all types of references inside a table descriptor. However, this would also need us // to look up the type of descriptor. - _, dbDesc, err := b.Descs.GetImmutableDatabaseByID(ctx, b.EvalCtx.Txn, - tableDesc.GetParentID(), tree.DatabaseLookupFlags{Required: true}) - if err != nil { - panic(err) - } - typeIDs, _, err := tableDesc.GetAllReferencedTypeIDs(dbDesc, func(id descpb.ID) (catalog.TypeDescriptor, error) { - mutDesc, err := b.Descs.GetMutableTypeByID(ctx, b.EvalCtx.Txn, id, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - return nil, err - } - return mutDesc, nil + db := mustReadDatabase(ctx, b, table.GetParentID()) + typeIDs, _, err := table.GetAllReferencedTypeIDs(db, func(id descpb.ID) (catalog.TypeDescriptor, error) { + return mustReadType(ctx, b, id), nil }) - if err != nil { - panic(err) - } + onErrPanic(err) // Drop all references to this table/view/sequence for _, typeID := range typeIDs { typeRef := &scpb.TypeReference{ TypeID: typeID, - DescID: tableDesc.GetID(), + DescID: table.GetID(), } if exists, _ := b.checkIfNodeExists(scpb.Target_DROP, typeRef); !exists { - b.addNode(scpb.Target_DROP, - typeRef) + b.addNode(scpb.Target_DROP, typeRef) } } } @@ -75,9 +62,7 @@ func (b *buildContext) removeColumnTypeBackRefs(table catalog.TableDescriptor, i continue } expr, err := parser.ParseExpr(col.GetDefaultExpr()) - if err != nil { - panic(err) - } + onErrPanic(err) if col.GetID() == id { tree.WalkExpr(visitorDeleted, expr) } else { @@ -85,9 +70,7 @@ func (b *buildContext) removeColumnTypeBackRefs(table catalog.TableDescriptor, i } if col.IsComputed() { expr, err := parser.ParseExpr(col.GetComputeExpr()) - if err != nil { - panic(err) - } + onErrPanic(err) if col.GetID() == id { tree.WalkExpr(visitorDeleted, expr) } else { @@ -99,9 +82,7 @@ func (b *buildContext) removeColumnTypeBackRefs(table catalog.TableDescriptor, i for oid := range visitorDeleted.OIDs { if _, ok := visitor.OIDs[oid]; !ok { typeID, err := typedesc.UserDefinedTypeOIDToID(oid) - if err != nil { - panic(err) - } + onErrPanic(err) typeRef := &scpb.TypeReference{ TypeID: typeID, DescID: table.GetID(), diff --git a/pkg/sql/schemachanger/scbuild/schema.go b/pkg/sql/schemachanger/scbuild/schema.go index eed2ec84528f..aa63892649bb 100644 --- a/pkg/sql/schemachanger/scbuild/schema.go +++ b/pkg/sql/schemachanger/scbuild/schema.go @@ -12,118 +12,95 @@ package scbuild import ( "context" - "sort" "github.com/cockroachdb/cockroach/pkg/sql/catalog" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/dbdesc" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" "github.com/cockroachdb/errors" ) func (b *buildContext) dropSchemaDesc( - ctx context.Context, sc catalog.SchemaDescriptor, db *dbdesc.Mutable, behavior tree.DropBehavior, -) (nodeAdded bool, dropIDs []descpb.ID) { - nodeAdded = false + ctx context.Context, + db catalog.DatabaseDescriptor, + schema catalog.SchemaDescriptor, + behavior tree.DropBehavior, +) (nodeAdded bool, dropIDs catalog.DescriptorIDSet) { // For non-user defined schemas, another check will be // done each object as we go to drop them. - if sc.SchemaKind() == catalog.SchemaUserDefined { - isAdmin, err := b.AuthAccessor.HasAdminRole(ctx) - if err != nil { - panic(err) - } - hasOwnership, err := b.AuthAccessor.HasOwnership(ctx, sc) - if err != nil { - panic(err) - } + if schema.SchemaKind() == catalog.SchemaUserDefined { + isAdmin, err := b.AuthorizationAccessor().HasAdminRole(ctx) + onErrPanic(err) + hasOwnership, err := b.AuthorizationAccessor().HasOwnership(ctx, schema) + onErrPanic(err) if !(isAdmin || hasOwnership) { - panic(pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied to drop schema %q", sc.GetName())) + panic(pgerror.Newf(pgcode.InsufficientPrivilege, "permission denied to drop schema %q", schema.GetName())) } } - _, dropIDs, err := resolver.GetObjectNamesAndIDs(ctx, b.EvalCtx.Txn, b.Res, b.EvalCtx.Codec, db, sc.GetName(), true /* explicitPrefix */) - if err != nil { - panic(err) + _, objectIDs := b.CatalogReader().ReadObjectNamesAndIDs(ctx, db, schema) + for _, id := range objectIDs { + dropIDs.Add(id) } - if behavior != tree.DropCascade && len(dropIDs) > 0 { + if behavior != tree.DropCascade && !dropIDs.Empty() { panic(pgerror.Newf(pgcode.DependentObjectsStillExist, - "schema %q is not empty and CASCADE was not specified", sc.GetName())) + "schema %q is not empty and CASCADE was not specified", schema.GetName())) } - // Sort the dropped IDs to ensure a consistent order of - // operations. - sort.SliceStable(dropIDs, func(i, j int) bool { - return dropIDs[i] < dropIDs[j] - }) - schemaNode := scpb.Schema{ - SchemaID: sc.GetID(), - DependentObjects: dropIDs, - } - for _, descID := range dropIDs { - tableDesc, err := b.Descs.GetMutableTableByID(ctx, b.EvalCtx.Txn, descID, tree.ObjectLookupFlagsWithRequired()) - if err == nil { - if tableDesc.IsView() { - b.maybeDropViewAndDependents(ctx, tableDesc, behavior) - } else if tableDesc.IsSequence() { - b.dropSequenceDesc(ctx, tableDesc, behavior) - } else if tableDesc.IsTable() { - b.dropTableDesc(ctx, tableDesc, behavior) - } - } else if pgerror.GetPGCode(err) == pgcode.UndefinedTable { - typeDesc, err := b.Descs.GetMutableTypeByID(ctx, b.EvalCtx.Txn, descID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - panic(err) + + for _, id := range dropIDs.Ordered() { + desc := b.CatalogReader().MustReadDescriptor(ctx, id) + switch t := desc.(type) { + case catalog.TableDescriptor: + if t.IsView() { + b.maybeDropViewAndDependents(ctx, t, behavior) + } else if t.IsSequence() { + b.dropSequenceDesc(ctx, t, behavior) + } else if t.IsTable() { + b.dropTableDesc(ctx, t, behavior) + } else { + panic(errors.AssertionFailedf("Table descriptor %q (%d) is neither table, sequence or view", + t.GetName(), t.GetID())) } - b.dropTypeDesc(ctx, typeDesc, behavior, true /* ignoreAlises*/) - } else { - panic(err) + case catalog.TypeDescriptor: + b.dropTypeDesc(ctx, t, behavior, true /* ignoreAliases */) + default: + panic(errors.AssertionFailedf("Expected table or type descriptor, instead %q (%d) is %q", + t.GetName(), t.GetID(), t.DescriptorType())) } } - if sc.SchemaKind() != catalog.SchemaPublic && - sc.SchemaKind() != catalog.SchemaVirtual && - sc.SchemaKind() != catalog.SchemaTemporary { - // Add a schema node with all the dependencies. - b.addNode(scpb.Target_DROP, &schemaNode) - nodeAdded = true + switch schema.SchemaKind() { + case catalog.SchemaPublic, catalog.SchemaVirtual, catalog.SchemaTemporary: + return false, dropIDs + case catalog.SchemaUserDefined: + b.addNode(scpb.Target_DROP, &scpb.Schema{ + SchemaID: schema.GetID(), + DependentObjects: dropIDs.Ordered(), + }) + return true, dropIDs } - return nodeAdded, dropIDs + panic(errors.AssertionFailedf("Unexpected schema kind %q for schema %q (%d)", + schema.SchemaKind(), schema.GetName(), schema.GetID())) } + func (b *buildContext) dropSchema(ctx context.Context, n *tree.DropSchema) { for _, name := range n.Names { - dbName := b.Res.CurrentDatabase() - if name.ExplicitCatalog { - dbName = name.Catalog() - } - scName := name.Schema() - db, err := b.Descs.GetMutableDatabaseByName(ctx, b.EvalCtx.Txn, dbName, - tree.DatabaseLookupFlags{Required: true}) - if err != nil { - panic(err) - } - - sc, err := b.Descs.GetSchemaByName(ctx, b.EvalCtx.Txn, db, scName, tree.SchemaLookupFlags{ - Required: true, - }) - if err != nil { - panic(err) - } - if sc == nil { + db, schema := b.CatalogReader().MayResolveSchema(ctx, name) + if schema == nil { if n.IfExists { continue } - panic(pgerror.Newf(pgcode.InvalidSchemaName, "unknown schema %q", scName)) + panic(sqlerrors.NewUndefinedSchemaError(name.String())) } - - switch sc.SchemaKind() { + switch schema.SchemaKind() { case catalog.SchemaPublic, catalog.SchemaVirtual, catalog.SchemaTemporary: - panic(pgerror.Newf(pgcode.InvalidSchemaName, "cannot drop schema %q", sc.GetName())) + panic(pgerror.Newf(pgcode.InsufficientPrivilege, "cannot drop schema %q", schema.GetName())) case catalog.SchemaUserDefined: - b.dropSchemaDesc(ctx, sc, db, n.DropBehavior) + // break default: - panic(errors.AssertionFailedf("unknown schema kind %d", sc.SchemaKind())) + panic(errors.AssertionFailedf("unknown schema kind %d", schema.SchemaKind())) } + b.dropSchemaDesc(ctx, db, schema, n.DropBehavior) } } diff --git a/pkg/sql/schemachanger/scbuild/sequence.go b/pkg/sql/schemachanger/scbuild/sequence.go index 5e067f37aad0..b79bb3de8734 100644 --- a/pkg/sql/schemachanger/scbuild/sequence.go +++ b/pkg/sql/schemachanger/scbuild/sequence.go @@ -15,12 +15,12 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/privilege" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/errors" + "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" ) // dropSequenceDesc builds targets and transformations using a descriptor. @@ -28,18 +28,15 @@ func (b *buildContext) dropSequenceDesc( ctx context.Context, seq catalog.TableDescriptor, cascade tree.DropBehavior, ) { // Check if there are dependencies. - err := seq.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { + _ = seq.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { if cascade != tree.DropCascade { - return pgerror.Newf( + panic(pgerror.Newf( pgcode.DependentObjectsStillExist, "cannot drop sequence %s because other objects depend on it", seq.GetName(), - ) - } - desc, err := b.Descs.GetImmutableTableByID(ctx, b.EvalCtx.Txn, dep.ID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - return err + )) } + desc := mustReadTable(ctx, b, dep.ID) for _, col := range desc.PublicColumns() { for _, id := range dep.ColumnIDs { if col.GetID() != id { @@ -58,9 +55,6 @@ func (b *buildContext) dropSequenceDesc( } return nil }) - if err != nil { - panic(err) - } // Add a node to drop the sequence sequenceNode := &scpb.Sequence{SequenceID: seq.GetID()} @@ -82,22 +76,17 @@ func (b *buildContext) dropSequenceDesc( func (b *buildContext) dropSequence(ctx context.Context, n *tree.DropSequence) { // Find the sequence first. for _, name := range n.Names { - _, table, err := resolver.ResolveExistingTableObject(ctx, b.Res, &name, - tree.ObjectLookupFlagsWithRequired()) - if err != nil { - if pgerror.GetPGCode(err) == pgcode.UndefinedTable && n.IfExists { + _, table := b.CatalogReader().MayResolveTable(ctx, *name.ToUnresolvedObjectName()) + if table == nil { + if n.IfExists { continue } - panic(err) + panic(sqlerrors.NewUndefinedRelationError(&name)) } - if table == nil { - panic(errors.AssertionFailedf("unable to resolve sequence %s", - name.FQString())) - } - - if table.Dropped() { - return + if !table.IsSequence() { + panic(pgerror.Newf(pgcode.WrongObjectType, "%q is not a sequence", table.GetName())) } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, table, privilege.DROP)) b.dropSequenceDesc(ctx, table, n.DropBehavior) } } diff --git a/pkg/sql/schemachanger/scbuild/table.go b/pkg/sql/schemachanger/scbuild/table.go index a330d3b488bd..355683a50663 100644 --- a/pkg/sql/schemachanger/scbuild/table.go +++ b/pkg/sql/schemachanger/scbuild/table.go @@ -15,7 +15,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemaexpr" "github.com/cockroachdb/cockroach/pkg/sql/catalog/seqexpr" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" @@ -43,14 +42,16 @@ func (b *buildContext) alterTable(ctx context.Context, n *tree.AlterTable) { // that is bad. n.HoistAddColumnConstraints() - // Resolve the table. tn := n.Table.ToTableName() - table, err := b.getTableDescriptorForLockingChange(ctx, &tn) - if err != nil { - if errors.Is(err, catalog.ErrDescriptorNotFound) && n.IfExists { + _, table := b.CatalogReader().MayResolveTable(ctx, *n.Table) + if table == nil { + if n.IfExists { return } - panic(err) + panic(sqlerrors.NewUndefinedRelationError(n.Table)) + } + if HasConcurrentSchemaChanges(table) { + panic(&ConcurrentSchemaChangeError{descID: table.GetID()}) } for _, cmd := range n.Cmds { b.alterTableCmd(ctx, table, cmd, &tn) @@ -77,15 +78,13 @@ func (b *buildContext) alterTableAddColumn( d := t.ColumnDef if d.IsComputed() { - d.Computed.Expr = schemaexpr.MaybeRewriteComputedColumn(d.Computed.Expr, b.EvalCtx.SessionData()) + d.Computed.Expr = schemaexpr.MaybeRewriteComputedColumn(d.Computed.Expr, b.SessionData()) } - toType, err := tree.ResolveType(ctx, d.Type, b.SemaCtx.GetTypeResolver()) - if err != nil { - panic(err) - } + toType, err := tree.ResolveType(ctx, d.Type, b.CatalogReader()) + onErrPanic(err) - version := b.EvalCtx.Settings.Version.ActiveVersionOrEmpty(ctx) + version := b.ClusterSettings().Version.ActiveVersionOrEmpty(ctx) supported := types.IsTypeSupportedInVersion(version, toType) if !supported { panic(pgerror.Newf( @@ -109,10 +108,9 @@ func (b *buildContext) alterTableAddColumn( if d.Unique.IsUnique { panic(¬ImplementedError{n: t.ColumnDef, detail: "contains unique constraint"}) } - col, idx, defaultExpr, err := tabledesc.MakeColumnDefDescs(ctx, d, b.SemaCtx, b.EvalCtx) - if err != nil { - panic(err) - } + col, idx, defaultExpr, err := tabledesc.MakeColumnDefDescs(ctx, d, semaCtx(b), evalCtx(ctx, b)) + onErrPanic(err) + colID := b.nextColumnID(table) col.ID = colID @@ -144,29 +142,22 @@ func (b *buildContext) alterTableAddColumn( // TODO (lucy): This is not going to work when the computed column // references columns created in the same transaction. serializedExpr, _, err := schemaexpr.ValidateComputedColumnExpression( - ctx, table, d, tn, "computed column", b.SemaCtx, + ctx, table, d, tn, "computed column", semaCtx(b), ) - if err != nil { - panic(err) - } + onErrPanic(err) col.ComputeExpr = &serializedExpr } if toType.UserDefined() { typeID, err := typedesc.UserDefinedTypeOIDToID(toType.Oid()) - if err != nil { - panic(err) - } - typeDesc, err := b.Descs.GetMutableTypeByID(ctx, b.EvalCtx.Txn, typeID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - panic(err) - } + onErrPanic(err) + typeDesc := mustReadType(ctx, b, typeID) // Only add a type reference node only if there isn't // any existing reference inside this table. This makes // it easier to handle drop columns and other operations, // since those can for example only remove nodes. found := false - for _, refID := range typeDesc.GetReferencingDescriptorIDs() { + for _, refID := range typeDesc.TypeDesc().GetReferencingDescriptorIDs() { if refID == table.GetID() { found = true break @@ -279,7 +270,7 @@ func (b *buildContext) findOrAddColumnFamily( func (b *buildContext) alterTableDropColumn( ctx context.Context, table catalog.TableDescriptor, t *tree.AlterTableDropColumn, ) { - if b.EvalCtx.SessionData().SafeUpdates { + if b.SessionData().SafeUpdates { panic(pgerror.DangerousStatementf("ALTER TABLE DROP COLUMN will " + "remove all data in that column")) } @@ -335,15 +326,10 @@ func (b *buildContext) alterTableDropColumn( } if needsDrop { typeID, err := typedesc.UserDefinedTypeOIDToID(colType.Oid()) - if err != nil { - panic(err) - } - typeDesc, err := b.Descs.GetMutableTypeByID(ctx, b.EvalCtx.Txn, typeID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - panic(err) - } + onErrPanic(err) + typ := mustReadType(ctx, b, typeID) b.addNode(scpb.Target_DROP, &scpb.TypeReference{ - TypeID: typeDesc.GetID(), + TypeID: typ.GetID(), DescID: table.GetID(), }) } @@ -366,37 +352,25 @@ func (b *buildContext) maybeAddSequenceReferenceDependencies( ctx context.Context, tableID descpb.ID, col *descpb.ColumnDescriptor, defaultExpr tree.TypedExpr, ) { seqIdentifiers, err := seqexpr.GetUsedSequences(defaultExpr) - if err != nil { - panic(err) - } + onErrPanic(err) - var tn tree.TableName seqNameToID := make(map[string]int64) for _, seqIdentifier := range seqIdentifiers { + var seq catalog.TableDescriptor if seqIdentifier.IsByID() { - name, err := b.SemaCtx.TableNameResolver.GetQualifiedTableNameByID( - ctx, seqIdentifier.SeqID, tree.ResolveRequireSequenceDesc) - if err != nil { - panic(err) - } - tn = *name + seq = mustReadTable(ctx, b, descpb.ID(seqIdentifier.SeqID)) } else { parsedSeqName, err := parser.ParseTableName(seqIdentifier.SeqName) - if err != nil { - panic(err) + onErrPanic(err) + _, seq = b.CatalogReader().MayResolveTable(ctx, *parsedSeqName) + if seq == nil { + panic(errors.WithAssertionFailure(sqlerrors.NewUndefinedRelationError(parsedSeqName))) } - tn = parsedSeqName.ToTableName() + seqNameToID[seqIdentifier.SeqName] = int64(seq.GetID()) } - - seqDesc, err := b.getTableDescriptor(ctx, &tn) - if err != nil { - panic(err) - } - seqNameToID[seqIdentifier.SeqName] = int64(seqDesc.GetID()) - - col.UsesSequenceIds = append(col.UsesSequenceIds, seqDesc.GetID()) + col.UsesSequenceIds = append(col.UsesSequenceIds, seq.GetID()) b.addNode(scpb.Target_ADD, &scpb.SequenceDependency{ - SequenceID: seqDesc.GetID(), + SequenceID: seq.GetID(), TableID: tableID, ColumnID: col.ID, }) @@ -404,9 +378,7 @@ func (b *buildContext) maybeAddSequenceReferenceDependencies( if len(seqIdentifiers) > 0 { newExpr, err := seqexpr.ReplaceSequenceNamesWithIDs(defaultExpr, seqNameToID) - if err != nil { - panic(err) - } + onErrPanic(err) s := tree.Serialize(newExpr) col.DefaultExpr = &s } @@ -584,22 +556,16 @@ func (b *buildContext) maybeCleanTableSequenceRefs( // Loop over owned sequences for seqIdx := 0; seqIdx < col.NumOwnsSequences(); seqIdx++ { seqID := col.GetOwnsSequenceID(seqIdx) - table, err := b.Descs.GetMutableTableByID(ctx, b.EvalCtx.Txn, seqID, tree.ObjectLookupFlagsWithRequiredTableKind(tree.ResolveRequireSequenceDesc)) - if err != nil { - panic(err) - } + seq := mustReadTable(ctx, b, seqID) if behavior != tree.DropCascade { panic(pgerror.Newf( pgcode.DependentObjectsStillExist, "cannot drop table %s because other objects depend on it", - table.GetName(), + seq.GetName(), )) } - err = b.AuthAccessor.CheckPrivilege(ctx, table, privilege.DROP) - if err != nil { - panic(err) - } - b.dropSequenceDesc(ctx, table, tree.DropCascade) + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, seq, privilege.DROP)) + b.dropSequenceDesc(ctx, seq, tree.DropCascade) } // Setup logic to clean up the default expression always. defaultExpr := &scpb.DefaultExpression{ @@ -617,15 +583,11 @@ func (b *buildContext) maybeCleanTableSequenceRefs( } if col.HasDefault() && !col.ColumnDesc().HasNullDefault() { expr, err := parser.ParseExpr(col.GetDefaultExpr()) - if err != nil { - panic(err) - } + onErrPanic(err) tree.WalkExpr(visitor, expr) for oid := range visitor.OIDs { typeID, err := typedesc.UserDefinedTypeOIDToID(oid) - if err != nil { - panic(err) - } + onErrPanic(err) typeRef := &scpb.TypeReference{ TypeID: typeID, DescID: table.GetID(), @@ -657,19 +619,13 @@ func (b *buildContext) maybeCleanTableFKs( ) { // Loop through and update inbound and outbound // foreign key references. _ = table.ForeachInboundFK(func(fk *descpb.ForeignKeyConstraint) error { - dependentTable, err := b.Descs.GetImmutableTableByID(ctx, b.EvalCtx.Txn, fk.OriginTableID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - panic(err) - } + dependentTable := mustReadTable(ctx, b, fk.OriginTableID) if behavior != tree.DropCascade { panic(pgerror.Newf( pgcode.DependentObjectsStillExist, "%q is referenced by foreign key from table %q", fk.Name, dependentTable.GetName())) } - err = b.AuthAccessor.CheckPrivilege(ctx, dependentTable, privilege.DROP) - if err != nil { - panic(err) - } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, dependentTable, privilege.DROP)) outFkNode := &scpb.OutboundForeignKey{ OriginID: fk.OriginTableID, OriginColumns: fk.OriginColumnIDs, @@ -737,26 +693,17 @@ func (b *buildContext) dropTableDesc( } // Drop dependent views - err := table.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { - dependentDesc, err := b.Descs.GetImmutableTableByID(ctx, b.EvalCtx.Txn, dep.ID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - panic(err) - } + onErrPanic(table.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { + dependentDesc := mustReadTable(ctx, b, dep.ID) if behavior != tree.DropCascade { return pgerror.Newf( pgcode.DependentObjectsStillExist, "cannot drop table %q because view %q depends on it", table.GetName(), dependentDesc.GetName()) } - err = b.AuthAccessor.CheckPrivilege(ctx, dependentDesc, privilege.DROP) - if err != nil { - panic(err) - } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, dependentDesc, privilege.DROP)) b.maybeDropViewAndDependents(ctx, dependentDesc, behavior) return nil - }) - if err != nil { - panic(err) - } + })) // Clean up foreign key references (both inbound // and out bound). @@ -773,18 +720,17 @@ func (b *buildContext) dropTableDesc( func (b *buildContext) dropTable(ctx context.Context, n *tree.DropTable) { // Find the table first. for _, name := range n.Names { - _, table, err := resolver.ResolveExistingTableObject(ctx, b.Res, &name, - tree.ObjectLookupFlagsWithRequired()) - if err != nil { - if errors.Is(err, catalog.ErrDescriptorNotFound) && n.IfExists { - return + _, table := b.CatalogReader().MayResolveTable(ctx, *name.ToUnresolvedObjectName()) + if table == nil { + if n.IfExists { + continue } - panic(err) + panic(sqlerrors.NewUndefinedRelationError(&name)) } - if table == nil { - panic(errors.AssertionFailedf("Unable to resolve table %s", - name.FQString())) + if !table.IsTable() { + panic(pgerror.Newf(pgcode.WrongObjectType, "%q is not a table", table.GetName())) } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, table, privilege.DROP)) b.dropTableDesc(ctx, table, n.DropBehavior) } } diff --git a/pkg/sql/schemachanger/scbuild/testdata/drop_database b/pkg/sql/schemachanger/scbuild/testdata/drop_database index 9b139e21120d..e9aa541831d8 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/drop_database +++ b/pkg/sql/schemachanger/scbuild/testdata/drop_database @@ -55,7 +55,6 @@ DROP DATABASE db1 CASCADE details: databaseId: 52 dependentObjects: - - 29 - 53 - 54 - 57 diff --git a/pkg/sql/schemachanger/scbuild/type.go b/pkg/sql/schemachanger/scbuild/type.go index b98ffaeedcd5..492909b4cd8c 100644 --- a/pkg/sql/schemachanger/scbuild/type.go +++ b/pkg/sql/schemachanger/scbuild/type.go @@ -13,30 +13,27 @@ package scbuild import ( "context" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/privilege" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sqlerrors" "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" "github.com/cockroachdb/errors" ) -func (b *buildContext) canModifyType(ctx context.Context, desc *typedesc.Mutable) { - hasAdmin, err := b.AuthAccessor.HasAdminRole(ctx) - if err != nil { - panic(err) - } +func (b *buildContext) canModifyType(ctx context.Context, desc catalog.TypeDescriptor) { + hasAdmin, err := b.AuthorizationAccessor().HasAdminRole(ctx) + onErrPanic(err) if hasAdmin { return } - hasOwnership, err := b.AuthAccessor.HasOwnership(ctx, desc) - if err != nil { - panic(err) - } + hasOwnership, err := b.AuthorizationAccessor().HasOwnership(ctx, desc) + onErrPanic(err) if !hasOwnership { panic(pgerror.Newf(pgcode.InsufficientPrivilege, "must be owner of type %s", tree.Name(desc.GetName()))) @@ -44,31 +41,31 @@ func (b *buildContext) canModifyType(ctx context.Context, desc *typedesc.Mutable } func (b *buildContext) canDropTypeDesc( - ctx context.Context, typeDesc *typedesc.Mutable, behavior tree.DropBehavior, + ctx context.Context, typ catalog.TypeDescriptor, behavior tree.DropBehavior, ) { - b.canModifyType(ctx, typeDesc) - if len(typeDesc.ReferencingDescriptorIDs) > 0 && behavior != tree.DropCascade { - dependentNames := make([]*tree.TableName, 0, len(typeDesc.ReferencingDescriptorIDs)) - for _, descID := range typeDesc.ReferencingDescriptorIDs { - name, err := b.Res.GetQualifiedTableNameByID(ctx, int64(descID), tree.ResolveAnyTableKind) + b.canModifyType(ctx, typ) + if len(typ.TypeDesc().ReferencingDescriptorIDs) > 0 && behavior != tree.DropCascade { + dependentNames := make([]*tree.TableName, 0, len(typ.TypeDesc().ReferencingDescriptorIDs)) + for _, descID := range typ.TypeDesc().ReferencingDescriptorIDs { + name, err := b.CatalogReader().GetQualifiedTableNameByID(ctx, int64(descID), tree.ResolveAnyTableKind) if err != nil { - panic(errors.Wrapf(err, "type %q has dependent objects", typeDesc.Name)) + panic(errors.Wrapf(err, "type %q has dependent objects", typ.GetName())) } dependentNames = append(dependentNames, name) } panic(pgerror.Newf( pgcode.DependentObjectsStillExist, "cannot drop type %q because other objects (%v) still depend on it", - typeDesc.Name, + typ.GetName(), dependentNames, )) } } func (b *buildContext) dropTypeDesc( - ctx context.Context, typeDesc *typedesc.Mutable, behavior tree.DropBehavior, ignoreAliases bool, + ctx context.Context, typ catalog.TypeDescriptor, behavior tree.DropBehavior, ignoreAliases bool, ) { - switch typeDesc.Kind { + switch typ.GetKind() { case descpb.TypeDescriptor_ALIAS: if ignoreAliases { return @@ -77,7 +74,7 @@ func (b *buildContext) dropTypeDesc( panic(pgerror.Newf( pgcode.DependentObjectsStillExist, "%q is an implicit array type and cannot be modified", - typeDesc.GetName(), + typ.GetName(), )) case descpb.TypeDescriptor_MULTIREGION_ENUM: // Multi-region enums are not directly droppable. @@ -85,23 +82,20 @@ func (b *buildContext) dropTypeDesc( pgerror.Newf( pgcode.DependentObjectsStillExist, "%q is a multi-region enum and cannot be modified directly", - typeDesc.GetName(), + typ.GetName(), ), - "try ALTER DATABASE DROP REGION %s", typeDesc.GetName())) + "try ALTER DATABASE DROP REGION %s", typ.GetName())) case descpb.TypeDescriptor_ENUM: sqltelemetry.IncrementEnumCounter(sqltelemetry.EnumDrop) } - b.canDropTypeDesc(ctx, typeDesc, behavior) + b.canDropTypeDesc(ctx, typ, behavior) // Get the array type that needs to be dropped as well. - mutArrayDesc, err := b.Descs.GetMutableTypeVersionByID(ctx, b.EvalCtx.Txn, typeDesc.ArrayTypeID) - if err != nil { - panic(err) - } + mutArrayDesc := mustReadType(ctx, b, typ.GetArrayTypeID()) // Ensure that we can drop the array type as well. b.canDropTypeDesc(ctx, mutArrayDesc, behavior) // Create drop elements for both. typeDescElem := &scpb.Type{ - TypeID: typeDesc.GetID(), + TypeID: typ.GetID(), } b.addNode(scpb.Target_DROP, typeDescElem) mutArrayDescElem := &scpb.Type{ @@ -115,14 +109,14 @@ func (b *buildContext) dropType(ctx context.Context, n *tree.DropType) { panic(unimplemented.NewWithIssue(51480, "DROP TYPE CASCADE is not yet supported")) } for _, name := range n.Names { - // Resolve the desired type descriptor. - _, typeDesc, err := resolver.ResolveMutableType(ctx, b.Res, name, !n.IfExists) - if err != nil { - panic(err) - } - if typeDesc == nil { - continue + _, typ := b.CatalogReader().MayResolveType(ctx, *name) + if typ == nil { + if n.IfExists { + continue + } + panic(sqlerrors.NewUndefinedTypeError(name)) } - b.dropTypeDesc(ctx, typeDesc, n.DropBehavior, false /* ignoreAliases */) + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, typ, privilege.DROP)) + b.dropTypeDesc(ctx, typ, n.DropBehavior, false /* ignoreAliases */) } } diff --git a/pkg/sql/schemachanger/scbuild/view.go b/pkg/sql/schemachanger/scbuild/view.go index 7e93c360d6b8..a8768d06a4ec 100644 --- a/pkg/sql/schemachanger/scbuild/view.go +++ b/pkg/sql/schemachanger/scbuild/view.go @@ -16,7 +16,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/privilege" @@ -32,10 +31,7 @@ func (b *buildContext) maybeDropViewAndDependents( ctx context.Context, view catalog.TableDescriptor, behavior tree.DropBehavior, ) { // Validate we have drop privileges. - err := b.AuthAccessor.CheckPrivilege(ctx, view, privilege.DROP) - if err != nil { - panic(err) - } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, view, privilege.DROP)) // Create a node for the view we are going to drop. viewNode := &scpb.View{ TableID: view.GetID(), @@ -74,25 +70,14 @@ func (b *buildContext) maybeDropViewAndDependents( // Remove any type back refs. b.removeTypeBackRefDeps(ctx, view) // Drop any dependent views next. - err = view.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { - dependentDesc, err := b.Descs.GetImmutableTableByID(ctx, b.EvalCtx.Txn, dep.ID, tree.ObjectLookupFlagsWithRequired()) - if err != nil { - return err - } - err = b.AuthAccessor.CheckPrivilege(ctx, dependentDesc, privilege.DROP) - if err != nil { - return err - } + _ = view.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { + dependentDesc := mustReadTable(ctx, b, dep.ID) + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, dependentDesc, privilege.DROP)) if behavior != tree.DropCascade { - name, err := b.Res.GetQualifiedTableNameByID(ctx, int64(view.GetID()), tree.ResolveRequireViewDesc) - if err != nil { - return err - } - - depViewName, err := b.Res.GetQualifiedTableNameByID(ctx, int64(dep.ID), tree.ResolveRequireViewDesc) - if err != nil { - return err - } + name, err := b.CatalogReader().GetQualifiedTableNameByID(ctx, int64(view.GetID()), tree.ResolveRequireViewDesc) + onErrPanic(err) + depViewName, err := b.CatalogReader().GetQualifiedTableNameByID(ctx, int64(dep.ID), tree.ResolveRequireViewDesc) + onErrPanic(err) panic(errors.WithHintf( sqlerrors.NewDependentObjectErrorf("cannot drop view %q because view %q depends on it", name, depViewName.FQString()), @@ -101,10 +86,6 @@ func (b *buildContext) maybeDropViewAndDependents( b.maybeDropViewAndDependents(ctx, dependentDesc, behavior) return nil }) - if err != nil { - panic(err) - } - } // dropView builds targets and transforms the provided schema change nodes @@ -112,26 +93,24 @@ func (b *buildContext) maybeDropViewAndDependents( func (b *buildContext) dropView(ctx context.Context, n *tree.DropView) { // Find the view first. for _, name := range n.Names { - _, view, err := resolver.ResolveExistingTableObject(ctx, b.Res, &name, - tree.ObjectLookupFlagsWithRequired()) - if err != nil { - if errors.Is(err, catalog.ErrDescriptorNotFound) && n.IfExists { + _, table := b.CatalogReader().MayResolveTable(ctx, *name.ToUnresolvedObjectName()) + if table == nil { + if n.IfExists { continue } - panic(err) + panic(sqlerrors.NewUndefinedRelationError(&name)) } - if view == nil { - panic(errors.AssertionFailedf("Unable to resolve view %s", - name.FQString())) + if !table.IsView() { + panic(pgerror.Newf(pgcode.WrongObjectType, "%q is not a view", table.GetName())) } - // Check if its materialized or not correctly. - isMaterialized := view.MaterializedView() - if isMaterialized && !n.IsMaterialized { - panic(errors.WithHint(pgerror.Newf(pgcode.WrongObjectType, "%q is a materialized view", view.GetName()), + if table.MaterializedView() && !n.IsMaterialized { + panic(errors.WithHint(pgerror.Newf(pgcode.WrongObjectType, "%q is a materialized view", table.GetName()), "use the corresponding MATERIALIZED VIEW command")) - } else if !isMaterialized && n.IsMaterialized { - panic(pgerror.Newf(pgcode.WrongObjectType, "%q is not a materialized view", view.GetName())) } - b.maybeDropViewAndDependents(ctx, view, n.DropBehavior) + if !table.MaterializedView() && n.IsMaterialized { + panic(pgerror.Newf(pgcode.WrongObjectType, "%q is not a materialized view", table.GetName())) + } + onErrPanic(b.AuthorizationAccessor().CheckPrivilege(ctx, table, privilege.DROP)) + b.maybeDropViewAndDependents(ctx, table, n.DropBehavior) } } diff --git a/pkg/sql/schemachanger/scdeps/BUILD.bazel b/pkg/sql/schemachanger/scdeps/BUILD.bazel new file mode 100644 index 000000000000..f3f9f9771aba --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "scdeps", + srcs = [ + "build_deps.go", + "exec_deps.go", + "run_deps.go", + ], + importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps", + visibility = ["//visibility:public"], + deps = [ + "//pkg/jobs", + "//pkg/jobs/jobspb", + "//pkg/keys", + "//pkg/kv", + "//pkg/roachpb:with-mocks", + "//pkg/security", + "//pkg/settings/cluster", + "//pkg/sql/catalog", + "//pkg/sql/catalog/catalogkeys", + "//pkg/sql/catalog/descpb", + "//pkg/sql/catalog/descs", + "//pkg/sql/catalog/resolver", + "//pkg/sql/schemachanger/scbuild", + "//pkg/sql/schemachanger/scexec", + "//pkg/sql/schemachanger/scop", + "//pkg/sql/schemachanger/scrun", + "//pkg/sql/sem/tree", + "//pkg/sql/sessiondata", + "//pkg/sql/sqlutil", + "//pkg/sql/types", + "@com_github_cockroachdb_errors//:errors", + "@com_github_lib_pq//oid", + ], +) diff --git a/pkg/sql/schemachanger/scdeps/build_deps.go b/pkg/sql/schemachanger/scdeps/build_deps.go new file mode 100644 index 000000000000..e4ddcbb8f294 --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/build_deps.go @@ -0,0 +1,225 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scdeps + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/lib/pq/oid" +) + +// NewBuilderDependencies returns an scbuild.Dependencies implementation built +// from the given arguments. +func NewBuilderDependencies( + codec keys.SQLCodec, + txn *kv.Txn, + descsCollection *descs.Collection, + schemaResolver resolver.SchemaResolver, + authAccessor scbuild.AuthorizationAccessor, + sessionData *sessiondata.SessionData, + settings *cluster.Settings, + statements []string, +) scbuild.Dependencies { + return &buildDeps{ + codec: codec, + txn: txn, + descsCollection: descsCollection, + schemaResolver: schemaResolver, + authAccessor: authAccessor, + sessionData: sessionData, + settings: settings, + statements: statements, + } +} + +type buildDeps struct { + codec keys.SQLCodec + txn *kv.Txn + descsCollection *descs.Collection + schemaResolver resolver.SchemaResolver + authAccessor scbuild.AuthorizationAccessor + sessionData *sessiondata.SessionData + settings *cluster.Settings + statements []string +} + +var _ scbuild.CatalogReader = (*buildDeps)(nil) + +// MayResolveDatabase implements the scbuild.CatalogReader interface. +func (d *buildDeps) MayResolveDatabase( + ctx context.Context, name tree.Name, +) catalog.DatabaseDescriptor { + db, err := d.descsCollection.GetImmutableDatabaseByName(ctx, d.txn, name.String(), tree.DatabaseLookupFlags{ + AvoidCached: true, + }) + if err != nil { + panic(err) + } + return db +} + +// MayResolveSchema implements the scbuild.CatalogReader interface. +func (d *buildDeps) MayResolveSchema( + ctx context.Context, name tree.ObjectNamePrefix, +) (catalog.DatabaseDescriptor, catalog.SchemaDescriptor) { + if !name.ExplicitCatalog { + name.CatalogName = tree.Name(d.schemaResolver.CurrentDatabase()) + } + db := d.MayResolveDatabase(ctx, name.CatalogName) + schema, err := d.descsCollection.GetSchemaByName(ctx, d.txn, db, name.Schema(), tree.SchemaLookupFlags{ + AvoidCached: true, + }) + if err != nil { + panic(err) + } + return db, schema +} + +// MayResolveTable implements the scbuild.CatalogReader interface. +func (d *buildDeps) MayResolveTable( + ctx context.Context, name tree.UnresolvedObjectName, +) (catalog.ResolvedObjectPrefix, catalog.TableDescriptor) { + desc, prefix, err := resolver.ResolveExistingObject(ctx, d.schemaResolver, &name, tree.ObjectLookupFlags{ + CommonLookupFlags: tree.CommonLookupFlags{ + AvoidCached: true, + }, + DesiredObjectKind: tree.TableObject, + }) + if err != nil { + panic(err) + } + if desc == nil { + return prefix, nil + } + return prefix, desc.(catalog.TableDescriptor) +} + +// MayResolveType implements the scbuild.CatalogReader interface. +func (d *buildDeps) MayResolveType( + ctx context.Context, name tree.UnresolvedObjectName, +) (catalog.ResolvedObjectPrefix, catalog.TypeDescriptor) { + desc, prefix, err := resolver.ResolveExistingObject(ctx, d.schemaResolver, &name, tree.ObjectLookupFlags{ + CommonLookupFlags: tree.CommonLookupFlags{ + AvoidCached: true, + }, + DesiredObjectKind: tree.TypeObject, + }) + if err != nil { + panic(err) + } + if desc == nil { + return prefix, nil + } + return prefix, desc.(catalog.TypeDescriptor) +} + +// ReadObjectNamesAndIDs implements the scbuild.CatalogReader interface. +func (d *buildDeps) ReadObjectNamesAndIDs( + ctx context.Context, db catalog.DatabaseDescriptor, schema catalog.SchemaDescriptor, +) (tree.TableNames, descpb.IDs) { + names, ids, err := d.descsCollection.GetObjectNamesAndIDs(ctx, d.txn, db, schema.GetName(), tree.DatabaseListFlags{ + CommonLookupFlags: tree.CommonLookupFlags{ + Required: true, + RequireMutable: false, + AvoidCached: true, + IncludeOffline: true, + IncludeDropped: true, + }, + ExplicitPrefix: true, + }) + if err != nil { + panic(err) + } + return names, ids +} + +// ResolveType implements the scbuild.CatalogReader interface. +func (d *buildDeps) ResolveType( + ctx context.Context, name *tree.UnresolvedObjectName, +) (*types.T, error) { + return d.schemaResolver.ResolveType(ctx, name) +} + +// ResolveTypeByOID implements the scbuild.CatalogReader interface. +func (d *buildDeps) ResolveTypeByOID(ctx context.Context, oid oid.Oid) (*types.T, error) { + return d.schemaResolver.ResolveTypeByOID(ctx, oid) +} + +// GetQualifiedTableNameByID implements the scbuild.CatalogReader interface. +func (d *buildDeps) GetQualifiedTableNameByID( + ctx context.Context, id int64, requiredType tree.RequiredTableKind, +) (*tree.TableName, error) { + return d.schemaResolver.GetQualifiedTableNameByID(ctx, id, requiredType) +} + +// CurrentDatabase implements the scbuild.CatalogReader interface. +func (d *buildDeps) CurrentDatabase() string { + return d.schemaResolver.CurrentDatabase() +} + +// MustReadDescriptor implements the scbuild.CatalogReader interface. +func (d *buildDeps) MustReadDescriptor(ctx context.Context, id descpb.ID) catalog.Descriptor { + flags := tree.CommonLookupFlags{ + Required: true, + RequireMutable: false, + AvoidCached: true, + IncludeOffline: true, + IncludeDropped: true, + } + desc, err := d.descsCollection.GetImmutableDescriptorByID(ctx, d.txn, id, flags) + if err != nil { + panic(err) + } + return desc +} + +var _ scbuild.Dependencies = (*buildDeps)(nil) + +// AuthorizationAccessor implements the scbuild.Dependencies interface. +func (d *buildDeps) AuthorizationAccessor() scbuild.AuthorizationAccessor { + return d.authAccessor +} + +// CatalogReader implements the scbuild.Dependencies interface. +func (d *buildDeps) CatalogReader() scbuild.CatalogReader { + return d +} + +// Codec implements the scbuild.Dependencies interface. +func (d *buildDeps) Codec() keys.SQLCodec { + return d.codec +} + +// SessionData implements the scbuild.Dependencies interface. +func (d *buildDeps) SessionData() *sessiondata.SessionData { + return d.sessionData +} + +// ClusterSettings implements the scbuild.Dependencies interface. +func (d *buildDeps) ClusterSettings() *cluster.Settings { + return d.settings +} + +// Statements implements the scbuild.Dependencies interface. +func (d *buildDeps) Statements() []string { + return d.statements +} diff --git a/pkg/sql/schemachanger/scdeps/exec_deps.go b/pkg/sql/schemachanger/scdeps/exec_deps.go new file mode 100644 index 000000000000..0ccf99a49ffc --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/exec_deps.go @@ -0,0 +1,243 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scdeps + +import ( + "context" + "time" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkeys" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/errors" +) + +// NewExecutorDependencies returns an scexec.Dependencies implementation built +// from the given arguments. +func NewExecutorDependencies( + codec keys.SQLCodec, + txn *kv.Txn, + descsCollection *descs.Collection, + jobRegistry *jobs.Registry, + indexBackfiller scexec.IndexBackfiller, + testingKnobs *scexec.NewSchemaChangerTestingKnobs, + statements []string, + phase scop.Phase, +) scexec.Dependencies { + return &execDeps{ + txnDeps: txnDeps{ + txn: txn, + codec: codec, + descsCollection: descsCollection, + jobRegistry: jobRegistry, + }, + indexBackfiller: indexBackfiller, + testingKnobs: testingKnobs, + statements: statements, + phase: phase, + } +} + +type txnDeps struct { + txn *kv.Txn + codec keys.SQLCodec + descsCollection *descs.Collection + jobRegistry *jobs.Registry +} + +var _ scexec.Catalog = (*txnDeps)(nil) + +// MustReadImmutableDescriptor implements the scmutationexec.CatalogReader interface. +func (d *txnDeps) MustReadImmutableDescriptor( + ctx context.Context, id descpb.ID, +) (catalog.Descriptor, error) { + flags := tree.CommonLookupFlags{ + Required: true, + RequireMutable: false, + AvoidCached: true, + IncludeOffline: true, + IncludeDropped: true, + } + return d.descsCollection.GetImmutableDescriptorByID(ctx, d.txn, id, flags) +} + +// AddSyntheticDescriptor implements the scmutationexec.CatalogReader interface. +func (d *txnDeps) AddSyntheticDescriptor(desc catalog.Descriptor) { + d.descsCollection.AddSyntheticDescriptor(desc) +} + +// RemoveSyntheticDescriptor implements the scmutationexec.CatalogReader interface. +func (d *txnDeps) RemoveSyntheticDescriptor(id descpb.ID) { + d.descsCollection.RemoveSyntheticDescriptor(id) +} + +// MustReadMutableDescriptor implements the scexec.Catalog interface. +func (d *txnDeps) MustReadMutableDescriptor( + ctx context.Context, id descpb.ID, +) (catalog.MutableDescriptor, error) { + return d.descsCollection.GetMutableDescriptorByID(ctx, id, d.txn) +} + +// NewCatalogChangeBatcher implements the scexec.Catalog interface. +func (d *txnDeps) NewCatalogChangeBatcher() scexec.CatalogChangeBatcher { + return &catalogChangeBatcher{ + txnDeps: d, + batch: d.txn.NewBatch(), + } +} + +type catalogChangeBatcher struct { + *txnDeps + batch *kv.Batch +} + +var _ scexec.CatalogChangeBatcher = (*catalogChangeBatcher)(nil) + +// CreateOrUpdateDescriptor implements the scexec.CatalogChangeBatcher interface. +func (b *catalogChangeBatcher) CreateOrUpdateDescriptor( + ctx context.Context, desc catalog.MutableDescriptor, +) error { + return b.descsCollection.WriteDescToBatch(ctx, false /* kvTrace */, desc, b.batch) +} + +// DeleteName implements the scexec.CatalogChangeBatcher interface. +func (b *catalogChangeBatcher) DeleteName( + ctx context.Context, nameInfo descpb.NameInfo, id descpb.ID, +) error { + b.batch.Del(catalogkeys.EncodeNameKey(b.codec, nameInfo)) + return nil +} + +// ValidateAndRun implements the scexec.CatalogChangeBatcher interface. +func (b *catalogChangeBatcher) ValidateAndRun(ctx context.Context) error { + if err := b.descsCollection.ValidateUncommittedDescriptors(ctx, b.txn); err != nil { + return err + } + if err := b.txn.Run(ctx, b.batch); err != nil { + return errors.Wrap(err, "writing descriptors") + } + return nil +} + +var _ scexec.TransactionalJobCreator = (*txnDeps)(nil) + +// CreateJob implements the scexec.TransactionalJobCreator interface. +func (d *txnDeps) CreateJob(ctx context.Context, record jobs.Record) (jobspb.JobID, error) { + if record.JobID == jobspb.InvalidJobID { + record.JobID = d.jobRegistry.MakeJobID() + } + job, err := d.jobRegistry.CreateJobWithTxn(ctx, record, record.JobID, d.txn) + if err != nil { + return jobspb.InvalidJobID, err + } + return job.ID(), nil +} + +var _ scexec.IndexSpanSplitter = (*txnDeps)(nil) + +// MaybeSplitIndexSpans implements the scexec.IndexSpanSplitter interface. +func (d *txnDeps) MaybeSplitIndexSpans( + ctx context.Context, table catalog.TableDescriptor, indexToBackfill catalog.Index, +) error { + // Only perform splits on the system tenant. + if !d.codec.ForSystemTenant() { + return nil + } + + span := table.IndexSpan(d.codec, indexToBackfill.GetID()) + const backfillSplitExpiration = time.Hour + expirationTime := d.txn.DB().Clock().Now().Add(backfillSplitExpiration.Nanoseconds(), 0) + return d.txn.DB().AdminSplit(ctx, span.Key, expirationTime) +} + +var _ scexec.JobProgressTracker = (*execDeps)(nil) + +// GetResumeSpans implements the scexec.JobProgressTracker interface. +func (d *txnDeps) GetResumeSpans( + ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, +) ([]roachpb.Span, error) { + table, err := d.descsCollection.GetImmutableTableByID(ctx, d.txn, tableID, tree.ObjectLookupFlags{ + CommonLookupFlags: tree.CommonLookupFlags{ + Required: true, + AvoidCached: true, + }, + }) + if err != nil { + return nil, err + } + return []roachpb.Span{table.IndexSpan(d.codec, indexID)}, nil +} + +// SetResumeSpans implements the scexec.JobProgressTracker interface. +func (d *txnDeps) SetResumeSpans( + ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, total, done []roachpb.Span, +) error { + panic("implement me") +} + +type execDeps struct { + txnDeps + indexBackfiller scexec.IndexBackfiller + testingKnobs *scexec.NewSchemaChangerTestingKnobs + statements []string + phase scop.Phase +} + +var _ scexec.Dependencies = (*execDeps)(nil) + +// Catalog implements the scexec.Dependencies interface. +func (d *execDeps) Catalog() scexec.Catalog { + return d +} + +// IndexBackfiller implements the scexec.Dependencies interface. +func (d *execDeps) IndexBackfiller() scexec.IndexBackfiller { + return d.indexBackfiller +} + +// IndexSpanSplitter implements the scexec.Dependencies interface. +func (d *execDeps) IndexSpanSplitter() scexec.IndexSpanSplitter { + return d +} + +// JobProgressTracker implements the scexec.Dependencies interface. +func (d *execDeps) JobProgressTracker() scexec.JobProgressTracker { + return d +} + +// TransactionalJobCreator implements the scexec.Dependencies interface. +func (d *execDeps) TransactionalJobCreator() scexec.TransactionalJobCreator { + return d +} + +// TestingKnobs implements the scexec.Dependencies interface. +func (d *execDeps) TestingKnobs() *scexec.NewSchemaChangerTestingKnobs { + return d.testingKnobs +} + +// Statements implements the scexec.Dependencies interface. +func (d *execDeps) Statements() []string { + return d.statements +} + +// Phase implements the scexec.Dependencies interface. +func (d *execDeps) Phase() scop.Phase { + return d.phase +} diff --git a/pkg/sql/schemachanger/scdeps/run_deps.go b/pkg/sql/schemachanger/scdeps/run_deps.go new file mode 100644 index 000000000000..ce1b4562f63a --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/run_deps.go @@ -0,0 +1,167 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scdeps + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scrun" + "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" +) + +// NewJobCreationDependencies returns an +// scexec.SchemaChangeJobCreationDependencies implementation built from the +// given arguments. +func NewJobCreationDependencies( + execDeps scexec.Dependencies, user security.SQLUsername, +) scrun.SchemaChangeJobCreationDependencies { + return &jobCreationDeps{ + execDeps: execDeps, + user: user, + } +} + +type jobCreationDeps struct { + execDeps scexec.Dependencies + user security.SQLUsername +} + +var _ scrun.SchemaChangeJobCreationDependencies = (*jobCreationDeps)(nil) + +// Catalog implements the scrun.SchemaChangeJobCreationDependencies interface. +func (d *jobCreationDeps) Catalog() scexec.Catalog { + return d.execDeps.Catalog() +} + +// TransactionalJobCreator implements the scrun.SchemaChangeJobCreationDependencies interface. +func (d *jobCreationDeps) TransactionalJobCreator() scexec.TransactionalJobCreator { + return d.execDeps.TransactionalJobCreator() +} + +// User implements the scrun.SchemaChangeJobCreationDependencies interface. +func (d *jobCreationDeps) User() security.SQLUsername { + return d.user +} + +// Statements implements the scrun.SchemaChangeJobCreationDependencies interface. +func (d *jobCreationDeps) Statements() []string { + return d.execDeps.Statements() +} + +// NewJobExecutionDependencies returns an +// scexec.SchemaChangeJobExecutionDependencies implementation built from the +// given arguments. +func NewJobExecutionDependencies( + collectionFactory *descs.CollectionFactory, + db *kv.DB, + internalExecutor sqlutil.InternalExecutor, + indexBackfiller scexec.IndexBackfiller, + jobRegistry *jobs.Registry, + job *jobs.Job, + codec keys.SQLCodec, + settings *cluster.Settings, + testingKnobs *scexec.NewSchemaChangerTestingKnobs, + statements []string, +) scrun.SchemaChangeJobExecutionDependencies { + return &jobExecutionDeps{ + collectionFactory: collectionFactory, + db: db, + internalExecutor: internalExecutor, + indexBackfiller: indexBackfiller, + jobRegistry: jobRegistry, + job: job, + codec: codec, + settings: settings, + testingKnobs: testingKnobs, + statements: statements, + } +} + +type jobExecutionDeps struct { + collectionFactory *descs.CollectionFactory + db *kv.DB + internalExecutor sqlutil.InternalExecutor + indexBackfiller scexec.IndexBackfiller + jobRegistry *jobs.Registry + job *jobs.Job + + codec keys.SQLCodec + settings *cluster.Settings + testingKnobs *scexec.NewSchemaChangerTestingKnobs + statements []string +} + +var _ scrun.SchemaChangeJobExecutionDependencies = (*jobExecutionDeps)(nil) + +// ClusterSettings implements the scrun.SchemaChangeJobExecutionDependencies interface. +func (d *jobExecutionDeps) ClusterSettings() *cluster.Settings { + return d.settings +} + +// WithTxnInJob implements the scrun.SchemaChangeJobExecutionDependencies interface. +func (d *jobExecutionDeps) WithTxnInJob( + ctx context.Context, + fn func(ctx context.Context, txndeps scrun.SchemaChangeJobTxnDependencies) error, +) error { + err := d.collectionFactory.Txn(ctx, d.internalExecutor, d.db, func( + ctx context.Context, txn *kv.Txn, descriptors *descs.Collection, + ) error { + return fn(ctx, &jobExecutionTxnDeps{ + jobExecutionDeps: *d, + txnDeps: txnDeps{ + txn: txn, + codec: d.codec, + descsCollection: descriptors, + jobRegistry: d.jobRegistry, + }, + }) + }) + if err != nil { + return err + } + d.jobRegistry.NotifyToAdoptJobs(ctx) + return nil +} + +type jobExecutionTxnDeps struct { + jobExecutionDeps + txnDeps +} + +var _ scrun.SchemaChangeJobTxnDependencies = (*jobExecutionTxnDeps)(nil) + +// UpdateSchemaChangeJob implements the scrun.SchemaChangeJobTxnDependencies interface. +func (d *jobExecutionTxnDeps) UpdateSchemaChangeJob( + ctx context.Context, fn func(md jobs.JobMetadata, ju scrun.JobProgressUpdater) error, +) error { + return d.job.Update(ctx, d.txn, func(txn *kv.Txn, md jobs.JobMetadata, ju *jobs.JobUpdater) error { + return fn(md, ju) + }) +} + +// ExecutorDependencies implements the scrun.SchemaChangeJobTxnDependencies interface. +func (d *jobExecutionTxnDeps) ExecutorDependencies() scexec.Dependencies { + return &execDeps{ + txnDeps: d.txnDeps, + indexBackfiller: d.indexBackfiller, + testingKnobs: d.testingKnobs, + statements: d.statements, + phase: scop.PostCommitPhase, + } +} diff --git a/pkg/sql/schemachanger/scdeps/sctestdeps/BUILD.bazel b/pkg/sql/schemachanger/scdeps/sctestdeps/BUILD.bazel new file mode 100644 index 000000000000..9c318d5795db --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/sctestdeps/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "sctestdeps", + srcs = [ + "test_deps.go", + "test_state.go", + ], + importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps/sctestdeps", + visibility = ["//visibility:public"], + deps = [ + "//pkg/jobs", + "//pkg/jobs/jobspb", + "//pkg/keys", + "//pkg/roachpb:with-mocks", + "//pkg/security", + "//pkg/settings/cluster", + "//pkg/sql/catalog", + "//pkg/sql/catalog/catalogkv", + "//pkg/sql/catalog/catconstants", + "//pkg/sql/catalog/descpb", + "//pkg/sql/catalog/nstree", + "//pkg/sql/catalog/schemadesc", + "//pkg/sql/catalog/typedesc", + "//pkg/sql/privilege", + "//pkg/sql/schemachanger/scbuild", + "//pkg/sql/schemachanger/scexec", + "//pkg/sql/schemachanger/scexec/scmutationexec", + "//pkg/sql/schemachanger/scop", + "//pkg/sql/schemachanger/scrun", + "//pkg/sql/sem/tree", + "//pkg/sql/sessiondata", + "//pkg/sql/sessiondatapb", + "//pkg/sql/types", + "//pkg/testutils/sqlutils", + "//pkg/util/hlc", + "//pkg/util/protoutil", + "@com_github_cockroachdb_errors//:errors", + "@com_github_lib_pq//oid", + ], +) diff --git a/pkg/sql/schemachanger/scdeps/sctestdeps/test_deps.go b/pkg/sql/schemachanger/scdeps/sctestdeps/test_deps.go new file mode 100644 index 000000000000..6bd5bb63cc7a --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/sctestdeps/test_deps.go @@ -0,0 +1,705 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package sctestdeps + +import ( + "context" + "sort" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/catconstants" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemadesc" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" + "github.com/cockroachdb/cockroach/pkg/sql/privilege" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/scmutationexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scrun" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/errors" + "github.com/lib/pq/oid" +) + +var _ scbuild.Dependencies = (*TestState)(nil) + +// AuthorizationAccessor implements the scbuild.Dependencies interface. +func (s *TestState) AuthorizationAccessor() scbuild.AuthorizationAccessor { + return s +} + +// CatalogReader implements the scbuild.Dependencies interface. +func (s *TestState) CatalogReader() scbuild.CatalogReader { + return s +} + +// Codec implements the scbuild.Dependencies interface. +func (s *TestState) Codec() keys.SQLCodec { + return keys.SystemSQLCodec +} + +// SessionData implements the scbuild.Dependencies interface. +func (s *TestState) SessionData() *sessiondata.SessionData { + return &s.sessionData +} + +// ClusterSettings implements the scbuild.Dependencies interface. +func (s *TestState) ClusterSettings() *cluster.Settings { + return cluster.MakeTestingClusterSettings() +} + +// Statements implements the scbuild.Dependencies interface. +func (s *TestState) Statements() []string { + return s.statements +} + +var _ scbuild.AuthorizationAccessor = (*TestState)(nil) + +// CheckPrivilege implements the scbuild.AuthorizationAccessor interface. +func (s *TestState) CheckPrivilege( + ctx context.Context, descriptor catalog.Descriptor, privilege privilege.Kind, +) error { + return nil +} + +// HasAdminRole implements the scbuild.AuthorizationAccessor interface. +func (s *TestState) HasAdminRole(ctx context.Context) (bool, error) { + return true, nil +} + +// HasOwnership implements the scbuild.AuthorizationAccessor interface. +func (s *TestState) HasOwnership(ctx context.Context, descriptor catalog.Descriptor) (bool, error) { + return true, nil +} + +var _ scbuild.CatalogReader = (*TestState)(nil) + +// MayResolveDatabase implements the scbuild.CatalogReader interface. +func (s *TestState) MayResolveDatabase( + ctx context.Context, name tree.Name, +) catalog.DatabaseDescriptor { + desc := s.mayGetByName(0, 0, name.String()) + if desc == nil { + return nil + } + db, err := catalog.AsDatabaseDescriptor(desc) + if err != nil { + panic(err) + } + if db.Dropped() || db.Offline() { + return nil + } + return db +} + +// MayResolveSchema implements the scbuild.CatalogReader interface. +func (s *TestState) MayResolveSchema( + ctx context.Context, name tree.ObjectNamePrefix, +) (catalog.DatabaseDescriptor, catalog.SchemaDescriptor) { + dbName := name.Catalog() + scName := name.Schema() + if !name.ExplicitCatalog && !name.ExplicitSchema { + return nil, nil + } + if !name.ExplicitCatalog || !name.ExplicitSchema { + dbName = s.CurrentDatabase() + if name.ExplicitCatalog { + scName = name.Catalog() + } else { + scName = name.Schema() + } + } + dbDesc := s.mayGetByName(0, 0, dbName) + if dbDesc == nil || dbDesc.Dropped() || dbDesc.Offline() { + if dbName == s.CurrentDatabase() { + panic(errors.AssertionFailedf("Invalid current database %q", s.CurrentDatabase())) + } + return nil, nil + } + db, err := catalog.AsDatabaseDescriptor(dbDesc) + if err != nil { + panic(err) + } + scDesc := s.mayGetByName(db.GetID(), 0, scName) + if scDesc == nil || scDesc.Dropped() || scDesc.Offline() { + return nil, nil + } + sc, err := catalog.AsSchemaDescriptor(scDesc) + if err != nil { + panic(err) + } + return db, sc +} + +// MayResolveTable implements the scbuild.CatalogReader interface. +func (s *TestState) MayResolveTable( + ctx context.Context, name tree.UnresolvedObjectName, +) (catalog.ResolvedObjectPrefix, catalog.TableDescriptor) { + prefix, desc, err := s.mayResolveObject(name) + if err != nil { + panic(err) + } + if desc == nil { + return prefix, nil + } + table, err := catalog.AsTableDescriptor(desc) + if err != nil { + panic(err) + } + return prefix, table +} + +// MayResolveType implements the scbuild.CatalogReader interface. +func (s *TestState) MayResolveType( + ctx context.Context, name tree.UnresolvedObjectName, +) (catalog.ResolvedObjectPrefix, catalog.TypeDescriptor) { + prefix, desc, err := s.mayResolveObject(name) + if err != nil { + panic(err) + } + if desc == nil { + return prefix, nil + } + typ, err := catalog.AsTypeDescriptor(desc) + if err != nil { + panic(err) + } + return prefix, typ +} + +func (s *TestState) mayResolveObject( + name tree.UnresolvedObjectName, +) (prefix catalog.ResolvedObjectPrefix, desc catalog.Descriptor, err error) { + tn := name.ToTableName() + { + db, sc := s.mayResolvePrefix(tn.ObjectNamePrefix) + if db == nil || sc == nil { + return catalog.ResolvedObjectPrefix{}, nil, nil + } + prefix.ExplicitDatabase = true + prefix.ExplicitSchema = true + prefix.Database, err = catalog.AsDatabaseDescriptor(db) + if err != nil { + return catalog.ResolvedObjectPrefix{}, nil, err + } + prefix.Schema, err = catalog.AsSchemaDescriptor(sc) + if err != nil { + return catalog.ResolvedObjectPrefix{}, nil, err + } + } + desc = s.mayGetByName(prefix.Database.GetID(), prefix.Schema.GetID(), name.Object()) + if desc == nil { + return prefix, nil, nil + } + if desc.Dropped() || desc.Offline() { + return prefix, nil, nil + } + return prefix, desc, nil +} + +func (s *TestState) mayResolvePrefix(name tree.ObjectNamePrefix) (db, sc catalog.Descriptor) { + if name.ExplicitCatalog && name.ExplicitSchema { + db = s.mayGetByName(0, 0, name.Catalog()) + if db == nil || db.Dropped() || db.Offline() { + return nil, nil + } + sc = s.mayGetByName(db.GetID(), 0, name.Schema()) + if sc == nil || sc.Dropped() || sc.Offline() { + return nil, nil + } + return db, sc + } + + db = s.mayGetByName(0, 0, s.CurrentDatabase()) + if db == nil || db.Dropped() || db.Offline() { + panic(errors.AssertionFailedf("Invalid current database %q", s.CurrentDatabase())) + } + + if !name.ExplicitCatalog && !name.ExplicitSchema { + sc = s.mayGetByName(db.GetID(), 0, catconstants.PublicSchemaName) + if sc == nil || sc.Dropped() || sc.Offline() { + return nil, nil + } + return db, sc + } + + var prefixName string + if name.ExplicitCatalog { + prefixName = name.Catalog() + } else { + prefixName = name.Schema() + } + + sc = s.mayGetByName(db.GetID(), 0, prefixName) + if sc != nil && !sc.Dropped() && !sc.Offline() { + return db, sc + } + + db = s.mayGetByName(0, 0, prefixName) + if db == nil || db.Dropped() || db.Offline() { + return nil, nil + } + sc = s.mayGetByName(db.GetID(), 0, catconstants.PublicSchemaName) + if sc == nil || sc.Dropped() || sc.Offline() { + return nil, nil + } + return db, sc +} + +func (s *TestState) mayGetByName( + parentID, parentSchemaID descpb.ID, name string, +) catalog.Descriptor { + key := descpb.NameInfo{ + ParentID: parentID, + ParentSchemaID: parentSchemaID, + Name: name, + } + id, found := s.namespace[key] + if !found { + return nil + } + if id == keys.PublicSchemaID { + return schemadesc.GetPublicSchema() + } + descriptorEntry := s.descriptors.GetByID(id) + if descriptorEntry == nil { + return nil + } + return descriptorEntry.(catalog.Descriptor) +} + +// ReadObjectNamesAndIDs implements the scbuild.CatalogReader interface. +func (s *TestState) ReadObjectNamesAndIDs( + ctx context.Context, db catalog.DatabaseDescriptor, schema catalog.SchemaDescriptor, +) (names tree.TableNames, ids descpb.IDs) { + m := make(map[string]descpb.ID) + for nameInfo, id := range s.namespace { + if nameInfo.ParentID == db.GetID() && nameInfo.GetParentSchemaID() == schema.GetID() { + m[nameInfo.Name] = id + names = append(names, tree.MakeTableNameWithSchema( + tree.Name(db.GetName()), + tree.Name(schema.GetName()), + tree.Name(nameInfo.Name), + )) + } + } + sort.Slice(names, func(i, j int) bool { + return names[i].Object() < names[j].Object() + }) + for _, name := range names { + ids = append(ids, m[name.Object()]) + } + return names, ids +} + +// ResolveType implements the scbuild.CatalogReader interface. +func (s *TestState) ResolveType( + ctx context.Context, name *tree.UnresolvedObjectName, +) (*types.T, error) { + prefix, obj, err := s.mayResolveObject(*name) + if err != nil { + return nil, err + } + if obj == nil { + return nil, errors.Wrapf(catalog.ErrDescriptorNotFound, "resolving type %q", name.String()) + } + typ, err := catalog.AsTypeDescriptor(obj) + if err != nil { + return nil, err + } + tn := tree.MakeQualifiedTypeName(prefix.Database.GetName(), prefix.Schema.GetName(), typ.GetName()) + return typ.MakeTypesT(ctx, &tn, s) +} + +// ResolveTypeByOID implements the scbuild.CatalogReader interface. +func (s *TestState) ResolveTypeByOID(ctx context.Context, oid oid.Oid) (*types.T, error) { + id, err := typedesc.UserDefinedTypeOIDToID(oid) + if err != nil { + return nil, err + } + name, typ, err := s.GetTypeDescriptor(ctx, id) + if err != nil { + return nil, err + } + return typ.MakeTypesT(ctx, &name, s) +} + +var _ catalog.TypeDescriptorResolver = (*TestState)(nil) + +// GetTypeDescriptor implements the scbuild.CatalogReader interface. +func (s *TestState) GetTypeDescriptor( + ctx context.Context, id descpb.ID, +) (tree.TypeName, catalog.TypeDescriptor, error) { + + desc, err := s.mustReadImmutableDescriptor(id) + if err != nil { + return tree.TypeName{}, nil, err + } + typ, err := catalog.AsTypeDescriptor(desc) + if err != nil { + return tree.TypeName{}, nil, err + } + tn, err := s.getQualifiedObjectNameByID(typ.GetID()) + if err != nil { + return tree.TypeName{}, nil, err + } + return tree.MakeTypeNameWithPrefix(tn.ObjectNamePrefix, tn.Object()), typ, nil +} + +// GetQualifiedTableNameByID implements the scbuild.CatalogReader interface. +func (s *TestState) GetQualifiedTableNameByID( + ctx context.Context, id int64, requiredType tree.RequiredTableKind, +) (*tree.TableName, error) { + return s.getQualifiedObjectNameByID(descpb.ID(id)) +} + +func (s *TestState) getQualifiedObjectNameByID(id descpb.ID) (*tree.TableName, error) { + obj, err := s.mustReadImmutableDescriptor(id) + if err != nil { + return nil, err + } + db := s.descriptors.GetByID(obj.GetParentID()) + if db == nil { + return nil, errors.Wrapf(catalog.ErrDescriptorNotFound, "parent database descriptor #%d", obj.GetParentID()) + } + sc := s.descriptors.GetByID(obj.GetParentSchemaID()) + if sc == nil { + return nil, errors.Wrapf(catalog.ErrDescriptorNotFound, "parent schema descriptor #%d", obj.GetParentSchemaID()) + } + return tree.NewTableNameWithSchema(tree.Name(db.GetName()), tree.Name(sc.GetName()), tree.Name(obj.GetName())), nil +} + +// CurrentDatabase implements the scbuild.CatalogReader interface. +func (s *TestState) CurrentDatabase() string { + return s.currentDatabase +} + +// MustReadDescriptor implements the scbuild.CatalogReader interface. +func (s *TestState) MustReadDescriptor(ctx context.Context, id descpb.ID) catalog.Descriptor { + desc, err := s.mustReadImmutableDescriptor(id) + if err != nil { + panic(err) + } + return desc +} + +func (s *TestState) mustReadMutableDescriptor(id descpb.ID) (catalog.MutableDescriptor, error) { + entry := s.descriptors.GetByID(id) + if entry == nil { + return nil, errors.Wrapf(catalog.ErrDescriptorNotFound, "reading mutable descriptor #%d", id) + } + return entry.(catalog.MutableDescriptor), nil +} + +func (s *TestState) mustReadImmutableDescriptor(id descpb.ID) (catalog.Descriptor, error) { + syntheticEntry := s.syntheticDescriptors.GetByID(id) + if syntheticEntry != nil { + return syntheticEntry.(catalog.Descriptor), nil + } + entry := s.descriptors.GetByID(id) + if entry != nil { + return entry.(catalog.Descriptor), nil + } + return nil, errors.Wrapf(catalog.ErrDescriptorNotFound, "reading immutable descriptor #%d", id) +} + +var _ scexec.Dependencies = (*TestState)(nil) + +// Catalog implements the scexec.Dependencies interface. +func (s *TestState) Catalog() scexec.Catalog { + return s +} + +var _ scmutationexec.CatalogReader = (*TestState)(nil) + +// MustReadImmutableDescriptor implements the scmutationexec.CatalogReader interface. +func (s *TestState) MustReadImmutableDescriptor( + ctx context.Context, id descpb.ID, +) (catalog.Descriptor, error) { + return s.mustReadMutableDescriptor(id) +} + +// AddSyntheticDescriptor implements the scmutationexec.CatalogReader interface. +func (s *TestState) AddSyntheticDescriptor(desc catalog.Descriptor) { + s.syntheticDescriptors.Upsert(desc) +} + +// RemoveSyntheticDescriptor implements the scmutationexec.CatalogReader interface. +func (s *TestState) RemoveSyntheticDescriptor(id descpb.ID) { + s.syntheticDescriptors.Remove(id) +} + +var _ scexec.Catalog = (*TestState)(nil) + +// MustReadMutableDescriptor implements the scexec.Catalog interface. +func (s *TestState) MustReadMutableDescriptor( + ctx context.Context, id descpb.ID, +) (catalog.MutableDescriptor, error) { + return s.mustReadMutableDescriptor(id) +} + +// NewCatalogChangeBatcher implements the scexec.Catalog interface. +func (s *TestState) NewCatalogChangeBatcher() scexec.CatalogChangeBatcher { + return &testCatalogChangeBatcher{ + s: s, + namesToDelete: make(map[descpb.NameInfo]descpb.ID), + } +} + +type testCatalogChangeBatcher struct { + s *TestState + descs []catalog.Descriptor + namesToDelete map[descpb.NameInfo]descpb.ID +} + +var _ scexec.CatalogChangeBatcher = (*testCatalogChangeBatcher)(nil) + +// CreateOrUpdateDescriptor implements the scexec.CatalogChangeBatcher interface. +func (b *testCatalogChangeBatcher) CreateOrUpdateDescriptor( + ctx context.Context, desc catalog.MutableDescriptor, +) error { + b.descs = append(b.descs, desc) + return nil +} + +// DeleteName implements the scexec.CatalogChangeBatcher interface. +func (b *testCatalogChangeBatcher) DeleteName( + ctx context.Context, nameInfo descpb.NameInfo, id descpb.ID, +) error { + b.namesToDelete[nameInfo] = id + return nil +} + +// ValidateAndRun implements the scexec.CatalogChangeBatcher interface. +func (b *testCatalogChangeBatcher) ValidateAndRun(ctx context.Context) error { + for nameInfo, id := range b.namesToDelete { + actualID, hasEntry := b.s.namespace[nameInfo] + if !hasEntry { + return errors.AssertionFailedf( + "cannot delete missing namespace entry %v", nameInfo) + } + if actualID != id { + return errors.AssertionFailedf( + "expected deleted namespace entry %v to have ID %d, instead is %d", nameInfo, id, actualID) + } + delete(b.s.namespace, nameInfo) + } + for _, desc := range b.descs { + b.s.descriptors.Upsert(desc) + } + return catalog.Validate(ctx, b.s, catalog.NoValidationTelemetry, catalog.ValidationLevelAllPreTxnCommit, b.descs...).CombinedError() +} + +var _ catalog.DescGetter = (*TestState)(nil) + +// GetDesc implements the catalog.DescGetter interface. +func (s *TestState) GetDesc(ctx context.Context, id descpb.ID) (catalog.Descriptor, error) { + // Read mutable descriptor to bypass synthetic descriptors. + return s.mustReadMutableDescriptor(id) +} + +// GetNamespaceEntry implements the catalog.DescGetter interface. +func (s *TestState) GetNamespaceEntry( + ctx context.Context, parentID, parentSchemaID descpb.ID, name string, +) (id descpb.ID, _ error) { + id = s.namespace[descpb.NameInfo{ + ParentID: parentID, + ParentSchemaID: parentSchemaID, + Name: name, + }] + return id, nil +} + +// IndexBackfiller implements the scexec.Dependencies interface. +func (s *TestState) IndexBackfiller() scexec.IndexBackfiller { + return s +} + +var _ scexec.IndexBackfiller = (*TestState)(nil) + +// BackfillIndex implements the scexec.IndexBackfiller interface. +func (s *TestState) BackfillIndex( + _ context.Context, + _ scexec.JobProgressTracker, + _ catalog.TableDescriptor, + _ descpb.IndexID, + _ ...descpb.IndexID, +) error { + return nil +} + +// IndexSpanSplitter implements the scexec.Dependencies interface. +func (s *TestState) IndexSpanSplitter() scexec.IndexSpanSplitter { + return s +} + +var _ scexec.IndexSpanSplitter = (*TestState)(nil) + +// MaybeSplitIndexSpans implements the scexec.IndexSpanSplitter interface. +func (s *TestState) MaybeSplitIndexSpans( + _ context.Context, _ catalog.TableDescriptor, _ catalog.Index, +) error { + return nil +} + +// JobProgressTracker implements the scexec.Dependencies interface. +func (s *TestState) JobProgressTracker() scexec.JobProgressTracker { + return s +} + +var _ scexec.JobProgressTracker = (*TestState)(nil) + +// GetResumeSpans implements the scexec.JobProgressTracker interface. +func (s *TestState) GetResumeSpans( + ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, +) ([]roachpb.Span, error) { + desc, err := s.mustReadImmutableDescriptor(tableID) + if err != nil { + return nil, err + } + table, err := catalog.AsTableDescriptor(desc) + if err != nil { + return nil, err + } + return []roachpb.Span{table.IndexSpan(s.Codec(), indexID)}, nil +} + +// SetResumeSpans implements the scexec.JobProgressTracker interface. +func (s *TestState) SetResumeSpans( + ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, total, done []roachpb.Span, +) error { + panic("implement me") +} + +// TransactionalJobCreator implements the scexec.Dependencies interface. +func (s *TestState) TransactionalJobCreator() scexec.TransactionalJobCreator { + return s +} + +var _ scexec.TransactionalJobCreator = (*TestState)(nil) + +// CreateJob implements the scexec.TransactionalJobCreator interface. +func (s *TestState) CreateJob(ctx context.Context, record jobs.Record) (jobspb.JobID, error) { + record.JobID = jobspb.JobID(1 + len(s.jobs)) + s.jobs = append(s.jobs, record) + return record.JobID, nil +} + +// TestingKnobs implements the scexec.Dependencies interface. +func (s *TestState) TestingKnobs() *scexec.NewSchemaChangerTestingKnobs { + return s.testingKnobs +} + +// Phase implements the scexec.Dependencies interface. +func (s *TestState) Phase() scop.Phase { + return s.phase +} + +var _ scrun.SchemaChangeJobCreationDependencies = (*TestState)(nil) + +// User implements the scrun.SchemaChangeJobCreationDependencies interface. +func (s *TestState) User() security.SQLUsername { + return security.RootUserName() +} + +var _ scrun.SchemaChangeJobExecutionDependencies = (*TestState)(nil) + +// WithTxnInJob implements the scrun.SchemaChangeJobExecutionDependencies interface. +func (s *TestState) WithTxnInJob( + ctx context.Context, + fn func(ctx context.Context, txndeps scrun.SchemaChangeJobTxnDependencies) error, +) (err error) { + s.WithTxn(func(s *TestState) { + err = fn(ctx, s) + }) + return err +} + +var _ scrun.SchemaChangeJobTxnDependencies = (*TestState)(nil) + +// UpdateSchemaChangeJob implements the scrun.SchemaChangeJobTxnDependencies interface. +func (s *TestState) UpdateSchemaChangeJob( + ctx context.Context, fn func(md jobs.JobMetadata, ju scrun.JobProgressUpdater) error, +) error { + var scjob *jobs.Record + for i, job := range s.jobs { + if job.Username == s.User() { + scjob = &s.jobs[i] + break + } + } + if scjob == nil { + return errors.AssertionFailedf("schema change job not found") + } + progress := jobspb.Progress{ + Progress: nil, + ModifiedMicros: 0, + RunningStatus: "", + Details: jobspb.WrapProgressDetails(scjob.Progress), + TraceID: 0, + } + payload := jobspb.Payload{ + Description: scjob.Description, + Statement: scjob.Statements, + UsernameProto: scjob.Username.EncodeProto(), + StartedMicros: 0, + FinishedMicros: 0, + DescriptorIDs: scjob.DescriptorIDs, + Error: "", + ResumeErrors: nil, + CleanupErrors: nil, + FinalResumeError: nil, + Noncancelable: false, + Details: jobspb.WrapPayloadDetails(scjob.Details), + PauseReason: "", + RetriableExecutionFailureLog: nil, + } + ju := testJobUpdater{ + md: jobs.JobMetadata{ + ID: scjob.JobID, + Status: jobs.StatusRunning, + Payload: &payload, + Progress: &progress, + RunStats: nil, + }, + } + err := fn(ju.md, &ju) + if err != nil { + return err + } + scjob.Progress = ju.md.Progress.GetNewSchemaChange() + return nil +} + +type testJobUpdater struct { + md jobs.JobMetadata +} + +var _ scrun.JobProgressUpdater = (*testJobUpdater)(nil) + +// UpdateProgress implements the JobProgressUpdater interface +func (ju *testJobUpdater) UpdateProgress(progress *jobspb.Progress) { + ju.md.Progress = progress +} + +// ExecutorDependencies implements the scrun.SchemaChangeJobTxnDependencies interface. +func (s *TestState) ExecutorDependencies() scexec.Dependencies { + return s +} diff --git a/pkg/sql/schemachanger/scdeps/sctestdeps/test_state.go b/pkg/sql/schemachanger/scdeps/sctestdeps/test_state.go new file mode 100644 index 000000000000..52ff86d94f30 --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/sctestdeps/test_state.go @@ -0,0 +1,176 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package sctestdeps + +import ( + "context" + "encoding/hex" + "strconv" + "testing" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/nstree" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/protoutil" +) + +// TestState is a backing struct used to implement all schema changer +// dependencies, like scbuild.Dependencies or scexec.Dependencies, for the +// purpose of facilitating end-to-end testing of the declarative schema changer. +type TestState struct { + descriptors, syntheticDescriptors nstree.Map + namespace map[descpb.NameInfo]descpb.ID + currentDatabase string + phase scop.Phase + sessionData sessiondata.SessionData + statements []string + testingKnobs *scexec.NewSchemaChangerTestingKnobs + jobs []jobs.Record +} + +// NewTestDependencies returns a TestState populated with the catalog state of +// the given test database handle. +func NewTestDependencies( + ctx context.Context, + t *testing.T, + tdb *sqlutils.SQLRunner, + testingKnobs *scexec.NewSchemaChangerTestingKnobs, + statements []string, +) *TestState { + + s := &TestState{ + currentDatabase: "defaultdb", + namespace: make(map[descpb.NameInfo]descpb.ID), + phase: scop.StatementPhase, + statements: statements, + testingKnobs: testingKnobs, + } + // Wait for schema changes to complete. + tdb.CheckQueryResultsRetry(t, ` +SELECT count(*) +FROM [SHOW JOBS] +WHERE job_type = 'SCHEMA CHANGE' + AND status NOT IN ('succeeded', 'failed', 'aborted')`, + [][]string{{"0"}}) + + // Fetch descriptor state. + { + hexDescRows := tdb.QueryStr(t, ` +SELECT encode(descriptor, 'hex'), crdb_internal_mvcc_timestamp +FROM system.descriptor +ORDER BY id`) + for _, hexDescRow := range hexDescRows { + descBytes, err := hex.DecodeString(hexDescRow[0]) + if err != nil { + t.Fatal(err) + } + ts, err := hlc.ParseTimestamp(hexDescRow[1]) + if err != nil { + t.Fatal(err) + } + descProto := &descpb.Descriptor{} + err = protoutil.Unmarshal(descBytes, descProto) + if err != nil { + t.Fatal(err) + } + b := catalogkv.NewBuilderWithMVCCTimestamp(descProto, ts) + err = b.RunPostDeserializationChanges(ctx, nil) + if err != nil { + t.Fatal(err) + } + desc := b.BuildCreatedMutable() + if desc.GetID() == keys.SystemDatabaseID || desc.GetParentID() == keys.SystemDatabaseID { + continue + } + s.descriptors.Upsert(desc) + } + } + + // Fetch namespace state. + { + nsRows := tdb.QueryStr(t, ` +SELECT "parentID", "parentSchemaID", name, id +FROM system.namespace +ORDER BY id`) + for _, nsRow := range nsRows { + parentID, err := strconv.Atoi(nsRow[0]) + if err != nil { + t.Fatal(err) + } + parentSchemaID, err := strconv.Atoi(nsRow[1]) + if err != nil { + t.Fatal(err) + } + name := nsRow[2] + id, err := strconv.Atoi(nsRow[3]) + if err != nil { + t.Fatal(err) + } + if id == keys.SystemDatabaseID || parentID == keys.SystemDatabaseID { + // Exclude system database and its objects from namespace state. + continue + } + key := descpb.NameInfo{ + ParentID: descpb.ID(parentID), + ParentSchemaID: descpb.ID(parentSchemaID), + Name: name, + } + s.namespace[key] = descpb.ID(id) + } + } + + // Fetch current database state. + { + currdb := tdb.QueryStr(t, `SELECT current_database()`) + if len(currdb) == 0 { + t.Fatal("Empty current database query results.") + } + s.currentDatabase = currdb[0][0] + } + + // Fetch session data. + { + hexSessionData := tdb.QueryStr(t, `SELECT encode(crdb_internal.serialize_session(), 'hex')`) + if len(hexSessionData) == 0 { + t.Fatal("Empty session data query results.") + } + sessionDataBytes, err := hex.DecodeString(hexSessionData[0][0]) + if err != nil { + t.Fatal(err) + } + sessionDataProto := sessiondatapb.SessionData{} + err = protoutil.Unmarshal(sessionDataBytes, &sessionDataProto) + if err != nil { + t.Fatal(err) + } + sessionData, err := sessiondata.UnmarshalNonLocal(sessionDataProto) + if err != nil { + t.Fatal(err) + } + s.sessionData = *sessionData + } + + return s +} + +// WithTxn simulates the execution of a transaction. +func (s *TestState) WithTxn(fn func(s *TestState)) { + defer s.syntheticDescriptors.Clear() + fn(s) +} diff --git a/pkg/sql/schemachanger/scdeps/sctestutils/BUILD.bazel b/pkg/sql/schemachanger/scdeps/sctestutils/BUILD.bazel new file mode 100644 index 000000000000..320d158bae9e --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/sctestutils/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "sctestutils", + srcs = ["sctestutils.go"], + importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps/sctestutils", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/security", + "//pkg/sql", + "//pkg/sql/catalog/descs", + "//pkg/sql/catalog/resolver", + "//pkg/sql/schemachanger/scbuild", + "//pkg/sql/schemachanger/scdeps", + "//pkg/sql/sessiondata", + "//pkg/sql/sessiondatapb", + "//pkg/testutils/serverutils", + ], +) diff --git a/pkg/sql/schemachanger/scdeps/sctestutils/sctestutils.go b/pkg/sql/schemachanger/scdeps/sctestutils/sctestutils.go new file mode 100644 index 000000000000..776da7e55b3e --- /dev/null +++ b/pkg/sql/schemachanger/scdeps/sctestutils/sctestutils.go @@ -0,0 +1,63 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package sctestutils + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/sql" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" +) + +// WithBuilderDependenciesFromTestServer sets up and tears down an +// scbuild.Dependencies object built using the test server interface and which +// it passes to the callback. +func WithBuilderDependenciesFromTestServer( + s serverutils.TestServerInterface, fn func(scbuild.Dependencies), +) { + execCfg := s.ExecutorConfig().(sql.ExecutorConfig) + ip, cleanup := sql.NewInternalPlanner( + "test", + kv.NewTxn(context.Background(), s.DB(), s.NodeID()), + security.RootUserName(), + &sql.MemoryMetrics{}, + &execCfg, + // Setting the database on the session data to "defaultdb" in the obvious + // way doesn't seem to do what we want. + sessiondatapb.SessionData{}, + ) + defer cleanup() + planner := ip.(interface { + Txn() *kv.Txn + Descriptors() *descs.Collection + SessionData() *sessiondata.SessionData + resolver.SchemaResolver + scbuild.AuthorizationAccessor + }) + fn(scdeps.NewBuilderDependencies( + execCfg.Codec, + planner.Txn(), + planner.Descriptors(), + planner, + planner, + planner.SessionData(), + execCfg.Settings, + nil, /* statements */ + )) +} diff --git a/pkg/sql/schemachanger/scexec/BUILD.bazel b/pkg/sql/schemachanger/scexec/BUILD.bazel index 1c88bb9914cf..ee962281c78c 100644 --- a/pkg/sql/schemachanger/scexec/BUILD.bazel +++ b/pkg/sql/schemachanger/scexec/BUILD.bazel @@ -3,32 +3,25 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "scexec", srcs = [ + "dependencies.go", + "exec_backfill.go", + "exec_mutation.go", + "exec_validation.go", "executor.go", - "mutation_desc_getter.go", - "mutation_jobs.go", ], importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec", visibility = ["//visibility:public"], deps = [ "//pkg/jobs", "//pkg/jobs/jobspb", - "//pkg/keys", - "//pkg/kv", "//pkg/roachpb:with-mocks", "//pkg/security", "//pkg/sql/catalog", - "//pkg/sql/catalog/catalogkeys", - "//pkg/sql/catalog/catalogkv", "//pkg/sql/catalog/descpb", - "//pkg/sql/catalog/descs", "//pkg/sql/catalog/tabledesc", - "//pkg/sql/catalog/typedesc", "//pkg/sql/schemachanger/scexec/descriptorutils", "//pkg/sql/schemachanger/scexec/scmutationexec", "//pkg/sql/schemachanger/scop", - "//pkg/sql/sem/tree", - "//pkg/sql/sessiondata", - "//pkg/sql/sqlutil", "//pkg/util/log", "//pkg/util/timeutil", "@com_github_cockroachdb_errors//:errors", @@ -55,15 +48,15 @@ go_test( "//pkg/sql/catalog/descpb", "//pkg/sql/catalog/descs", "//pkg/sql/catalog/lease", - "//pkg/sql/catalog/resolver", "//pkg/sql/catalog/tabledesc", "//pkg/sql/parser", "//pkg/sql/schemachanger/scbuild", + "//pkg/sql/schemachanger/scdeps", + "//pkg/sql/schemachanger/scdeps/sctestutils", "//pkg/sql/schemachanger/scop", "//pkg/sql/schemachanger/scpb", "//pkg/sql/schemachanger/scplan", "//pkg/sql/sem/tree", - "//pkg/sql/sessiondatapb", "//pkg/sql/sqlutil", "//pkg/sql/types", "//pkg/testutils/serverutils", diff --git a/pkg/sql/schemachanger/scexec/dependencies.go b/pkg/sql/schemachanger/scexec/dependencies.go new file mode 100644 index 000000000000..6e4e3de1af48 --- /dev/null +++ b/pkg/sql/schemachanger/scexec/dependencies.go @@ -0,0 +1,148 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scexec + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/scmutationexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" +) + +// Dependencies contains all the dependencies required by the executor. +type Dependencies interface { + Catalog() Catalog + TransactionalJobCreator() TransactionalJobCreator + IndexBackfiller() IndexBackfiller + IndexSpanSplitter() IndexSpanSplitter + JobProgressTracker() JobProgressTracker + + // TestingKnobs returns the testing knobs for the new schema changer. + TestingKnobs() *NewSchemaChangerTestingKnobs + + // Statements returns the statements behind this schema change. + Statements() []string + + // Phase returns the phase in which operations are to be executed. + Phase() scop.Phase +} + +// Catalog encapsulates the catalog-related dependencies for the executor. +// This involves reading descriptors, as well as preparing batches of catalog +// changes. +type Catalog interface { + scmutationexec.CatalogReader + + // MustReadMutableDescriptor the mutable equivalent to + // MustReadImmutableDescriptor in scmutationexec.CatalogReader. + // This method should be used carefully. + MustReadMutableDescriptor(ctx context.Context, id descpb.ID) (catalog.MutableDescriptor, error) + + // NewCatalogChangeBatcher is equivalent to creating a new kv.Batch for the + // current kv.Txn. + NewCatalogChangeBatcher() CatalogChangeBatcher +} + +// CatalogChangeBatcher encapsulates batched updates to the catalog: descriptor +// updates, namespace operations, etc. +type CatalogChangeBatcher interface { + + // CreateOrUpdateDescriptor upserts a descriptor. + CreateOrUpdateDescriptor(ctx context.Context, desc catalog.MutableDescriptor) error + + // DeleteName deletes a namespace entry. + DeleteName(ctx context.Context, nameInfo descpb.NameInfo, id descpb.ID) error + + // ValidateAndRun executes the updates after validating them using + // catalog.Validate. + ValidateAndRun(ctx context.Context) error +} + +// TransactionalJobCreator creates a job in the current transaction. +type TransactionalJobCreator interface { + + // CreateJob creates a job in the current transaction and returns the + // id which was assigned to that job, or an error otherwise. + CreateJob(ctx context.Context, record jobs.Record) (jobspb.JobID, error) +} + +// IndexBackfiller is an abstract index backfiller that performs index backfills +// when provided with a specification of tables and indexes and a way to track +// job progress. +type IndexBackfiller interface { + BackfillIndex( + ctx context.Context, + _ JobProgressTracker, + _ catalog.TableDescriptor, + source descpb.IndexID, + destinations ...descpb.IndexID, + ) error +} + +// IndexSpanSplitter can try to split an index span in the current transaction +// prior to backfilling. +type IndexSpanSplitter interface { + + // MaybeSplitIndexSpans will attempt to split the backfilled index span. + MaybeSplitIndexSpans(ctx context.Context, table catalog.TableDescriptor, indexToBackfill catalog.Index) error +} + +// JobProgressTracker abstracts the infrastructure to read and write backfill +// progress to job state. +type JobProgressTracker interface { + + // This interface is implicitly implying that there is only one stage of + // index backfills for a given table in a schema change. It implies that + // because it assumes that it's safe and reasonable to just store one set of + // resume spans per table on the job. + // + // Potentially something close to interface could still work if there were + // multiple stages of backfills for a table if we tracked which stage this + // were somehow. Maybe we could do something like increment a stage counter + // per table after finishing the backfills. + // + // It definitely is possible that there are multiple index backfills on a + // table in the context of a single schema change that changes the set of + // columns (primary index) and adds secondary indexes. + // + // Really this complexity arises in the computation of the fraction completed. + // We'll want to know whether there are more index backfills to come. + // + // One idea is to index secondarily on the source index. + + GetResumeSpans(ctx context.Context, tableID descpb.ID, indexID descpb.IndexID) ([]roachpb.Span, error) + SetResumeSpans(ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, total, done []roachpb.Span) error +} + +// NewSchemaChangerTestingKnobs are testing knobs for the executor. +type NewSchemaChangerTestingKnobs struct { + // BeforeStage is called before ops passed to the executor are executed. + // Errors returned are injected into the executor. + BeforeStage func(ops scop.Ops, m TestingKnobMetadata) error + // BeforeWaitingForConcurrentSchemaChanges is called at the start of waiting + // for concurrent schema changes to finish. + BeforeWaitingForConcurrentSchemaChanges func(stmts []string) +} + +// ModuleTestingKnobs is part of the base.ModuleTestingKnobs interface. +func (*NewSchemaChangerTestingKnobs) ModuleTestingKnobs() {} + +// TestingKnobMetadata holds additional information about the execution of the +// schema change that is used by the testing knobs. +type TestingKnobMetadata struct { + Statements []string + Phase scop.Phase +} diff --git a/pkg/sql/schemachanger/scexec/exec_backfill.go b/pkg/sql/schemachanger/scexec/exec_backfill.go new file mode 100644 index 000000000000..4c29561c2ae4 --- /dev/null +++ b/pkg/sql/schemachanger/scexec/exec_backfill.go @@ -0,0 +1,66 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scexec + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/descriptorutils" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" +) + +func executeBackfillOps(ctx context.Context, deps Dependencies, execute []scop.Op) error { + // TODO(ajwerner): Run backfills in parallel. Will require some plumbing for + // checkpointing at the very least. + + for _, op := range execute { + var err error + switch op := op.(type) { + case *scop.BackfillIndex: + err = executeIndexBackfillOp(ctx, deps, op) + default: + panic("unimplemented") + } + if err != nil { + return err + } + } + return nil +} + +func executeIndexBackfillOp(ctx context.Context, deps Dependencies, op *scop.BackfillIndex) error { + // Note that the leasing here is subtle. We'll avoid the cache and ensure that + // the descriptor is read from the store. That means it will not be leased. + // This relies on changed to the descriptor not messing with this index + // backfill. + desc, err := deps.Catalog().MustReadImmutableDescriptor(ctx, op.TableID) + if err != nil { + return err + } + table, ok := desc.(catalog.TableDescriptor) + if !ok { + return catalog.WrapTableDescRefErr(desc.GetID(), catalog.NewDescriptorTypeError(desc)) + } + mut, err := descriptorutils.FindMutation(table, descriptorutils.MakeIndexIDMutationSelector(op.IndexID)) + if err != nil { + return err + } + + // Must be the right index given the above call. + idxToBackfill := mut.AsIndex() + + // Split off the index span prior to backfilling. + if err := deps.IndexSpanSplitter().MaybeSplitIndexSpans(ctx, table, idxToBackfill); err != nil { + return err + } + return deps.IndexBackfiller().BackfillIndex(ctx, deps.JobProgressTracker(), table, table.GetPrimaryIndexID(), idxToBackfill.GetID()) +} diff --git a/pkg/sql/schemachanger/scexec/exec_mutation.go b/pkg/sql/schemachanger/scexec/exec_mutation.go new file mode 100644 index 000000000000..0bdc0fdd2152 --- /dev/null +++ b/pkg/sql/schemachanger/scexec/exec_mutation.go @@ -0,0 +1,140 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scexec + +import ( + "context" + "fmt" + "strings" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/scmutationexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" +) + +func executeDescriptorMutationOps(ctx context.Context, deps Dependencies, ops []scop.Op) error { + mvs := newMutationVisitorState(deps.Catalog()) + v := scmutationexec.NewMutationVisitor(deps.Catalog(), mvs) + for _, op := range ops { + if err := op.(scop.MutationOp).Visit(ctx, v); err != nil { + return err + } + } + b := deps.Catalog().NewCatalogChangeBatcher() + for _, id := range mvs.checkedOutDescriptors.Ordered() { + desc, err := mvs.c.MustReadMutableDescriptor(ctx, id) + if err != nil { + return errors.NewAssertionErrorWithWrappedErrf(err, "failed to retrieve modified descriptor") + } + if err := b.CreateOrUpdateDescriptor(ctx, desc); err != nil { + return err + } + } + for id, drainedNames := range mvs.drainedNames { + for _, name := range drainedNames { + if err := b.DeleteName(ctx, name, id); err != nil { + return err + } + } + } + if len(mvs.descriptorGCJobs) > 0 { + job := jobspb.SchemaChangeGCDetails{ + Tables: mvs.descriptorGCJobs, + } + descriptorList := strings.Builder{} + descriptorList.WriteString("Dropping descriptors ") + for _, table := range mvs.descriptorGCJobs { + descriptorList.WriteString(fmt.Sprintf("%d ", table.ID)) + } + record := createGCJobRecord(descriptorList.String(), security.NodeUserName(), job) + if _, err := deps.TransactionalJobCreator().CreateJob(ctx, record); err != nil { + return err + } + } + return b.ValidateAndRun(ctx) +} + +type mutationVisitorState struct { + c Catalog + checkedOutDescriptors catalog.DescriptorIDSet + drainedNames map[descpb.ID][]descpb.NameInfo + descriptorGCJobs []jobspb.SchemaChangeGCDetails_DroppedID +} + +func newMutationVisitorState(c Catalog) *mutationVisitorState { + return &mutationVisitorState{ + c: c, + drainedNames: make(map[descpb.ID][]descpb.NameInfo), + } +} + +var _ scmutationexec.MutationVisitorStateUpdater = (*mutationVisitorState)(nil) + +func (mvs *mutationVisitorState) CheckOutDescriptor( + ctx context.Context, id descpb.ID, +) (catalog.MutableDescriptor, error) { + desc, err := mvs.c.MustReadMutableDescriptor(ctx, id) + if err != nil { + return nil, err + } + mut := desc.(catalog.MutableDescriptor) + mut.MaybeIncrementVersion() + mvs.checkedOutDescriptors.Add(id) + return mut, nil +} + +func (mvs *mutationVisitorState) AddDrainedName(id descpb.ID, nameInfo descpb.NameInfo) { + if _, ok := mvs.drainedNames[id]; !ok { + mvs.drainedNames[id] = []descpb.NameInfo{nameInfo} + } else { + mvs.drainedNames[id] = append(mvs.drainedNames[id], nameInfo) + } +} + +func (mvs *mutationVisitorState) AddNewGCJobForDescriptor(descriptor catalog.Descriptor) { + mvs.descriptorGCJobs = append(mvs.descriptorGCJobs, + jobspb.SchemaChangeGCDetails_DroppedID{ + ID: descriptor.GetID(), + DropTime: timeutil.Now().UnixNano(), + }) +} + +// createGCJobRecord creates the job record for a GC job, setting some +// properties which are common for all GC jobs. +func createGCJobRecord( + originalDescription string, username security.SQLUsername, details jobspb.SchemaChangeGCDetails, +) jobs.Record { + descriptorIDs := make([]descpb.ID, 0) + if len(details.Indexes) > 0 { + if len(descriptorIDs) == 0 { + descriptorIDs = []descpb.ID{details.ParentID} + } + } else { + for _, table := range details.Tables { + descriptorIDs = append(descriptorIDs, table.ID) + } + } + return jobs.Record{ + Description: fmt.Sprintf("GC for %s", originalDescription), + Username: username, + DescriptorIDs: descriptorIDs, + Details: details, + Progress: jobspb.SchemaChangeGCProgress{}, + RunningStatus: "waiting for GC TTL", + NonCancelable: true, + } +} diff --git a/pkg/sql/schemachanger/scexec/exec_validation.go b/pkg/sql/schemachanger/scexec/exec_validation.go new file mode 100644 index 000000000000..54f235b96f47 --- /dev/null +++ b/pkg/sql/schemachanger/scexec/exec_validation.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scexec + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/util/log" +) + +func executeValidationOps(ctx context.Context, deps Dependencies, execute []scop.Op) error { + log.Errorf(ctx, "not implemented") + return nil +} diff --git a/pkg/sql/schemachanger/scexec/executor.go b/pkg/sql/schemachanger/scexec/executor.go index 8fa1d02c3684..b093d36ab3ea 100644 --- a/pkg/sql/schemachanger/scexec/executor.go +++ b/pkg/sql/schemachanger/scexec/executor.go @@ -12,295 +12,72 @@ package scexec import ( "context" - "time" - "github.com/cockroachdb/cockroach/pkg/jobs" "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" - "github.com/cockroachdb/cockroach/pkg/keys" - "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/descriptorutils" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/scmutationexec" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" - "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" ) -// An Executor executes ops generated during planning. It mostly holds -// dependencies for execution and has little additional logic of its own. -type Executor struct { - txn *kv.Txn - descsCollection *descs.Collection - codec keys.SQLCodec - indexBackfiller IndexBackfiller - jobTracker JobProgressTracker - testingKnobs *NewSchemaChangerTestingKnobs - jobRegistry *jobs.Registry - executor sqlutil.InternalExecutor -} - -// NewExecutor creates a new Executor. -func NewExecutor( - txn *kv.Txn, - descsCollection *descs.Collection, - codec keys.SQLCodec, - backfiller IndexBackfiller, - tracker JobProgressTracker, - testingKnobs *NewSchemaChangerTestingKnobs, - jobRegistry *jobs.Registry, - executor sqlutil.InternalExecutor, -) *Executor { - return &Executor{ - txn: txn, - descsCollection: descsCollection, - codec: codec, - indexBackfiller: backfiller, - jobTracker: tracker, - testingKnobs: testingKnobs, - jobRegistry: jobRegistry, - executor: executor, - } -} - -// NewSchemaChangerTestingKnobs are testing knobs for the executor. -type NewSchemaChangerTestingKnobs struct { - // BeforeStage is called before ops passed to the executor are executed. - // Errors returned are injected into the executor. - BeforeStage func(ops scop.Ops, m TestingKnobMetadata) error - // BeforeWaitingForConcurrentSchemaChanges is called at the start of waiting - // for concurrent schema changes to finish. - BeforeWaitingForConcurrentSchemaChanges func(stmts []string) -} - -// ModuleTestingKnobs is part of the base.ModuleTestingKnobs interface. -func (*NewSchemaChangerTestingKnobs) ModuleTestingKnobs() {} - -// TestingKnobMetadata holds additional information about the execution of the -// schema change that is used by the testing knobs. -type TestingKnobMetadata struct { - Statements []string - Phase scop.Phase -} - // ExecuteOps executes the provided ops. The ops must all be of the same type. -func (ex *Executor) ExecuteOps( - ctx context.Context, toExecute scop.Ops, m TestingKnobMetadata, -) error { +func ExecuteOps(ctx context.Context, deps Dependencies, toExecute scop.Ops) error { log.Infof(ctx, "executing %d ops of type %s", len(toExecute.Slice()), toExecute.Type().String()) - if ex.testingKnobs != nil && ex.testingKnobs.BeforeStage != nil { - if err := ex.testingKnobs.BeforeStage(toExecute, m); err != nil { + if deps.TestingKnobs() != nil && deps.TestingKnobs().BeforeStage != nil { + md := TestingKnobMetadata{ + Statements: deps.Statements(), + Phase: deps.Phase(), + } + if err := deps.TestingKnobs().BeforeStage(toExecute, md); err != nil { return err } } switch typ := toExecute.Type(); typ { case scop.MutationType: - return ex.executeDescriptorMutationOps(ctx, toExecute.Slice()) + return executeDescriptorMutationOps(ctx, deps, toExecute.Slice()) case scop.BackfillType: - return ex.executeBackfillOps(ctx, toExecute.Slice()) + return executeBackfillOps(ctx, deps, toExecute.Slice()) case scop.ValidationType: - return ex.executeValidationOps(ctx, toExecute.Slice()) + return executeValidationOps(ctx, deps, toExecute.Slice()) default: return errors.AssertionFailedf("unknown ops type %d", typ) } } -func (ex *Executor) executeValidationOps(ctx context.Context, execute []scop.Op) error { - log.Errorf(ctx, "not implemented") - return nil -} - -func (ex *Executor) executeBackfillOps(ctx context.Context, execute []scop.Op) error { - // TODO(ajwerner): Run backfills in parallel. Will require some plumbing for - // checkpointing at the very least. - - for _, op := range execute { - var err error - switch op := op.(type) { - case *scop.BackfillIndex: - err = ex.executeIndexBackfillOp(ctx, op) - default: - panic("unimplemented") - } - if err != nil { - return err - } - } - return nil -} - -func (ex *Executor) executeIndexBackfillOp(ctx context.Context, op *scop.BackfillIndex) error { - // Note that the leasing here is subtle. We'll avoid the cache and ensure that - // the descriptor is read from the store. That means it will not be leased. - // This relies on changed to the descriptor not messing with this index - // backfill. - table, err := ex.descsCollection.GetImmutableTableByID(ctx, ex.txn, op.TableID, tree.ObjectLookupFlags{ - CommonLookupFlags: tree.CommonLookupFlags{ - Required: true, - RequireMutable: false, - AvoidCached: true, - }, - }) - if err != nil { - return err - } - mut, err := descriptorutils.FindMutation(table, descriptorutils.MakeIndexIDMutationSelector(op.IndexID)) - if err != nil { - return err - } - - // Must be the right index given the above call. - idxToBackfill := mut.AsIndex() - - // Split off the index span prior to backfilling. - if err := ex.maybeSplitIndexSpans(ctx, table.IndexSpan(ex.codec, idxToBackfill.GetID())); err != nil { - return err - } - return ex.indexBackfiller.BackfillIndex(ctx, ex.jobTracker, table, table.GetPrimaryIndexID(), idxToBackfill.GetID()) -} - -// IndexBackfiller is an abstract index backfiller that performs index backfills -// when provided with a specification of tables and indexes and a way to track -// job progress. -type IndexBackfiller interface { - BackfillIndex( - ctx context.Context, - _ JobProgressTracker, - _ catalog.TableDescriptor, - source descpb.IndexID, - destinations ...descpb.IndexID, - ) error -} - -// JobProgressTracker abstracts the infrastructure to read and write backfill -// progress to job state. -type JobProgressTracker interface { - - // This interface is implicitly implying that there is only one stage of - // index backfills for a given table in a schema change. It implies that - // because it assumes that it's safe and reasonable to just store one set of - // resume spans per table on the job. - // - // Potentially something close to interface could still work if there were - // multiple stages of backfills for a table if we tracked which stage this - // were somehow. Maybe we could do something like increment a stage counter - // per table after finishing the backfills. - // - // It definitely is possible that there are multiple index backfills on a - // table in the context of a single schema change that changes the set of - // columns (primary index) and adds secondary indexes. - // - // Really this complexity arises in the computation of the fraction completed. - // We'll want to know whether there are more index backfills to come. - // - // One idea is to index secondarily on the source index. - - GetResumeSpans(ctx context.Context, tableID descpb.ID, indexID descpb.IndexID) ([]roachpb.Span, error) - SetResumeSpans(ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, total, done []roachpb.Span) error -} - -func (ex *Executor) maybeSplitIndexSpans(ctx context.Context, span roachpb.Span) error { - // Only perform splits on the system tenant. - if !ex.codec.ForSystemTenant() { - return nil - } - const backfillSplitExpiration = time.Hour - expirationTime := ex.txn.DB().Clock().Now().Add(backfillSplitExpiration.Nanoseconds(), 0) - return ex.txn.DB().AdminSplit(ctx, span.Key, expirationTime) -} - -func (ex *Executor) executeDescriptorMutationOps(ctx context.Context, ops []scop.Op) error { - dg := newMutationDescGetter(ex.descsCollection, ex.txn, ex.executor) - mj := &mutationJobs{jobRegistry: ex.jobRegistry} - v := scmutationexec.NewMutationVisitor(dg, mj) - for _, op := range ops { - if err := op.(scop.MutationOp).Visit(ctx, v); err != nil { - return err - } - } - ba := ex.txn.NewBatch() - for _, id := range dg.retrieved.Ordered() { - desc, err := ex.descsCollection.GetMutableDescriptorByID(ctx, id, ex.txn) - if err != nil { - return errors.NewAssertionErrorWithWrappedErrf(err, "failed to retrieve modified descriptor") - } - if err := ex.descsCollection.WriteDescToBatch(ctx, false, desc, ba); err != nil { - return err - } - } - err := dg.SubmitDrainedNames(ctx, ex.codec, ba) - if err != nil { - return err - } - _, err = mj.SubmitAllJobs(ctx, ex.txn) - if err != nil { - return err - } - err = ex.descsCollection.ValidateUncommittedDescriptors(ctx, ex.txn) - if err != nil { - return err - } - if err := ex.txn.Run(ctx, ba); err != nil { - return errors.Wrap(err, "writing descriptors") - } - return nil -} - // UpdateDescriptorJobIDs updates the job ID for the schema change on the // specified set of table descriptors. func UpdateDescriptorJobIDs( ctx context.Context, - txn *kv.Txn, - descriptors *descs.Collection, + catalog Catalog, descIDs []descpb.ID, expectedID jobspb.JobID, newID jobspb.JobID, ) error { - b := txn.NewBatch() + b := catalog.NewCatalogChangeBatcher() for _, id := range descIDs { // Confirm the descriptor is a table, view or sequence // since we can only lock those types. - desc, err := descriptors.GetImmutableDescriptorByID(ctx, txn, id, - tree.CommonLookupFlags{ - Required: true, - IncludeDropped: true}, - ) - if err != nil { - return err - } - if desc.DescriptorType() != catalog.Table { - continue - } + desc, err := catalog.MustReadMutableDescriptor(ctx, id) if err != nil { return err } + // Currently all "locking" schema changes are on tables. This will probably - // need to be expanded at least to types. Synthetic descriptors are intentionally - // ignore, since we may inject these in the statement phase to mark things as - // dropped for example. - table, err := descriptors.GetMutableTableByID(ctx, txn, id, - tree.ObjectLookupFlags{ - CommonLookupFlags: tree.CommonLookupFlags{ - Required: true, - IncludeDropped: true, - AvoidSynthetic: true}, - }) - if err != nil { - return err + // need to be expanded at least to types. + table, ok := desc.(*tabledesc.Mutable) + if !ok { + continue } if oldID := jobspb.JobID(table.NewSchemaChangeJobID); oldID != expectedID { return errors.AssertionFailedf( "unexpected schema change job ID %d on table %d, expected %d", oldID, table.GetID(), expectedID) } table.NewSchemaChangeJobID = int64(newID) - if err := descriptors.WriteDescToBatch(ctx, true /* kvTrace */, table, b); err != nil { + if err := b.CreateOrUpdateDescriptor(ctx, table); err != nil { return err } } - return txn.Run(ctx, b) + return b.ValidateAndRun(ctx) } diff --git a/pkg/sql/schemachanger/scexec/executor_external_test.go b/pkg/sql/schemachanger/scexec/executor_external_test.go index 65842de05592..9b5a9c3bc92f 100644 --- a/pkg/sql/schemachanger/scexec/executor_external_test.go +++ b/pkg/sql/schemachanger/scexec/executor_external_test.go @@ -16,23 +16,22 @@ import ( "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/security" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" "github.com/cockroachdb/cockroach/pkg/sql/catalog/lease" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps/sctestutils" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scplan" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" @@ -51,6 +50,21 @@ type testInfra struct { cf *descs.CollectionFactory } +func (ti testInfra) newExecDeps( + txn *kv.Txn, descsCollection *descs.Collection, phase scop.Phase, +) scexec.Dependencies { + return scdeps.NewExecutorDependencies( + ti.lm.Codec(), + txn, + descsCollection, + nil, /* jobRegistry */ + noopBackfiller{}, /* indexBackfiller */ + nil, /* testingKnobs */ + nil, /* statements */ + phase, + ) +} + func setupTestInfra(t testing.TB) *testInfra { tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{}) return &testInfra{ @@ -128,11 +142,11 @@ CREATE TABLE db.t ( require.NoError(t, ti.txn(ctx, func( ctx context.Context, txn *kv.Txn, descriptors *descs.Collection, ) error { - ex := scexec.NewExecutor(txn, descriptors, ti.lm.Codec(), nil, nil, nil, nil, nil) + exDeps := ti.newExecDeps(txn, descriptors, scop.PreCommitPhase) _, orig, err := descriptors.GetImmutableTableByName(ctx, txn, &tn, immFlags) require.NoError(t, err) require.Equal(t, c.orig(), orig) - require.NoError(t, ex.ExecuteOps(ctx, c.ops(), scexec.TestingKnobMetadata{})) + require.NoError(t, scexec.ExecuteOps(ctx, exDeps, c.ops())) _, after, err := descriptors.GetImmutableTableByName(ctx, txn, &tn, immFlags) require.NoError(t, err) require.Equal(t, c.exp(), after) @@ -304,17 +318,8 @@ func TestSchemaChanger(t *testing.T) { require.NoError(t, err) stages := sc.Stages for _, s := range stages { - exec := scexec.NewExecutor( - txn, - descriptors, - ti.lm.Codec(), - noopBackfiller{}, - nil, - nil, - nil, - nil, - ) - require.NoError(t, exec.ExecuteOps(ctx, s.Ops, scexec.TestingKnobMetadata{})) + exDeps := ti.newExecDeps(txn, descriptors, phase) + require.NoError(t, scexec.ExecuteOps(ctx, exDeps, s.Ops)) ts = s.After } } @@ -329,8 +334,8 @@ func TestSchemaChanger(t *testing.T) { }) require.NoError(t, err) for _, s := range sc.Stages { - exec := scexec.NewExecutor(txn, descriptors, ti.lm.Codec(), noopBackfiller{}, nil, nil, nil, nil) - require.NoError(t, exec.ExecuteOps(ctx, s.Ops, scexec.TestingKnobMetadata{})) + exDeps := ti.newExecDeps(txn, descriptors, scop.PostCommitPhase) + require.NoError(t, scexec.ExecuteOps(ctx, exDeps, s.Ops)) after = s.After } return nil @@ -361,49 +366,28 @@ func TestSchemaChanger(t *testing.T) { require.NoError(t, ti.txn(ctx, func( ctx context.Context, txn *kv.Txn, descriptors *descs.Collection, ) (err error) { - execCfg := ti.tc.Server(0).ExecutorConfig().(sql.ExecutorConfig) - ip, cleanup := sql.NewInternalPlanner( - "foo", - kv.NewTxn(context.Background(), ti.db, ti.tc.Server(0).NodeID()), - security.RootUserName(), - &sql.MemoryMetrics{}, - &execCfg, - sessiondatapb.SessionData{}, - ) - planner := ip.(interface { - resolver.SchemaResolver - SemaCtx() *tree.SemaContext - EvalContext() *tree.EvalContext - scbuild.AuthorizationAccessor - }) - defer cleanup() - buildDeps := scbuild.Dependencies{ - Res: planner, - SemaCtx: planner.SemaCtx(), - EvalCtx: planner.EvalContext(), - Descs: descriptors, - AuthAccessor: planner, - } - parsed, err := parser.Parse("ALTER TABLE db.foo ADD COLUMN j INT") - require.NoError(t, err) - require.Len(t, parsed, 1) - outputNodes, err := scbuild.Build(ctx, buildDeps, nil, parsed[0].AST.(*tree.AlterTable)) - require.NoError(t, err) - - for _, phase := range []scop.Phase{ - scop.StatementPhase, - scop.PreCommitPhase, - } { - sc, err := scplan.MakePlan(outputNodes, scplan.Params{ - ExecutionPhase: phase, - }) + sctestutils.WithBuilderDependenciesFromTestServer(ti.tc.Server(0), func(buildDeps scbuild.Dependencies) { + parsed, err := parser.Parse("ALTER TABLE db.foo ADD COLUMN j INT") require.NoError(t, err) - for _, s := range sc.Stages { - require.NoError(t, scexec.NewExecutor(txn, descriptors, ti.lm.Codec(), noopBackfiller{}, nil, nil, nil, nil). - ExecuteOps(ctx, s.Ops, scexec.TestingKnobMetadata{})) - ts = s.After + require.Len(t, parsed, 1) + outputNodes, err := scbuild.Build(ctx, buildDeps, nil, parsed[0].AST.(*tree.AlterTable)) + require.NoError(t, err) + + for _, phase := range []scop.Phase{ + scop.StatementPhase, + scop.PreCommitPhase, + } { + sc, err := scplan.MakePlan(outputNodes, scplan.Params{ + ExecutionPhase: phase, + }) + require.NoError(t, err) + for _, s := range sc.Stages { + exDeps := ti.newExecDeps(txn, descriptors, phase) + require.NoError(t, scexec.ExecuteOps(ctx, exDeps, s.Ops)) + ts = s.After + } } - } + }) return nil })) require.NoError(t, ti.txn(ctx, func( @@ -414,8 +398,8 @@ func TestSchemaChanger(t *testing.T) { }) require.NoError(t, err) for _, s := range sc.Stages { - exec := scexec.NewExecutor(txn, descriptors, ti.lm.Codec(), noopBackfiller{}, nil, nil, nil, nil) - require.NoError(t, exec.ExecuteOps(ctx, s.Ops, scexec.TestingKnobMetadata{})) + exDeps := ti.newExecDeps(txn, descriptors, scop.PostCommitPhase) + require.NoError(t, scexec.ExecuteOps(ctx, exDeps, s.Ops)) } return nil })) diff --git a/pkg/sql/schemachanger/scexec/mutation_desc_getter.go b/pkg/sql/schemachanger/scexec/mutation_desc_getter.go deleted file mode 100644 index 8fa364663bd2..000000000000 --- a/pkg/sql/schemachanger/scexec/mutation_desc_getter.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package scexec - -import ( - "context" - - "github.com/cockroachdb/cockroach/pkg/keys" - "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/sql/catalog" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkeys" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/scmutationexec" - "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" - "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" -) - -type mutationDescGetter struct { - descs *descs.Collection - txn *kv.Txn - retrieved catalog.DescriptorIDSet - drainedNames map[descpb.ID][]descpb.NameInfo - executor sqlutil.InternalExecutor -} - -func newMutationDescGetter( - descs *descs.Collection, txn *kv.Txn, executor sqlutil.InternalExecutor, -) *mutationDescGetter { - return &mutationDescGetter{ - descs: descs, - txn: txn, - drainedNames: make(map[descpb.ID][]descpb.NameInfo), - executor: executor, - } -} - -func (m *mutationDescGetter) GetAnyDescriptorByID( - ctx context.Context, id descpb.ID, -) (catalog.MutableDescriptor, error) { - desc, err := m.descs.GetMutableDescriptorByID(ctx, id, m.txn) - if err != nil { - return nil, err - } - desc.MaybeIncrementVersion() - m.retrieved.Add(desc.GetID()) - return desc, nil -} - -func (m *mutationDescGetter) GetMutableTypeByID( - ctx context.Context, id descpb.ID, -) (*typedesc.Mutable, error) { - typeDesc, err := m.descs.GetMutableTypeVersionByID(ctx, m.txn, id) - if err != nil { - return nil, err - } - typeDesc.MaybeIncrementVersion() - m.retrieved.Add(typeDesc.GetID()) - return typeDesc, nil -} - -func (m *mutationDescGetter) GetImmutableDatabaseByID( - ctx context.Context, id descpb.ID, -) (catalog.DatabaseDescriptor, error) { - _, dbDesc, err := m.descs.GetImmutableDatabaseByID(ctx, m.txn, id, tree.DatabaseLookupFlags{Required: true}) - return dbDesc, err -} - -func (m *mutationDescGetter) GetMutableTableByID( - ctx context.Context, id descpb.ID, -) (*tabledesc.Mutable, error) { - table, err := m.descs.GetMutableTableVersionByID(ctx, id, m.txn) - if err != nil { - return nil, err - } - table.MaybeIncrementVersion() - m.retrieved.Add(table.GetID()) - return table, nil -} - -func (m *mutationDescGetter) AddDrainedName(id descpb.ID, nameInfo descpb.NameInfo) { - if _, ok := m.drainedNames[id]; !ok { - m.drainedNames[id] = []descpb.NameInfo{nameInfo} - } else { - m.drainedNames[id] = append(m.drainedNames[id], nameInfo) - } -} - -func (m *mutationDescGetter) SubmitDrainedNames( - ctx context.Context, codec keys.SQLCodec, ba *kv.Batch, -) error { - for _, drainedNames := range m.drainedNames { - for _, drain := range drainedNames { - ba.Del(catalogkeys.EncodeNameKey(codec, drain)) - } - } - return nil -} - -func (m *mutationDescGetter) MarkDescriptorAsSyntheticDropped( - ctx context.Context, id descpb.ID, -) error { - table, err := m.descs.GetImmutableDescriptorByID(ctx, m.txn, id, tree.CommonLookupFlags{Required: true}) - if err != nil { - return err - } - droppedDesc := catalogkv.NewBuilderWithMVCCTimestamp(table.DescriptorProto(), m.txn.ReadTimestamp()).BuildExistingMutable() - droppedDesc.SetDropped() - m.descs.AddSyntheticDescriptor(droppedDesc) - return nil -} - -func (m *mutationDescGetter) RemoveSyntheticDescFromTxn(_ context.Context, id descpb.ID) error { - m.descs.RemoveSyntheticDescriptor(id) - return nil -} - -func (m *mutationDescGetter) RemoveObjectComments(ctx context.Context, id descpb.ID) error { - _, err := m.executor.ExecEx( - ctx, - "delete-table-comments", - m.txn, - sessiondata.InternalExecutorOverride{User: security.RootUserName()}, - "DELETE FROM system.comments WHERE object_id=$1", - id) - if err != nil { - return err - } - return err -} - -var _ scmutationexec.Catalog = (*mutationDescGetter)(nil) diff --git a/pkg/sql/schemachanger/scexec/mutation_jobs.go b/pkg/sql/schemachanger/scexec/mutation_jobs.go deleted file mode 100644 index 3ead0d19a036..000000000000 --- a/pkg/sql/schemachanger/scexec/mutation_jobs.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package scexec - -import ( - "context" - "fmt" - "strings" - - "github.com/cockroachdb/cockroach/pkg/jobs" - "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" - "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/sql/catalog" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec/scmutationexec" - "github.com/cockroachdb/cockroach/pkg/util/timeutil" -) - -type mutationJobs struct { - jobs []jobs.Record - jobIDs []jobspb.JobID - jobRegistry *jobs.Registry - descriptorGCJobs []jobspb.SchemaChangeGCDetails_DroppedID -} - -// CreateGCJobRecord creates the job record for a GC job, setting some -// properties which are common for all GC jobs. -func CreateGCJobRecord( - originalDescription string, username security.SQLUsername, details jobspb.SchemaChangeGCDetails, -) jobs.Record { - descriptorIDs := make([]descpb.ID, 0) - if len(details.Indexes) > 0 { - if len(descriptorIDs) == 0 { - descriptorIDs = []descpb.ID{details.ParentID} - } - } else { - for _, table := range details.Tables { - descriptorIDs = append(descriptorIDs, table.ID) - } - } - return jobs.Record{ - Description: fmt.Sprintf("GC for %s", originalDescription), - Username: username, - DescriptorIDs: descriptorIDs, - Details: details, - Progress: jobspb.SchemaChangeGCProgress{}, - RunningStatus: "waiting for GC TTL", - NonCancelable: true, - } -} - -func (m *mutationJobs) AddNewGCJobForDescriptor(descriptor catalog.Descriptor) { - m.descriptorGCJobs = append(m.descriptorGCJobs, - jobspb.SchemaChangeGCDetails_DroppedID{ - ID: descriptor.GetID(), - DropTime: timeutil.Now().UnixNano(), - }) -} - -func (m *mutationJobs) addNewGCJob(job jobspb.SchemaChangeGCDetails, description string) { - record := CreateGCJobRecord(description, security.NodeUserName(), job) - jobID := m.jobRegistry.MakeJobID() - m.jobs = append(m.jobs, record) - m.jobIDs = append(m.jobIDs, jobID) -} - -func (m *mutationJobs) SubmitAllJobs(ctx context.Context, txn *kv.Txn) (bool, error) { - if len(m.descriptorGCJobs) > 0 { - job := jobspb.SchemaChangeGCDetails{ - Tables: m.descriptorGCJobs, - } - descriptorList := strings.Builder{} - descriptorList.WriteString("Dropping descriptors ") - for _, table := range m.descriptorGCJobs { - descriptorList.WriteString(fmt.Sprintf("%d ", table.ID)) - } - m.addNewGCJob(job, descriptorList.String()) - m.descriptorGCJobs = nil - } - for idx := range m.jobIDs { - _, err := m.jobRegistry.CreateJobWithTxn(ctx, m.jobs[idx], m.jobIDs[idx], txn) - if err != nil { - return false, err - } - } - return len(m.jobIDs) > 0, nil -} - -var _ scmutationexec.MutationJobs = (*mutationJobs)(nil) diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/BUILD.bazel b/pkg/sql/schemachanger/scexec/scmutationexec/BUILD.bazel index ee2d373973ff..842b5051ae5c 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/BUILD.bazel +++ b/pkg/sql/schemachanger/scexec/scmutationexec/BUILD.bazel @@ -10,7 +10,9 @@ go_library( visibility = ["//visibility:public"], deps = [ "//pkg/sql/catalog", + "//pkg/sql/catalog/dbdesc", "//pkg/sql/catalog/descpb", + "//pkg/sql/catalog/schemadesc", "//pkg/sql/catalog/seqexpr", "//pkg/sql/catalog/tabledesc", "//pkg/sql/catalog/typedesc", diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go b/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go index d54ad1a2695c..1feb9105809d 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go +++ b/pkg/sql/schemachanger/scexec/scmutationexec/scmutationexec.go @@ -15,7 +15,9 @@ import ( "sort" "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/dbdesc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemadesc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/seqexpr" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" @@ -26,65 +28,106 @@ import ( "github.com/cockroachdb/errors" ) -// DescriptorReader encapsulates logic used to retrieve -// and track modified descriptors. -type DescriptorReader interface { - GetMutableTypeByID(ctx context.Context, id descpb.ID) (*typedesc.Mutable, error) - GetImmutableDatabaseByID(ctx context.Context, id descpb.ID) (catalog.DatabaseDescriptor, error) - GetMutableTableByID(ctx context.Context, id descpb.ID) (*tabledesc.Mutable, error) - GetAnyDescriptorByID(ctx context.Context, id descpb.ID) (catalog.MutableDescriptor, error) - AddDrainedName(id descpb.ID, nameInfo descpb.NameInfo) -} +// CatalogReader describes catalog read operations as required by the mutation +// visitor. +type CatalogReader interface { + // MustReadImmutableDescriptor reads a descriptor from the catalog by ID. + MustReadImmutableDescriptor(ctx context.Context, id descpb.ID) (catalog.Descriptor, error) + + // AddSyntheticDescriptor adds a synthetic descriptor to the reader state. + // Subsequent calls to MustReadImmutableDescriptor for this ID will return + // this synthetic descriptor instead of what it would have otherwise returned. + AddSyntheticDescriptor(desc catalog.Descriptor) -// SyntheticDescriptorWriter encapsulates logic for -// injecting synthetic descriptors only in the current -// transaction. -type SyntheticDescriptorWriter interface { - MarkDescriptorAsSyntheticDropped(ctx context.Context, id descpb.ID) error - RemoveSyntheticDescFromTxn(ctx context.Context, id descpb.ID) error + // RemoveSyntheticDescriptor undoes the effects of AddSyntheticDescriptor. + RemoveSyntheticDescriptor(id descpb.ID) } -// NamespaceWriter encapsulates operations used to manipulate -// namespaces. -type NamespaceWriter interface { +// MutationVisitorStateUpdater is the interface for updating the visitor state. +type MutationVisitorStateUpdater interface { + + // CheckOutDescriptor reads a descriptor from the catalog by ID and marks it + // as undergoing a change. + CheckOutDescriptor(ctx context.Context, id descpb.ID) (catalog.MutableDescriptor, error) + + // AddDrainedName marks a namespace entry as being drained. AddDrainedName(id descpb.ID, nameInfo descpb.NameInfo) + + // AddNewGCJobForDescriptor enqueues a GC job for the given descriptor. + AddNewGCJobForDescriptor(descriptor catalog.Descriptor) } -// CommentWriter encapsulates operations used to manipulate -// object comments. -type CommentWriter interface { - RemoveObjectComments(ctx context.Context, id descpb.ID) error +// NewMutationVisitor creates a new scop.MutationVisitor. +func NewMutationVisitor(cr CatalogReader, s MutationVisitorStateUpdater) scop.MutationVisitor { + return &visitor{ + cr: cr, + s: s, + } } -// Catalog encapsulates the logic to modify descriptors, -// namespaces, comments to help support schema changer mutations. -type Catalog interface { - DescriptorReader - SyntheticDescriptorWriter - NamespaceWriter - CommentWriter +type visitor struct { + cr CatalogReader + s MutationVisitorStateUpdater } -// MutationJobs encapsulates the logic to create different types -// of jobs. -type MutationJobs interface { - AddNewGCJobForDescriptor(descriptor catalog.Descriptor) +func (m *visitor) checkOutTable(ctx context.Context, id descpb.ID) (*tabledesc.Mutable, error) { + desc, err := m.s.CheckOutDescriptor(ctx, id) + if err != nil { + return nil, err + } + mut, ok := desc.(*tabledesc.Mutable) + if !ok { + return nil, catalog.WrapTableDescRefErr(id, catalog.NewDescriptorTypeError(desc)) + } + return mut, nil } -// NewMutationVisitor creates a new scop.MutationVisitor. -func NewMutationVisitor(catalog Catalog, jobs MutationJobs) scop.MutationVisitor { - return &visitor{catalog: catalog, jobs: jobs} +func (m *visitor) checkOutDatabase(ctx context.Context, id descpb.ID) (*dbdesc.Mutable, error) { + desc, err := m.s.CheckOutDescriptor(ctx, id) + if err != nil { + return nil, err + } + mut, ok := desc.(*dbdesc.Mutable) + if !ok { + return nil, catalog.WrapDatabaseDescRefErr(id, catalog.NewDescriptorTypeError(desc)) + } + return mut, nil } -type visitor struct { - catalog Catalog - jobs MutationJobs +// Stop the linter from complaining. +var _ = ((*visitor)(nil)).checkOutDatabase + +func (m *visitor) checkOutSchema(ctx context.Context, id descpb.ID) (*schemadesc.Mutable, error) { + desc, err := m.s.CheckOutDescriptor(ctx, id) + if err != nil { + return nil, err + } + mut, ok := desc.(*schemadesc.Mutable) + if !ok { + return nil, catalog.WrapSchemaDescRefErr(id, catalog.NewDescriptorTypeError(desc)) + } + return mut, nil +} + +// Stop the linter from complaining. +var _ = ((*visitor)(nil)).checkOutSchema + +func (m *visitor) checkOutType(ctx context.Context, id descpb.ID) (*typedesc.Mutable, error) { + desc, err := m.s.CheckOutDescriptor(ctx, id) + if err != nil { + return nil, err + } + mut, ok := desc.(*typedesc.Mutable) + if !ok { + return nil, catalog.WrapTypeDescRefErr(id, catalog.NewDescriptorTypeError(desc)) + } + return mut, nil } func (m *visitor) MakeAddedColumnDeleteAndWriteOnly( ctx context.Context, op scop.MakeAddedColumnDeleteAndWriteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -99,7 +142,7 @@ func (m *visitor) MakeAddedColumnDeleteAndWriteOnly( func (m *visitor) UpdateRelationDeps(ctx context.Context, op scop.UpdateRelationDeps) error { // TODO(fqazi): Only implemented for sequences. - tableDesc, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + tableDesc, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -146,7 +189,7 @@ func (m *visitor) RemoveColumnDefaultExpression( ctx context.Context, op scop.RemoveColumnDefaultExpression, ) error { // Remove the descriptors namespaces as the last stage - tableDesc, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + tableDesc, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -162,13 +205,13 @@ func (m *visitor) RemoveColumnDefaultExpression( } func (m *visitor) AddTypeBackRef(ctx context.Context, op scop.AddTypeBackRef) error { - mutDesc, err := m.catalog.GetMutableTypeByID(ctx, op.TypeID) + mutDesc, err := m.checkOutType(ctx, op.TypeID) if err != nil { return err } mutDesc.AddReferencingDescriptorID(op.DescID) // Sanity: Validate that a back reference exists by now. - desc, err := m.catalog.GetAnyDescriptorByID(ctx, op.DescID) + desc, err := m.cr.MustReadImmutableDescriptor(ctx, op.DescID) if err != nil { return err } @@ -187,7 +230,7 @@ func (m *visitor) RemoveRelationDependedOnBy( ctx context.Context, op scop.RemoveRelationDependedOnBy, ) error { // Remove the descriptors namespaces as the last stage - tableDesc, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + tableDesc, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -201,7 +244,7 @@ func (m *visitor) RemoveRelationDependedOnBy( } func (m *visitor) RemoveSequenceOwnedBy(ctx context.Context, op scop.RemoveSequenceOwnedBy) error { - mutDesc, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + mutDesc, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -211,7 +254,7 @@ func (m *visitor) RemoveSequenceOwnedBy(ctx context.Context, op scop.RemoveSeque } func (m *visitor) RemoveTypeBackRef(ctx context.Context, op scop.RemoveTypeBackRef) error { - mutDesc, err := m.catalog.GetMutableTypeByID(ctx, op.TypeID) + mutDesc, err := m.checkOutType(ctx, op.TypeID) if err != nil { return err } @@ -222,41 +265,42 @@ func (m *visitor) RemoveTypeBackRef(ctx context.Context, op scop.RemoveTypeBackR func (m *visitor) CreateGcJobForDescriptor( ctx context.Context, op scop.CreateGcJobForDescriptor, ) error { - desc, err := m.catalog.GetAnyDescriptorByID(ctx, op.DescID) + desc, err := m.cr.MustReadImmutableDescriptor(ctx, op.DescID) if err != nil { return err } - m.jobs.AddNewGCJobForDescriptor(desc) + m.s.AddNewGCJobForDescriptor(desc) return nil } func (m *visitor) MarkDescriptorAsDropped( ctx context.Context, op scop.MarkDescriptorAsDropped, ) error { - // Before we can mutate the descriptor, drop any synthetic - // one that inserted. - err := m.catalog.RemoveSyntheticDescFromTxn(ctx, op.DescID) - if err != nil { - return err - } - // Mark the descriptor as dropped. - descriptor, err := m.catalog.GetAnyDescriptorByID(ctx, op.DescID) + // Before we can mutate the descriptor, get rid of any synthetic descriptor. + m.cr.RemoveSyntheticDescriptor(op.DescID) + desc, err := m.s.CheckOutDescriptor(ctx, op.DescID) if err != nil { return err } - descriptor.SetDropped() + desc.SetDropped() return nil } func (m *visitor) MarkDescriptorAsDroppedSynthetically( ctx context.Context, op scop.MarkDescriptorAsDroppedSynthetically, ) error { - // Mark table as dropped inside the current txn. - return m.catalog.MarkDescriptorAsSyntheticDropped(ctx, op.DescID) + desc, err := m.cr.MustReadImmutableDescriptor(ctx, op.DescID) + if err != nil { + return err + } + mut := desc.NewBuilder().BuildCreatedMutable() + mut.SetDropped() + m.cr.AddSyntheticDescriptor(mut) + return nil } func (m *visitor) DrainDescriptorName(ctx context.Context, op scop.DrainDescriptorName) error { - descriptor, err := m.catalog.GetAnyDescriptorByID(ctx, op.TableID) + descriptor, err := m.cr.MustReadImmutableDescriptor(ctx, op.TableID) if err != nil { return err } @@ -265,12 +309,12 @@ func (m *visitor) DrainDescriptorName(ctx context.Context, op scop.DrainDescript ParentID: descriptor.GetParentID(), ParentSchemaID: descriptor.GetParentSchemaID(), Name: descriptor.GetName()} - m.catalog.AddDrainedName(descriptor.GetID(), nameDetails) + m.s.AddDrainedName(descriptor.GetID(), nameDetails) return nil } func (m *visitor) MakeColumnPublic(ctx context.Context, op scop.MakeColumnPublic) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -294,7 +338,7 @@ func (m *visitor) MakeColumnPublic(ctx context.Context, op scop.MakeColumnPublic func (m *visitor) MakeDroppedNonPrimaryIndexDeleteAndWriteOnly( ctx context.Context, op scop.MakeDroppedNonPrimaryIndexDeleteAndWriteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -317,7 +361,7 @@ func (m *visitor) MakeDroppedNonPrimaryIndexDeleteAndWriteOnly( func (m *visitor) MakeDroppedColumnDeleteAndWriteOnly( ctx context.Context, op scop.MakeDroppedColumnDeleteAndWriteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -340,7 +384,7 @@ func (m *visitor) MakeDroppedColumnDeleteAndWriteOnly( func (m *visitor) MakeDroppedColumnDeleteOnly( ctx context.Context, op scop.MakeDroppedColumnDeleteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -354,7 +398,7 @@ func (m *visitor) MakeDroppedColumnDeleteOnly( } func (m *visitor) MakeColumnAbsent(ctx context.Context, op scop.MakeColumnAbsent) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -375,7 +419,7 @@ func (m *visitor) MakeColumnAbsent(ctx context.Context, op scop.MakeColumnAbsent func (m *visitor) MakeAddedIndexDeleteAndWriteOnly( ctx context.Context, op scop.MakeAddedIndexDeleteAndWriteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -391,7 +435,7 @@ func (m *visitor) MakeAddedIndexDeleteAndWriteOnly( func (m *visitor) MakeAddedColumnDeleteOnly( ctx context.Context, op scop.MakeAddedColumnDeleteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -431,7 +475,7 @@ func (m *visitor) MakeAddedColumnDeleteOnly( func (m *visitor) MakeDroppedIndexDeleteOnly( ctx context.Context, op scop.MakeDroppedIndexDeleteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -447,7 +491,7 @@ func (m *visitor) MakeDroppedIndexDeleteOnly( func (m *visitor) MakeDroppedPrimaryIndexDeleteAndWriteOnly( ctx context.Context, op scop.MakeDroppedPrimaryIndexDeleteAndWriteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -466,7 +510,7 @@ func (m *visitor) MakeAddedIndexDeleteOnly( ctx context.Context, op scop.MakeAddedIndexDeleteOnly, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -483,7 +527,7 @@ func (m *visitor) MakeAddedIndexDeleteOnly( } func (m *visitor) AddCheckConstraint(ctx context.Context, op scop.AddCheckConstraint) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -505,7 +549,7 @@ func (m *visitor) AddCheckConstraint(ctx context.Context, op scop.AddCheckConstr func (m *visitor) MakeAddedPrimaryIndexPublic( ctx context.Context, op scop.MakeAddedPrimaryIndexPublic, ) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -522,7 +566,7 @@ func (m *visitor) MakeAddedPrimaryIndexPublic( } func (m *visitor) MakeIndexAbsent(ctx context.Context, op scop.MakeIndexAbsent) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -535,7 +579,7 @@ func (m *visitor) MakeIndexAbsent(ctx context.Context, op scop.MakeIndexAbsent) } func (m *visitor) AddColumnFamily(ctx context.Context, op scop.AddColumnFamily) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } @@ -547,7 +591,7 @@ func (m *visitor) AddColumnFamily(ctx context.Context, op scop.AddColumnFamily) } func (m *visitor) DropForeignKeyRef(ctx context.Context, op scop.DropForeignKeyRef) error { - table, err := m.catalog.GetMutableTableByID(ctx, op.TableID) + table, err := m.checkOutTable(ctx, op.TableID) if err != nil { return err } diff --git a/pkg/sql/schemachanger/scjob/BUILD.bazel b/pkg/sql/schemachanger/scjob/BUILD.bazel index ce841defc0d7..33b8e13bc38d 100644 --- a/pkg/sql/schemachanger/scjob/BUILD.bazel +++ b/pkg/sql/schemachanger/scjob/BUILD.bazel @@ -8,18 +8,11 @@ go_library( deps = [ "//pkg/jobs", "//pkg/jobs/jobspb", - "//pkg/keys", "//pkg/kv", - "//pkg/roachpb:with-mocks", "//pkg/settings/cluster", "//pkg/sql", - "//pkg/sql/catalog/descpb", - "//pkg/sql/catalog/descs", - "//pkg/sql/schemachanger/scexec", - "//pkg/sql/schemachanger/scop", + "//pkg/sql/schemachanger/scdeps", "//pkg/sql/schemachanger/scpb", - "//pkg/sql/schemachanger/scplan", - "//pkg/sql/sem/tree", - "//pkg/util/log/logcrash", + "//pkg/sql/schemachanger/scrun", ], ) diff --git a/pkg/sql/schemachanger/scjob/job.go b/pkg/sql/schemachanger/scjob/job.go index 454eb68416af..2abb009cc547 100644 --- a/pkg/sql/schemachanger/scjob/job.go +++ b/pkg/sql/schemachanger/scjob/job.go @@ -15,19 +15,12 @@ import ( "github.com/cockroachdb/cockroach/pkg/jobs" "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" - "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" - "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scplan" - "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/util/log/logcrash" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scrun" ) func init() { @@ -47,37 +40,9 @@ type newSchemaChangeResumer struct { targets []*scpb.Target } -type badJobTracker struct { - txn *kv.Txn - descriptors *descs.Collection - codec keys.SQLCodec -} - -func (b badJobTracker) GetResumeSpans( - ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, -) ([]roachpb.Span, error) { - table, err := b.descriptors.GetImmutableTableByID(ctx, b.txn, tableID, tree.ObjectLookupFlags{ - CommonLookupFlags: tree.CommonLookupFlags{ - Required: true, - AvoidCached: true, - }, - }) - if err != nil { - return nil, err - } - return []roachpb.Span{table.IndexSpan(b.codec, indexID)}, nil -} - -func (b badJobTracker) SetResumeSpans( - ctx context.Context, tableID descpb.ID, indexID descpb.IndexID, total, done []roachpb.Span, -) error { - panic("implement me") -} - -var _ scexec.JobProgressTracker = (*badJobTracker)(nil) - func (n *newSchemaChangeResumer) Resume(ctx context.Context, execCtxI interface{}) (err error) { execCtx := execCtxI.(sql.JobExecContext) + execCfg := execCtx.ExecCfg() if err := n.job.Update(ctx, nil /* txn */, func(txn *kv.Txn, md jobs.JobMetadata, ju *jobs.JobUpdater) error { return nil }); err != nil { @@ -88,102 +53,23 @@ func (n *newSchemaChangeResumer) Resume(ctx context.Context, execCtxI interface{ // TODO(ajwerner): Wait for leases on all descriptors before starting to // avoid restarts. + payload := n.job.Payload() progress := n.job.Progress() - states := progress.GetNewSchemaChange().States - - settings := execCtx.ExtendedEvalContext().Settings - sc, err := scplan.MakePlan(makeState(ctx, settings, n.targets, states), scplan.Params{ - ExecutionPhase: scop.PostCommitPhase, - }) - if err != nil { - return err - } - restoreTableIDs := func(txn *kv.Txn, descriptors *descs.Collection) error { - return scexec.UpdateDescriptorJobIDs( - ctx, txn, descriptors, n.job.Payload().DescriptorIDs, n.job.ID(), jobspb.InvalidJobID, - ) - } - - for i, s := range sc.Stages { - if err := sql.DescsTxn(ctx, execCtx.ExecCfg(), func( - ctx context.Context, txn *kv.Txn, descriptors *descs.Collection, - ) error { - jt := badJobTracker{ - txn: txn, - descriptors: descriptors, - codec: execCtx.ExecCfg().Codec, - } - if err := scexec.NewExecutor( - txn, descriptors, execCtx.ExecCfg().Codec, execCtx.ExecCfg().IndexBackfiller, - jt, execCtx.ExecCfg().NewSchemaChangerTestingKnobs, execCtx.ExecCfg().JobRegistry, - execCtx.ExecCfg().InternalExecutor).ExecuteOps(ctx, s.Ops, scexec.TestingKnobMetadata{ - Statements: n.job.Payload().Statement, - Phase: scop.PostCommitPhase, - }); err != nil { - return err - } - // If this is the last stage, also update all the table descriptors to - // remove the job ID. - if i == len(sc.Stages)-1 { - if err := restoreTableIDs(txn, descriptors); err != nil { - return err - } - } - return n.job.Update(ctx, txn, func(txn *kv.Txn, md jobs.JobMetadata, ju *jobs.JobUpdater) error { - pg := md.Progress.GetNewSchemaChange() - pg.States = makeStatuses(s.After) - ju.UpdateProgress(md.Progress) - return nil - }) - }); err != nil { - return err - } - execCtx.ExecCfg().JobRegistry.NotifyToAdoptJobs(ctx) - } - - // If no stages exist, then execute a singe transaction - // within this job to allow schema changes again. - if len(sc.Stages) == 0 { - err := sql.DescsTxn(ctx, execCtx.ExecCfg(), func( - ctx context.Context, txn *kv.Txn, descriptors *descs.Collection, - ) error { - err := restoreTableIDs(txn, descriptors) - if err != nil { - return err - } - return nil - }) - if err != nil { - return err - } - execCtx.ExecCfg().JobRegistry.NotifyToAdoptJobs(ctx) - } - return nil -} - -func makeStatuses(next scpb.State) []scpb.Status { - states := make([]scpb.Status, len(next)) - for i := range next { - states[i] = next[i].Status - } - return states -} - -func makeState( - ctx context.Context, sv *cluster.Settings, protos []*scpb.Target, states []scpb.Status, -) scpb.State { - if len(protos) != len(states) { - logcrash.ReportOrPanic(ctx, &sv.SV, "unexpected slice size mismatch %d and %d", - len(protos), len(states)) - } - ts := make(scpb.State, len(protos)) - for i := range protos { - ts[i] = &scpb.Node{ - Target: protos[i], - Status: states[i], - } - } - return ts + newSchemaChangeProgress := progress.GetNewSchemaChange() + newSchemaChangeDetails := payload.GetNewSchemaChange() + deps := scdeps.NewJobExecutionDependencies( + execCfg.CollectionFactory, + execCfg.DB, + execCfg.InternalExecutor, + execCfg.IndexBackfiller, + execCfg.JobRegistry, + n.job, + execCfg.Codec, + execCfg.Settings, + execCfg.NewSchemaChangerTestingKnobs, + payload.Statement, + ) + return scrun.RunSchemaChangesInJob(ctx, deps, n.job.ID(), payload.DescriptorIDs, *newSchemaChangeDetails, *newSchemaChangeProgress) } func (n *newSchemaChangeResumer) OnFailOrCancel(ctx context.Context, execCtx interface{}) error { diff --git a/pkg/sql/schemachanger/scplan/BUILD.bazel b/pkg/sql/schemachanger/scplan/BUILD.bazel index e4b635f52156..9cd5ec58e17c 100644 --- a/pkg/sql/schemachanger/scplan/BUILD.bazel +++ b/pkg/sql/schemachanger/scplan/BUILD.bazel @@ -27,23 +27,19 @@ go_test( deps = [ ":scplan", "//pkg/base", - "//pkg/kv", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", - "//pkg/sql", "//pkg/sql/catalog/descpb", - "//pkg/sql/catalog/descs", - "//pkg/sql/catalog/resolver", "//pkg/sql/parser", "//pkg/sql/schemachanger/scbuild", + "//pkg/sql/schemachanger/scdeps/sctestutils", "//pkg/sql/schemachanger/scgraph", "//pkg/sql/schemachanger/scgraphviz", "//pkg/sql/schemachanger/scop", "//pkg/sql/schemachanger/scpb", "//pkg/sql/schemachanger/screl", "//pkg/sql/sem/tree", - "//pkg/sql/sessiondatapb", "//pkg/testutils/serverutils", "//pkg/testutils/sqlutils", "//pkg/testutils/testcluster", diff --git a/pkg/sql/schemachanger/scplan/plan_test.go b/pkg/sql/schemachanger/scplan/plan_test.go index 95e10bc377a3..26d62c0fb26f 100644 --- a/pkg/sql/schemachanger/scplan/plan_test.go +++ b/pkg/sql/schemachanger/scplan/plan_test.go @@ -20,14 +20,10 @@ import ( "testing" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/kv" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scdeps/sctestutils" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scgraph" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scgraphviz" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" @@ -35,7 +31,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scplan" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/screl" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/util/leaktest" @@ -91,40 +86,39 @@ func TestPlanAlterTable(t *testing.T) { } return "" case "ops", "deps": - deps, cleanup := newTestingPlanDeps(s) - defer cleanup() - - stmts, err := parser.Parse(d.Input) - require.NoError(t, err) - var outputNodes scpb.State - for i := range stmts { - outputNodes, err = scbuild.Build(ctx, *deps, outputNodes, stmts[i].AST) + var plan scplan.Plan + sctestutils.WithBuilderDependenciesFromTestServer(s, func(deps scbuild.Dependencies) { + stmts, err := parser.Parse(d.Input) require.NoError(t, err) - } + var outputNodes scpb.State + for i := range stmts { + outputNodes, err = scbuild.Build(ctx, deps, outputNodes, stmts[i].AST) + require.NoError(t, err) + } - plan, err := scplan.MakePlan(outputNodes, - scplan.Params{ - ExecutionPhase: scop.PostCommitPhase, - }) - require.NoError(t, err) + plan, err = scplan.MakePlan(outputNodes, + scplan.Params{ + ExecutionPhase: scop.PostCommitPhase, + }) + require.NoError(t, err) + }) if d.Cmd == "ops" { return marshalOps(t, &plan) } return marshalDeps(t, &plan) case "unimplemented": - deps, cleanup := newTestingPlanDeps(s) - defer cleanup() - - stmts, err := parser.Parse(d.Input) - require.NoError(t, err) - require.Len(t, stmts, 1) - - stmt := stmts[0] - alter, ok := stmt.AST.(*tree.AlterTable) - require.Truef(t, ok, "not an ALTER TABLE statement: %s", stmt.SQL) - _, err = scbuild.Build(ctx, *deps, nil, alter) - require.Truef(t, scbuild.HasNotImplemented(err), "expected unimplemented, got %v", err) + sctestutils.WithBuilderDependenciesFromTestServer(s, func(deps scbuild.Dependencies) { + stmts, err := parser.Parse(d.Input) + require.NoError(t, err) + require.Len(t, stmts, 1) + + stmt := stmts[0] + alter, ok := stmt.AST.(*tree.AlterTable) + require.Truef(t, ok, "not an ALTER TABLE statement: %s", stmt.SQL) + _, err = scbuild.Build(ctx, deps, nil, alter) + require.Truef(t, scbuild.HasNotImplemented(err), "expected unimplemented, got %v", err) + }) return "" default: @@ -197,32 +191,3 @@ func marshalOps(t *testing.T, plan *scplan.Plan) string { } return stages.String() } - -func newTestingPlanDeps(s serverutils.TestServerInterface) (*scbuild.Dependencies, func()) { - execCfg := s.ExecutorConfig().(sql.ExecutorConfig) - ip, cleanup := sql.NewInternalPlanner( - "test", - kv.NewTxn(context.Background(), s.DB(), s.NodeID()), - security.RootUserName(), - &sql.MemoryMetrics{}, - &execCfg, - // Setting the database on the session data to "defaultdb" in the obvious - // way doesn't seem to do what we want. - sessiondatapb.SessionData{}, - ) - planner := ip.(interface { - resolver.SchemaResolver - SemaCtx() *tree.SemaContext - EvalContext() *tree.EvalContext - Descriptors() *descs.Collection - scbuild.AuthorizationAccessor - }) - buildDeps := scbuild.Dependencies{ - Res: planner, - SemaCtx: planner.SemaCtx(), - EvalCtx: planner.EvalContext(), - Descs: planner.Descriptors(), - AuthAccessor: planner, - } - return &buildDeps, cleanup -} diff --git a/pkg/sql/schemachanger/scrun/BUILD.bazel b/pkg/sql/schemachanger/scrun/BUILD.bazel new file mode 100644 index 000000000000..f70c23f59739 --- /dev/null +++ b/pkg/sql/schemachanger/scrun/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "scrun", + srcs = [ + "dependencies.go", + "scrun.go", + ], + importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scrun", + visibility = ["//visibility:public"], + deps = [ + "//pkg/jobs", + "//pkg/jobs/jobspb", + "//pkg/security", + "//pkg/settings/cluster", + "//pkg/sql/catalog", + "//pkg/sql/catalog/descpb", + "//pkg/sql/schemachanger/scexec", + "//pkg/sql/schemachanger/scop", + "//pkg/sql/schemachanger/scpb", + "//pkg/sql/schemachanger/scplan", + "//pkg/sql/schemachanger/screl", + "//pkg/util/log/logcrash", + ], +) diff --git a/pkg/sql/schemachanger/scrun/dependencies.go b/pkg/sql/schemachanger/scrun/dependencies.go new file mode 100644 index 000000000000..363dcac54378 --- /dev/null +++ b/pkg/sql/schemachanger/scrun/dependencies.go @@ -0,0 +1,68 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scrun + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" +) + +// SchemaChangeJobCreationDependencies contains the dependencies required for +// creating a schema change job for the declarative schema changer. +type SchemaChangeJobCreationDependencies interface { + Catalog() scexec.Catalog + TransactionalJobCreator() scexec.TransactionalJobCreator + + // User returns the user to be associated with the schema change job. + User() security.SQLUsername + + // Statements returns the statements behind this schema change. + Statements() []string +} + +// SchemaChangeJobExecutionDependencies contains the dependencies required for +// executing the schema change job, i.e. for the logic in its Resume() method. +type SchemaChangeJobExecutionDependencies interface { + // WithTxnInJob is a wrapper for opening and committing a transaction around + // the execution of the callback. After committing the transaction, the job + // registry should be notified to adopt jobs. + WithTxnInJob(ctx context.Context, fn func(ctx context.Context, txndeps SchemaChangeJobTxnDependencies) error) error + + // ClusterSettings returns the current cluster settings. + ClusterSettings() *cluster.Settings +} + +// SchemaChangeJobTxnDependencies contains the dependencies required for +// executing a specific transaction in the schema change job execution. +type SchemaChangeJobTxnDependencies interface { + + // UpdateSchemaChangeJob triggers the update of the current schema change job + // via the supplied callback. + UpdateSchemaChangeJob(ctx context.Context, fn func(md jobs.JobMetadata, ju JobProgressUpdater) error) error + + // ExecutorDependencies constructs the executor dependencies for use within + // this transaction. + ExecutorDependencies() scexec.Dependencies +} + +// JobProgressUpdater is for updating the progress of the schema change job. +// The internals of the jobs.JobUpdater are private, but this interface allows +// for test implementations. +type JobProgressUpdater interface { + + // UpdateProgress is implemented by jobs.JobUpdater. + UpdateProgress(progress *jobspb.Progress) +} diff --git a/pkg/sql/schemachanger/scrun/scrun.go b/pkg/sql/schemachanger/scrun/scrun.go new file mode 100644 index 000000000000..a1e9b368d45a --- /dev/null +++ b/pkg/sql/schemachanger/scrun/scrun.go @@ -0,0 +1,195 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package scrun + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scexec" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scplan" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/screl" + "github.com/cockroachdb/cockroach/pkg/util/log/logcrash" +) + +// RunSchemaChangesInTxn executes in-transaction schema changes for the targeted +// state. These are the immediate changes which take place at DDL statement +// execution time (scop.StatementPhase) or when executing COMMIT +// (scop.PreCommitPhase), rather than the asynchronous changes which are done +// by the schema changer job after the transaction commits. +func RunSchemaChangesInTxn( + ctx context.Context, deps scexec.Dependencies, state scpb.State, +) (scpb.State, error) { + if len(state) == 0 { + return nil, nil + } + sc, err := scplan.MakePlan(state, scplan.Params{ + ExecutionPhase: deps.Phase(), + // TODO(ajwerner): Populate the set of new descriptors + }) + if err != nil { + return nil, err + } + after := state + for _, s := range sc.Stages { + if err := scexec.ExecuteOps(ctx, deps, s.Ops); err != nil { + return nil, err + } + after = s.After + } + if len(after) == 0 { + return nil, nil + } + return after, nil +} + +// CreateSchemaChangeJob builds and enqueues a schema change job for the target +// state at pre-COMMIT time. This also updates the affected descriptors with the +// id of the created job, effectively locking them to prevent any other schema +// changes concurrent to this job's execution. +func CreateSchemaChangeJob( + ctx context.Context, deps SchemaChangeJobCreationDependencies, state scpb.State, +) (jobspb.JobID, error) { + if len(state) == 0 { + return jobspb.InvalidJobID, nil + } + + targets := make([]*scpb.Target, len(state)) + states := make([]scpb.Status, len(state)) + // TODO(ajwerner): It may be better in the future to have the builder be + // responsible for determining this set of descriptors. As of the time of + // writing, the descriptors to be "locked," descriptors that need schema + // change jobs, and descriptors with schema change mutations all coincide. But + // there are future schema changes to be implemented in the new schema changer + // (e.g., RENAME TABLE) for which this may no longer be true. + descIDSet := catalog.MakeDescriptorIDSet() + for i := range state { + targets[i] = state[i].Target + states[i] = state[i].Status + // Depending on the element type either a single descriptor ID + // will exist or multiple (i.e. foreign keys). + if id := screl.GetDescID(state[i].Element()); id != descpb.InvalidID { + descIDSet.Add(id) + } + } + descIDs := descIDSet.Ordered() + jobID, err := deps.TransactionalJobCreator().CreateJob(ctx, jobs.Record{ + Description: "Schema change job", // TODO(ajwerner): use const + Statements: deps.Statements(), + Username: deps.User(), + DescriptorIDs: descIDs, + Details: jobspb.NewSchemaChangeDetails{Targets: targets}, + Progress: jobspb.NewSchemaChangeProgress{States: states}, + RunningStatus: "", + NonCancelable: false, + }) + if err != nil { + return jobspb.InvalidJobID, err + } + // Write the job ID to the affected descriptors. + if err := scexec.UpdateDescriptorJobIDs( + ctx, + deps.Catalog(), + descIDs, + jobspb.InvalidJobID, + jobID, + ); err != nil { + return jobID, err + } + return jobID, nil +} + +// RunSchemaChangesInJob contains the business logic for the Resume method of a +// declarative schema change job, with the dependencies abstracted away. +func RunSchemaChangesInJob( + ctx context.Context, + deps SchemaChangeJobExecutionDependencies, + jobID jobspb.JobID, + jobDescriptorIDs []descpb.ID, + jobDetails jobspb.NewSchemaChangeDetails, + jobProgress jobspb.NewSchemaChangeProgress, +) error { + state := makeState(ctx, deps.ClusterSettings(), jobDetails.Targets, jobProgress.States) + sc, err := scplan.MakePlan(state, scplan.Params{ExecutionPhase: scop.PostCommitPhase}) + if err != nil { + return err + } + + if len(sc.Stages) == 0 { + // In the case where no stage exists, and therefore there's nothing to + // execute, we still need to open a transaction to remove all references to + // this schema change job from the descriptors. + return deps.WithTxnInJob(ctx, func(ctx context.Context, td SchemaChangeJobTxnDependencies) error { + c := td.ExecutorDependencies().Catalog() + return scexec.UpdateDescriptorJobIDs(ctx, c, jobDescriptorIDs, jobID, jobspb.InvalidJobID) + }) + } + + for i, stage := range sc.Stages { + isLastStage := i == len(sc.Stages)-1 + // Execute each stage in its own transaction. + if err := deps.WithTxnInJob(ctx, func(ctx context.Context, td SchemaChangeJobTxnDependencies) error { + execDeps := td.ExecutorDependencies() + if err := scexec.ExecuteOps(ctx, execDeps, stage.Ops); err != nil { + return err + } + if err := td.UpdateSchemaChangeJob(ctx, func(md jobs.JobMetadata, ju JobProgressUpdater) error { + pg := md.Progress.GetNewSchemaChange() + pg.States = makeStatuses(stage.After) + ju.UpdateProgress(md.Progress) + return nil + }); err != nil { + return err + } + if isLastStage { + // Remove the reference to this schema change job from all affected + // descriptors in the transaction executing the last stage. + return scexec.UpdateDescriptorJobIDs(ctx, execDeps.Catalog(), jobDescriptorIDs, jobID, jobspb.InvalidJobID) + } + return nil + }); err != nil { + return err + } + } + + return nil +} + +func makeStatuses(next scpb.State) []scpb.Status { + states := make([]scpb.Status, len(next)) + for i := range next { + states[i] = next[i].Status + } + return states +} + +func makeState( + ctx context.Context, sv *cluster.Settings, protos []*scpb.Target, states []scpb.Status, +) scpb.State { + if len(protos) != len(states) { + logcrash.ReportOrPanic(ctx, &sv.SV, "unexpected slice size mismatch %d and %d", + len(protos), len(states)) + } + ts := make(scpb.State, len(protos)) + for i := range protos { + ts[i] = &scpb.Node{ + Target: protos[i], + Status: states[i], + } + } + return ts +} From 0fe8454e3a825795001c6e17e8f966af8388168f Mon Sep 17 00:00:00 2001 From: Raphael 'kena' Poss Date: Thu, 28 Oct 2021 18:58:45 +0200 Subject: [PATCH 093/205] deps: bump elastic/gosigar to v0.14.1 This adds support for macOS M1 variants and fixes a couple of bugs. Release note: None --- DEPS.bzl | 8 ++++---- go.mod | 4 ++-- go.sum | 8 +++++--- vendor | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/DEPS.bzl b/DEPS.bzl index 676b648578c6..6f86e218c2ae 100644 --- a/DEPS.bzl +++ b/DEPS.bzl @@ -1526,8 +1526,8 @@ def go_deps(): name = "com_github_elastic_gosigar", build_file_proto_mode = "disable_global", importpath = "github.com/elastic/gosigar", - sum = "h1:bPIzW1Qkut7n9uwvPAXbnLDVEd45TV5ZwxYZAVX/zEQ=", - version = "v0.10.0", + sum = "h1:T0aQ7n/n2ZA9W7DmAnj60v+qzqKERdBgJBO1CG2W6rc=", + version = "v0.14.1", ) go_repository( @@ -6239,8 +6239,8 @@ def go_deps(): name = "org_golang_x_sys", build_file_proto_mode = "disable_global", importpath = "golang.org/x/sys", - sum = "h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk=", - version = "v0.0.0-20211015200801-69063c4bb744", + sum = "h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=", + version = "v0.0.0-20211025201205-69cdffdb9359", ) go_repository( name = "org_golang_x_term", diff --git a/go.mod b/go.mod index e88b47a7f61a..59b16b06bb32 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/dustin/go-humanize v1.0.0 github.com/edsrzf/mmap-go v1.0.0 - github.com/elastic/gosigar v0.10.0 + github.com/elastic/gosigar v0.14.1 github.com/emicklei/dot v0.15.0 github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 @@ -157,7 +157,7 @@ require ( golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 + golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d golang.org/x/text v0.3.7 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac diff --git a/go.sum b/go.sum index 96dacdb5bb5a..1a5e04cddfb4 100644 --- a/go.sum +++ b/go.sum @@ -598,8 +598,8 @@ github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7j github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/elastic/gosigar v0.10.0 h1:bPIzW1Qkut7n9uwvPAXbnLDVEd45TV5ZwxYZAVX/zEQ= -github.com/elastic/gosigar v0.10.0/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/elastic/gosigar v0.14.1 h1:T0aQ7n/n2ZA9W7DmAnj60v+qzqKERdBgJBO1CG2W6rc= +github.com/elastic/gosigar v0.14.1/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/dot v0.15.0 h1:XDBW0Xco1QNyRb33cqLe10cT04yMWL1XpCZfa98Q6Og= @@ -2231,6 +2231,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2351,8 +2352,9 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk= golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= diff --git a/vendor b/vendor index ea98f7ec407d..cd95d58038ce 160000 --- a/vendor +++ b/vendor @@ -1 +1 @@ -Subproject commit ea98f7ec407d5c6cfda7f883b90adf27603422cf +Subproject commit cd95d58038ce94c8a855b9ad4ded813751f84cd9 From b34f03a64b2e1313e61a2a095dfc67ce8e7f6803 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 16:04:27 -0400 Subject: [PATCH 094/205] roachpb: fix a stray comment Release note: None --- pkg/roachpb/span_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/roachpb/span_config.go b/pkg/roachpb/span_config.go index f2667b02fea2..209149ccc1e5 100644 --- a/pkg/roachpb/span_config.go +++ b/pkg/roachpb/span_config.go @@ -105,7 +105,7 @@ func TestingDefaultSpanConfig() SpanConfig { // default. Users desiring to take incremental backups every 24h may // incorrectly assume that the previous default 24h was sufficient to do // that. But the equation for incremental backups is: - // GC TTLSeconds >= (desired backup interval) (time to perform incremental backup) + // GC TTLSeconds >= (desired backup interval) + (time to perform incremental backup) // We think most new users' incremental backups will complete within an // hour, and larger clusters will have more experienced operators and will // understand how to change these settings if needed. From c955a484c655c05b7c484e20bb56c661c389ee45 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 16:05:35 -0400 Subject: [PATCH 095/205] spanconfig: use consistent naming scheme Re-name only commit. Release note: None --- pkg/spanconfig/spanconfigsqltranslator/BUILD.bazel | 4 ++-- .../{sql_translator.go => sqltranslator.go} | 0 .../{sql_translator_test.go => sqltranslator_test.go} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename pkg/spanconfig/spanconfigsqltranslator/{sql_translator.go => sqltranslator.go} (100%) rename pkg/spanconfig/spanconfigsqltranslator/{sql_translator_test.go => sqltranslator_test.go} (100%) diff --git a/pkg/spanconfig/spanconfigsqltranslator/BUILD.bazel b/pkg/spanconfig/spanconfigsqltranslator/BUILD.bazel index e21539ea3512..5cb708f9738f 100644 --- a/pkg/spanconfig/spanconfigsqltranslator/BUILD.bazel +++ b/pkg/spanconfig/spanconfigsqltranslator/BUILD.bazel @@ -2,7 +2,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "spanconfigsqltranslator", - srcs = ["sql_translator.go"], + srcs = ["sqltranslator.go"], importpath = "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigsqltranslator", visibility = ["//visibility:public"], deps = [ @@ -23,5 +23,5 @@ go_library( go_test( name = "spanconfigsqltranslator_test", - srcs = ["sql_translator_test.go"], + srcs = ["sqltranslator_test.go"], ) diff --git a/pkg/spanconfig/spanconfigsqltranslator/sql_translator.go b/pkg/spanconfig/spanconfigsqltranslator/sqltranslator.go similarity index 100% rename from pkg/spanconfig/spanconfigsqltranslator/sql_translator.go rename to pkg/spanconfig/spanconfigsqltranslator/sqltranslator.go diff --git a/pkg/spanconfig/spanconfigsqltranslator/sql_translator_test.go b/pkg/spanconfig/spanconfigsqltranslator/sqltranslator_test.go similarity index 100% rename from pkg/spanconfig/spanconfigsqltranslator/sql_translator_test.go rename to pkg/spanconfig/spanconfigsqltranslator/sqltranslator_test.go From 9ad52431ec32dfd1124cfbc3b186b9a280f3c7dd Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 16:08:19 -0400 Subject: [PATCH 096/205] spanconfig: extract testing helpers kvaccessor into shared package We'll use it in a future commit. Also rename a field while here. Release note: None --- .../spanconfigkvaccessor/BUILD.bazel | 3 +- .../spanconfigkvaccessor/kvaccessor.go | 46 ++++++++------ ...{datadriven_test.go => kvaccessor_test.go} | 43 ++----------- pkg/spanconfig/spanconfigtestutils/utils.go | 61 +++++++++++++++++++ 4 files changed, 95 insertions(+), 58 deletions(-) rename pkg/spanconfig/spanconfigkvaccessor/{datadriven_test.go => kvaccessor_test.go} (69%) diff --git a/pkg/spanconfig/spanconfigkvaccessor/BUILD.bazel b/pkg/spanconfig/spanconfigkvaccessor/BUILD.bazel index 57099ddf444c..d2980b2f737f 100644 --- a/pkg/spanconfig/spanconfigkvaccessor/BUILD.bazel +++ b/pkg/spanconfig/spanconfigkvaccessor/BUILD.bazel @@ -15,6 +15,7 @@ go_library( "//pkg/settings", "//pkg/settings/cluster", "//pkg/spanconfig", + "//pkg/sql/parser", "//pkg/sql/sem/tree", "//pkg/sql/sessiondata", "//pkg/sql/sqlutil", @@ -26,7 +27,7 @@ go_library( go_test( name = "spanconfigkvaccessor_test", srcs = [ - "datadriven_test.go", + "kvaccessor_test.go", "main_test.go", "validation_test.go", ], diff --git a/pkg/spanconfig/spanconfigkvaccessor/kvaccessor.go b/pkg/spanconfig/spanconfigkvaccessor/kvaccessor.go index edb2f22a2589..f51684700aa1 100644 --- a/pkg/spanconfig/spanconfigkvaccessor/kvaccessor.go +++ b/pkg/spanconfig/spanconfigkvaccessor/kvaccessor.go @@ -22,6 +22,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/spanconfig" + "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" @@ -32,23 +33,32 @@ import ( // KVAccessor provides read/write access to all the span configurations for a // CRDB cluster. It's a concrete implementation of the KVAccessor interface. type KVAccessor struct { - db *kv.DB - ie sqlutil.InternalExecutor - settings *cluster.Settings - tableName string // typically system.span_configurations, but overridable for testing purposes + db *kv.DB + ie sqlutil.InternalExecutor + settings *cluster.Settings + + // configurationsTableFQN is typically + // 'system.public.span_configurations', but left configurable + // ease-of-testing. + configurationsTableFQN string } var _ spanconfig.KVAccessor = &KVAccessor{} -// New constructs a new Manager. +// New constructs a new KVAccessor. func New( - db *kv.DB, ie sqlutil.InternalExecutor, settings *cluster.Settings, tableFQN string, + db *kv.DB, ie sqlutil.InternalExecutor, settings *cluster.Settings, configurationsTableFQN string, ) *KVAccessor { + if _, err := parser.ParseQualifiedTableName(configurationsTableFQN); err != nil { + panic(fmt.Sprintf("unabled to parse configurations table FQN: %s", configurationsTableFQN)) + } + return &KVAccessor{ - db: db, - ie: ie, - settings: settings, - tableName: tableFQN, + db: db, + ie: ie, + settings: settings, + + configurationsTableFQN: configurationsTableFQN, } } @@ -248,9 +258,9 @@ SELECT start_key, end_key, config FROM ( WHERE start_key < $%[2]d ORDER BY start_key DESC LIMIT 1 ) WHERE end_key > $%[2]d `, - k.tableName, // [1] - startKeyIdx+1, // [2] -- prepared statement placeholder (1-indexed) - endKeyIdx+1, // [3] -- prepared statement placeholder (1-indexed) + k.configurationsTableFQN, // [1] + startKeyIdx+1, // [2] -- prepared statement placeholder (1-indexed) + endKeyIdx+1, // [3] -- prepared statement placeholder (1-indexed) ) } return getStmtBuilder.String(), queryArgs @@ -275,7 +285,7 @@ func (k *KVAccessor) constructDeleteStmtAndArgs(toDelete []roachpb.Span) (string startKeyIdx+1, endKeyIdx+1) // prepared statement placeholders (1-indexed) } deleteStmt := fmt.Sprintf(`DELETE FROM %[1]s WHERE (start_key, end_key) IN (VALUES %[2]s)`, - k.tableName, strings.Join(values, ", ")) + k.configurationsTableFQN, strings.Join(values, ", ")) return deleteStmt, deleteQueryArgs } @@ -306,7 +316,7 @@ func (k *KVAccessor) constructUpsertStmtAndArgs( startKeyIdx+1, endKeyIdx+1, configIdx+1) // prepared statement placeholders (1-indexed) } upsertStmt := fmt.Sprintf(`UPSERT INTO %[1]s (start_key, end_key, config) VALUES %[2]s`, - k.tableName, strings.Join(upsertValues, ", ")) + k.configurationsTableFQN, strings.Join(upsertValues, ", ")) return upsertStmt, upsertQueryArgs, nil } @@ -369,9 +379,9 @@ SELECT count(*) = 1 FROM ( ) WHERE end_key > $%[2]d ) `, - k.tableName, // [1] - startKeyIdx+1, // [2] -- prepared statement placeholder (1-indexed) - endKeyIdx+1, // [3] -- prepared statement placeholder (1-indexed) + k.configurationsTableFQN, // [1] + startKeyIdx+1, // [2] -- prepared statement placeholder (1-indexed) + endKeyIdx+1, // [3] -- prepared statement placeholder (1-indexed) ) } validationStmt := fmt.Sprintf("SELECT true = ALL(%s)", validationInnerStmtBuilder.String()) diff --git a/pkg/spanconfig/spanconfigkvaccessor/datadriven_test.go b/pkg/spanconfig/spanconfigkvaccessor/kvaccessor_test.go similarity index 69% rename from pkg/spanconfig/spanconfigkvaccessor/datadriven_test.go rename to pkg/spanconfig/spanconfigkvaccessor/kvaccessor_test.go index 2a4bc7833bb7..55c0bc1a1831 100644 --- a/pkg/spanconfig/spanconfigkvaccessor/datadriven_test.go +++ b/pkg/spanconfig/spanconfigkvaccessor/kvaccessor_test.go @@ -17,10 +17,10 @@ import ( "testing" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvaccessor" "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigtestutils" "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" + "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" "github.com/cockroachdb/cockroach/pkg/util/leaktest" @@ -51,7 +51,7 @@ import ( func TestDataDriven(t *testing.T) { defer leaktest.AfterTest(t)() - datadriven.Walk(t, "testdata", func(t *testing.T, path string) { + datadriven.Walk(t, testutils.TestDataPath(t), func(t *testing.T, path string) { ctx := context.Background() tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{ ServerArgs: base.TestServerArgs{ @@ -74,21 +74,7 @@ func TestDataDriven(t *testing.T) { datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { switch d.Cmd { case "kvaccessor-get": - var spans []roachpb.Span - for _, line := range strings.Split(d.Input, "\n") { - line = strings.TrimSpace(line) - if line == "" { - continue - } - - const spanPrefix = "span " - if !strings.HasPrefix(line, spanPrefix) { - t.Fatalf("malformed line %q, expected to find spanPrefix %q", line, spanPrefix) - } - line = strings.TrimPrefix(line, spanPrefix) - spans = append(spans, spanconfigtestutils.ParseSpan(t, line)) - } - + spans := spanconfigtestutils.ParseKVAccessorGetArguments(t, d.Input) entries, err := accessor.GetSpanConfigEntriesFor(ctx, spans) if err != nil { return fmt.Sprintf("err: %s", err.Error()) @@ -100,28 +86,7 @@ func TestDataDriven(t *testing.T) { } return output.String() case "kvaccessor-update": - var toDelete []roachpb.Span - var toUpsert []roachpb.SpanConfigEntry - for _, line := range strings.Split(d.Input, "\n") { - line = strings.TrimSpace(line) - if line == "" { - continue - } - - const upsertPrefix, deletePrefix = "upsert ", "delete " - if !strings.HasPrefix(line, upsertPrefix) && !strings.HasPrefix(line, deletePrefix) { - t.Fatalf("malformed line %q, expected to find prefix %q or %q", - line, upsertPrefix, deletePrefix) - } - - if strings.HasPrefix(line, deletePrefix) { - line = strings.TrimPrefix(line, line[:len(deletePrefix)]) - toDelete = append(toDelete, spanconfigtestutils.ParseSpan(t, line)) - } else { - line = strings.TrimPrefix(line, line[:len(upsertPrefix)]) - toUpsert = append(toUpsert, spanconfigtestutils.ParseSpanConfigEntry(t, line)) - } - } + toDelete, toUpsert := spanconfigtestutils.ParseKVAccessorUpdateArguments(t, d.Input) if err := accessor.UpdateSpanConfigEntries(ctx, toDelete, toUpsert); err != nil { return fmt.Sprintf("err: %s", err.Error()) } diff --git a/pkg/spanconfig/spanconfigtestutils/utils.go b/pkg/spanconfig/spanconfigtestutils/utils.go index 596ef2b7e1b0..314010602986 100644 --- a/pkg/spanconfig/spanconfigtestutils/utils.go +++ b/pkg/spanconfig/spanconfigtestutils/utils.go @@ -76,6 +76,67 @@ func ParseSpanConfigEntry(t *testing.T, conf string) roachpb.SpanConfigEntry { } } +// ParseKVAccessorGetArguments is a helper function that parses datadriven +// kvaccessor-get arguments into the relevant spans. The input is of the +// following form: +// +// span [a,e) +// span [a,b) +// span [b,c) +// +func ParseKVAccessorGetArguments(t *testing.T, input string) []roachpb.Span { + var spans []roachpb.Span + for _, line := range strings.Split(input, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + + const spanPrefix = "span " + if !strings.HasPrefix(line, spanPrefix) { + t.Fatalf("malformed line %q, expected to find spanPrefix %q", line, spanPrefix) + } + line = strings.TrimPrefix(line, spanPrefix) + spans = append(spans, ParseSpan(t, line)) + } + return spans +} + +// ParseKVAccessorUpdateArguments is a helper function that parses datadriven +// kvaccessor-update arguments into the relevant spans. The input is of the +// following form: +// +// delete [c,e) +// upsert [c,d):C +// upsert [d,e):D +// +func ParseKVAccessorUpdateArguments( + t *testing.T, input string, +) ([]roachpb.Span, []roachpb.SpanConfigEntry) { + var toDelete []roachpb.Span + var toUpsert []roachpb.SpanConfigEntry + for _, line := range strings.Split(input, "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + + const upsertPrefix, deletePrefix = "upsert ", "delete " + switch { + case strings.HasPrefix(line, deletePrefix): + line = strings.TrimPrefix(line, line[:len(deletePrefix)]) + toDelete = append(toDelete, ParseSpan(t, line)) + case strings.HasPrefix(line, upsertPrefix): + line = strings.TrimPrefix(line, line[:len(upsertPrefix)]) + toUpsert = append(toUpsert, ParseSpanConfigEntry(t, line)) + default: + t.Fatalf("malformed line %q, expected to find prefix %q or %q", + line, upsertPrefix, deletePrefix) + } + } + return toDelete, toUpsert +} + // PrintSpan is a helper function that transforms roachpb.Span into a string of // the form "[start,end)". The span is assumed to have been constructed by the // ParseSpan helper above. From 82f6f2e548dfff704a09b4d51472d26c1f2156f6 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 16:09:39 -0400 Subject: [PATCH 097/205] store: remove dependency on syscfgspan in store metrics It's dead code; was just a convenient way of avoiding test log spam way back in #16354. Release note: None --- pkg/kv/kvserver/store.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/kv/kvserver/store.go b/pkg/kv/kvserver/store.go index 5227575f9b12..c3019eb04ab1 100644 --- a/pkg/kv/kvserver/store.go +++ b/pkg/kv/kvserver/store.go @@ -2531,12 +2531,6 @@ func (s *Store) RangeFeed( // scanning ranges. An ideal solution would be to create incremental events // whenever availability changes. func (s *Store) updateReplicationGauges(ctx context.Context) error { - // Load the system config. - cfg := s.Gossip().GetSystemConfig() - if cfg == nil { - return errors.Errorf("%s: system config not yet available", s) - } - var ( raftLeaderCount int64 leaseHolderCount int64 From fde4604efce3c6c2b471b5a494cddcdf124867b0 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 16:12:44 -0400 Subject: [PATCH 098/205] rangefeed: improve a comment Release note: None --- pkg/kv/kvclient/rangefeed/rangefeed.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/kv/kvclient/rangefeed/rangefeed.go b/pkg/kv/kvclient/rangefeed/rangefeed.go index f2b728d5e7eb..0ef98e9281ef 100644 --- a/pkg/kv/kvclient/rangefeed/rangefeed.go +++ b/pkg/kv/kvclient/rangefeed/rangefeed.go @@ -193,8 +193,9 @@ func (f *RangeFeed) Start(ctx context.Context) error { return nil } -// Close closes the RangeFeed and waits for it to shut down. -// Close is idempotent. +// Close closes the RangeFeed and waits for it to shut down; it does +// idempotently. It's guaranteed that no future handlers will be invoked after +// this point. func (f *RangeFeed) Close() { f.closeOnce.Do(func() { f.cancel() From c485cc5c7477315408173dcf3f1f5cbe0456f7c8 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 18:50:29 -0400 Subject: [PATCH 099/205] dev: pad internal timeouts when under --stress --timeout I was seeing the earlier one second buffer being insufficient. Release note: None --- pkg/cmd/dev/test.go | 6 +++--- pkg/cmd/dev/testdata/recording/test.txt | 2 +- pkg/cmd/dev/testdata/test.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/dev/test.go b/pkg/cmd/dev/test.go index 283d8b34093d..738608203ce3 100644 --- a/pkg/cmd/dev/test.go +++ b/pkg/cmd/dev/test.go @@ -180,9 +180,9 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { args = append(args, "--run_under", fmt.Sprintf("%s -maxtime=%s %s", stressTarget, timeout, stressArgs)) - // The bazel timeout needs to be higher than the stress duration to - // pass reliably. - args = append(args, fmt.Sprintf("--test_timeout=%.0f", (timeout+time.Second).Seconds())) + // The timeout should be higher than the stress duration, lets + // generously give it an extra minute. + args = append(args, fmt.Sprintf("--test_timeout=%d", int((timeout+time.Minute).Seconds()))) } else { // We're running under stress and no timeout is specified. We want // to respect the timeout passed down to stress[1]. Similar to above diff --git a/pkg/cmd/dev/testdata/recording/test.txt b/pkg/cmd/dev/testdata/recording/test.txt index 6f438e02163a..51ea3d0f69ab 100644 --- a/pkg/cmd/dev/testdata/recording/test.txt +++ b/pkg/cmd/dev/testdata/recording/test.txt @@ -97,7 +97,7 @@ bazel query 'kind(go_test, //pkg/util/tracing:all)' ---- //pkg/util/tracing:tracing_test -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' --test_timeout=11 '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' --test_timeout=70 '--test_filter=TestStartChild*' --test_output streamed ---- ---- ==================== Test output for //pkg/util/tracing:tracing_test: diff --git a/pkg/cmd/dev/testdata/test.txt b/pkg/cmd/dev/testdata/test.txt index 492431d6ace6..1fb3685efab0 100644 --- a/pkg/cmd/dev/testdata/test.txt +++ b/pkg/cmd/dev/testdata/test.txt @@ -36,7 +36,7 @@ bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --r dev test --stress pkg/util/tracing --filter TestStartChild* --timeout=10s -v ---- bazel query 'kind(go_test, //pkg/util/tracing:all)' -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' --test_timeout=11 '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' --test_timeout=70 '--test_filter=TestStartChild*' --test_output streamed dev test //pkg/testutils --timeout=10s ---- From 048eda9aa78c98f7c107829527e0c317757ab125 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Thu, 28 Oct 2021 17:09:25 -0700 Subject: [PATCH 100/205] coldata: fix BenchmarkAppend so that it could be run with multiple count Previously, we could get an index out of bounds because we'd allocate larger `Bytes.data` slice than `int32` offset can support. Release note: None --- pkg/col/coldata/vec_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/col/coldata/vec_test.go b/pkg/col/coldata/vec_test.go index df5016d33bb9..7c9b6abaa04c 100644 --- a/pkg/col/coldata/vec_test.go +++ b/pkg/col/coldata/vec_test.go @@ -429,10 +429,10 @@ func BenchmarkAppend(b *testing.B) { for _, bc := range benchCases { bc.args.Src = src bc.args.SrcEndIdx = coldata.BatchSize() - dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) b.Run(fmt.Sprintf("%s/%s/NullProbability=%.1f", typ, bc.name, nullProbability), func(b *testing.B) { b.SetBytes(8 * int64(coldata.BatchSize())) bc.args.DestIdx = 0 + dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory) for i := 0; i < b.N; i++ { dest.Append(bc.args) bc.args.DestIdx += coldata.BatchSize() From 0355fa5edd6102457d63b46129cdfc6d69739110 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Fri, 15 Oct 2021 18:47:20 -0700 Subject: [PATCH 101/205] colexecjoin: make cross/merge join streaming with regards to left input This commit refactors the cross and merge join to be streaming with regards to the left input. Previously, we were using two spilling queues to consume both inputs first before proceeding to building the cross product (in case of the merge join this is needed when building from the buffered group). That approach is suboptimal because buffering only one side is sufficient, so this commit switches the cross join builder to operate in a streaming fashion with regards to the left input. This is done by building all result rows that correspond to the current left batch before proceeding to the next left batch and allows us to significantly reduce amount of copying and, thus, improving the performance. Release note: None --- pkg/sql/colexec/colexecjoin/crossjoiner.eg.go | 2505 ++++++++--------- pkg/sql/colexec/colexecjoin/crossjoiner.go | 406 +-- .../colexec/colexecjoin/crossjoiner_tmpl.go | 339 +-- .../colexec/colexecjoin/mergejoinbase.eg.go | 52 +- .../colexec/colexecjoin/mergejoinbase_tmpl.go | 12 +- pkg/sql/colexec/colexecjoin/mergejoiner.go | 554 ++-- .../colexecjoin/mergejoiner_exceptall.eg.go | 993 ++++--- .../colexecjoin/mergejoiner_fullouter.eg.go | 967 ++++--- .../colexecjoin/mergejoiner_inner.eg.go | 958 ++++--- .../mergejoiner_intersectall.eg.go | 962 ++++--- .../colexecjoin/mergejoiner_leftanti.eg.go | 959 ++++--- .../colexecjoin/mergejoiner_leftouter.eg.go | 963 ++++--- .../colexecjoin/mergejoiner_leftsemi.eg.go | 954 ++++--- .../colexecjoin/mergejoiner_rightanti.eg.go | 955 ++++--- .../colexecjoin/mergejoiner_rightouter.eg.go | 963 ++++--- .../colexecjoin/mergejoiner_rightsemi.eg.go | 955 ++++--- .../colexec/colexecjoin/mergejoiner_tmpl.go | 356 ++- pkg/sql/colexec/crossjoiner_test.go | 30 +- pkg/sql/colexec/mergejoiner_test.go | 27 + .../logictest/testdata/logic_test/cross_join | 84 + .../exec/execbuilder/testdata/vectorize_local | 25 + 21 files changed, 7393 insertions(+), 6626 deletions(-) create mode 100644 pkg/sql/logictest/testdata/logic_test/cross_join diff --git a/pkg/sql/colexec/colexecjoin/crossjoiner.eg.go b/pkg/sql/colexec/colexecjoin/crossjoiner.eg.go index 85fd93c7462d..7514686a8d94 100644 --- a/pkg/sql/colexec/colexecjoin/crossjoiner.eg.go +++ b/pkg/sql/colexec/colexecjoin/crossjoiner.eg.go @@ -26,1386 +26,1299 @@ var ( _ = types.BoolFamily ) +// buildFromLeftBatch is the body of buildFromLeftInput that templates out the +// fact whether the current left batch has a selection vector or not. +// execgen:inline +const _ = "template_buildFromLeftBatch" + // buildFromLeftInput builds part of the output of a cross join that comes from // the vectors of the left input. The new output tuples are put starting at // index destStartIdx and will not exceed the capacity of the output batch. It -// is assumed that setupBuilder has been called. +// is assumed that setupLeftBuilder and prepareForNextLeftBatch have been +// called. // The goal of this method is to repeat each tuple from the left input -// leftNumRepeats times. For set-operation joins only first setOpLeftSrcIdx -// tuples are built from. +// leftNumRepeats times. Only the tuples in [curSrcStartIdx, leftSrcEndIdx) are +// used from the current left batch. func (b *crossJoinerBase) buildFromLeftInput(ctx context.Context, destStartIdx int) { - var err error currentBatch := b.builderState.left.currentBatch - if currentBatch == nil || b.builderState.left.curSrcStartIdx == currentBatch.Length() { - // We need to get the next batch to build from if it is the first one or - // we have fully processed the previous one. - currentBatch, err = b.left.tuples.Dequeue(ctx) - if err != nil { - colexecerror.InternalError(err) - } - b.builderState.left.currentBatch = currentBatch - b.builderState.left.curSrcStartIdx = 0 - b.builderState.left.numRepeatsIdx = 0 - } - initialBuilderState := b.builderState.left b.left.unlimitedAllocator.PerformOperation( b.output.ColVecs()[:len(b.left.types)], func() { - leftNumRepeats := b.builderState.setup.leftNumRepeats - isSetOp := b.joinType.IsSetOpJoin() - outputCapacity := b.output.Capacity() - batchLength := currentBatch.Length() - for batchLength > 0 { - // Loop over every column. - LeftColLoop: - for colIdx := range b.left.types { - outStartIdx := destStartIdx - src := currentBatch.ColVec(colIdx) - srcNulls := src.Nulls() - out := b.output.ColVec(colIdx) - outNulls := out.Nulls() - switch b.left.canonicalTypeFamilies[colIdx] { - case types.BoolFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Bool() - outCol := out.Bool() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + if sel := currentBatch.Selection(); sel != nil { + { + // We'll be modifying the builder state as we go, but we'll need to be able + // to restore the state to initial for each column. + initialBuilderState := b.builderState.left + bs := &b.builderState.left + leftNumRepeats := b.builderState.setup.leftNumRepeats + leftSrcEndIdx := b.builderState.setup.leftSrcEndIdx + outputCapacity := b.output.Capacity() + var srcStartIdx int + // Loop over every column. + for colIdx := range b.left.types { + if colIdx > 0 { + // Restore the builder state so that this new column started off + // fresh. + *bs = initialBuilderState + } + outStartIdx := destStartIdx + src := currentBatch.ColVec(colIdx) + srcNulls := src.Nulls() + out := b.output.ColVec(colIdx) + outNulls := out.Nulls() + switch b.left.canonicalTypeFamilies[colIdx] { + case types.BoolFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Bool() + outCol := out.Bool() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + outStartIdx++ + bs.curSrcStartIdx++ } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.BytesFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Bytes() + outCol := out.Bytes() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - } - case types.BytesFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Bytes() - outCol := out.Bytes() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.DecimalFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Decimal() + outCol := out.Decimal() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) + outStartIdx++ + bs.curSrcStartIdx++ } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.IntFamily: + switch b.left.types[colIdx].Width() { + case 16: + srcCol := src.Int16() + outCol := out.Int16() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 - } - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 - } - } - } - case types.DecimalFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Decimal() - outCol := out.Decimal() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + case 32: + srcCol := src.Int32() + outCol := out.Int32() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) + } + outStartIdx++ + bs.curSrcStartIdx++ } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + case -1: + default: + srcCol := src.Int64() + outCol := out.Int64() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - } - case types.IntFamily: - switch b.left.types[colIdx].Width() { - case 16: - srcCol := src.Int16() - outCol := out.Int16() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.FloatFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Float64() + outCol := out.Float64() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + outStartIdx++ + bs.curSrcStartIdx++ } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.TimestampTZFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Timestamp() + outCol := out.Timestamp() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - case 32: - srcCol := src.Int32() - outCol := out.Int32() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.IntervalFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Interval() + outCol := out.Interval() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ + outStartIdx++ + bs.curSrcStartIdx++ } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.JsonFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.JSON() + outCol := out.JSON() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - case -1: - default: - srcCol := src.Int64() - outCol := out.Int64() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case typeconv.DatumVecCanonicalTypeFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Datum() + outCol := out.Datum() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) + outStartIdx++ + bs.curSrcStartIdx++ } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = sel[bs.curSrcStartIdx] + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + default: + colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", b.left.types[colIdx].String())) + } + } + } + } else { + { + var sel []int = nil + // Remove the unused warning. + _ = sel + // We'll be modifying the builder state as we go, but we'll need to be able + // to restore the state to initial for each column. + initialBuilderState := b.builderState.left + bs := &b.builderState.left + leftNumRepeats := b.builderState.setup.leftNumRepeats + leftSrcEndIdx := b.builderState.setup.leftSrcEndIdx + outputCapacity := b.output.Capacity() + var srcStartIdx int + // Loop over every column. + for colIdx := range b.left.types { + if colIdx > 0 { + // Restore the builder state so that this new column started off + // fresh. + *bs = initialBuilderState + } + outStartIdx := destStartIdx + src := currentBatch.ColVec(colIdx) + srcNulls := src.Nulls() + out := b.output.ColVec(colIdx) + outNulls := out.Nulls() + switch b.left.canonicalTypeFamilies[colIdx] { + case types.BoolFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Bool() + outCol := out.Bool() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - } - case types.FloatFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Float64() - outCol := out.Float64() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.BytesFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Bytes() + outCol := out.Bytes() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + outStartIdx++ + bs.curSrcStartIdx++ } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.DecimalFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Decimal() + outCol := out.Decimal() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - } - case types.TimestampTZFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Timestamp() - outCol := out.Timestamp() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.IntFamily: + switch b.left.types[colIdx].Width() { + case 16: + srcCol := src.Int16() + outCol := out.Int16() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ + outStartIdx++ + bs.curSrcStartIdx++ } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + case 32: + srcCol := src.Int32() + outCol := out.Int32() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 - } - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 - } - } - } - case types.IntervalFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Interval() - outCol := out.Interval() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + case -1: + default: + srcCol := src.Int64() + outCol := out.Int64() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) + } + outStartIdx++ + bs.curSrcStartIdx++ } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.FloatFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Float64() + outCol := out.Float64() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - } - case types.JsonFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.JSON() - outCol := out.JSON() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.TimestampTZFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Timestamp() + outCol := out.Timestamp() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + outStartIdx++ + bs.curSrcStartIdx++ } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case types.IntervalFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Interval() + outCol := out.Interval() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } - } - case typeconv.DatumVecCanonicalTypeFamily: - switch b.left.types[colIdx].Width() { - case -1: - default: - srcCol := src.Datum() - outCol := out.Datum() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop + case types.JsonFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.JSON() + outCol := out.JSON() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) + outStartIdx++ + bs.curSrcStartIdx++ } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - b.builderState.left.setOpLeftSrcIdx += toAppend } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { + } + } + case typeconv.DatumVecCanonicalTypeFamily: + switch b.left.types[colIdx].Width() { + case -1: + default: + srcCol := src.Datum() + outCol := out.Datum() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + _ = true + srcStartIdx = bs.curSrcStartIdx + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) outCol.Set(outStartIdx, val) - outStartIdx++ } + outStartIdx++ + bs.curSrcStartIdx++ } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + _ = true + srcStartIdx = bs.curSrcStartIdx + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ } - return } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 } } + default: + colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", b.left.types[colIdx].String())) } - default: - colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", b.left.types[colIdx].String())) } - if colIdx == len(b.left.types)-1 { - // We have appended some tuples into the output batch from the current - // batch (the latter is now fully processed), so we need to adjust - // destStartIdx accordingly for the next batch. - destStartIdx = outStartIdx - } else { - b.builderState.left = initialBuilderState - } - } - // We have processed all tuples in the current batch from the - // buffered group, so we need to Dequeue the next one. - currentBatch, err = b.left.tuples.Dequeue(ctx) - if err != nil { - colexecerror.InternalError(err) } - b.builderState.left.currentBatch = currentBatch - batchLength = currentBatch.Length() - // We have transitioned to building from a new batch, so we - // need to update the builder state to build from the beginning - // of the new batch. - b.builderState.left.curSrcStartIdx = 0 - b.builderState.left.numRepeatsIdx = 0 - // We also need to update 'initialBuilderState' so that the - // builder state gets reset correctly in-between different - // columns in the loop above. - initialBuilderState = b.builderState.left } }, ) @@ -1414,30 +1327,34 @@ func (b *crossJoinerBase) buildFromLeftInput(ctx context.Context, destStartIdx i // buildFromRightInput builds part of the output of a cross join that comes from // the vectors of the right input. The new output tuples are put starting at // index destStartIdx and will not exceed the capacity of the output batch. It -// is assumed that setupBuilder has been called. +// is assumed that the right input has been fully consumed and is stored in +// b.rightTuples spilling queue. // The goal of this method is to repeat all tuples from the right input // rightNumRepeats times (i.e. repeating the whole list of tuples at once). func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx int) { var err error + bs := &b.builderState.right + rightNumRepeats := b.builderState.setup.rightNumRepeats b.right.unlimitedAllocator.PerformOperation( b.output.ColVecs()[b.builderState.rightColOffset:], func() { outStartIdx := destStartIdx outputCapacity := b.output.Capacity() - // Repeat the buffered tuples rightNumRepeats times. - for ; b.builderState.right.numRepeatsIdx < b.builderState.setup.rightNumRepeats; b.builderState.right.numRepeatsIdx++ { - currentBatch := b.builderState.right.currentBatch + // Repeat the buffered tuples rightNumRepeats times until we fill + // the output capacity. + for ; outStartIdx < outputCapacity && bs.numRepeatsIdx < rightNumRepeats; bs.numRepeatsIdx++ { + currentBatch := bs.currentBatch if currentBatch == nil { - currentBatch, err = b.right.tuples.Dequeue(ctx) + currentBatch, err = b.rightTuples.Dequeue(ctx) if err != nil { colexecerror.InternalError(err) } - b.builderState.right.currentBatch = currentBatch - b.builderState.right.curSrcStartIdx = 0 + bs.currentBatch = currentBatch + bs.curSrcStartIdx = 0 } batchLength := currentBatch.Length() for batchLength > 0 { - toAppend := batchLength - b.builderState.right.curSrcStartIdx + toAppend := batchLength - bs.curSrcStartIdx if outStartIdx+toAppend > outputCapacity { toAppend = outputCapacity - outStartIdx } @@ -1459,10 +1376,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1470,8 +1387,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1486,10 +1403,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1497,8 +1414,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1513,10 +1430,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1524,8 +1441,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1539,10 +1456,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1550,8 +1467,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1562,10 +1479,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1573,8 +1490,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1586,10 +1503,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1597,8 +1514,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1613,10 +1530,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1624,8 +1541,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1640,10 +1557,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1651,8 +1568,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1667,10 +1584,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1678,8 +1595,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1694,10 +1611,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1705,8 +1622,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1721,10 +1638,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -1732,8 +1649,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -1744,34 +1661,38 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx } outStartIdx += toAppend - if toAppend < batchLength-b.builderState.right.curSrcStartIdx { + if toAppend < batchLength-bs.curSrcStartIdx { // If we haven't materialized all the tuples from the // batch, then we are ready to emit the output batch. - b.builderState.right.curSrcStartIdx += toAppend + bs.curSrcStartIdx += toAppend return } // We have fully processed the current batch, so we need to // get the next one. - currentBatch, err = b.right.tuples.Dequeue(ctx) + currentBatch, err = b.rightTuples.Dequeue(ctx) if err != nil { colexecerror.InternalError(err) } - b.builderState.right.currentBatch = currentBatch + bs.currentBatch = currentBatch batchLength = currentBatch.Length() - b.builderState.right.curSrcStartIdx = 0 - - if outStartIdx == outputCapacity { - // We reached the capacity of the output batch, so we - // can emit it. - return - } + bs.curSrcStartIdx = 0 } // We have fully processed all the batches from the right side, // so we need to Rewind the queue. - if err := b.right.tuples.Rewind(); err != nil { + if err := b.rightTuples.Rewind(); err != nil { colexecerror.InternalError(err) } - b.builderState.right.currentBatch = nil + bs.currentBatch = nil } }) } + +// buildFromLeftBatch is the body of buildFromLeftInput that templates out the +// fact whether the current left batch has a selection vector or not. +// execgen:inline +const _ = "inlined_buildFromLeftBatch_true" + +// buildFromLeftBatch is the body of buildFromLeftInput that templates out the +// fact whether the current left batch has a selection vector or not. +// execgen:inline +const _ = "inlined_buildFromLeftBatch_false" diff --git a/pkg/sql/colexec/colexecjoin/crossjoiner.go b/pkg/sql/colexec/colexecjoin/crossjoiner.go index 01da98daa21d..7029d9560378 100644 --- a/pkg/sql/colexec/colexecjoin/crossjoiner.go +++ b/pkg/sql/colexec/colexecjoin/crossjoiner.go @@ -63,16 +63,18 @@ type crossJoiner struct { *joinHelper unlimitedAllocator *colmem.Allocator - inputsConsumed bool + rightInputConsumed bool outputTypes []*types.T maxOutputBatchMemSize int64 - numTotalOutputTuples int - numAlreadyEmitted int // isLeftAllNulls and isRightAllNulls indicate whether the output vectors // corresponding to the left and right inputs, respectively, should consist // only of NULL values. This is the case when we have right or left, - // respectively, unmatched tuples. Note that only one can be set to true. + // respectively, unmatched tuples. isLeftAllNulls, isRightAllNulls bool + // done indicates that the cross joiner has fully built its output and + // closed the spilling queue. Once set to true, only zero-length batches are + // emitted. + done bool } var _ colexecop.ClosableOperator = &crossJoiner{} @@ -88,17 +90,21 @@ func (c *crossJoiner) Init(ctx context.Context) { } func (c *crossJoiner) Next() coldata.Batch { - if !c.inputsConsumed { - c.consumeInputs(c.Ctx) + if c.done { + return coldata.ZeroBatch + } + if !c.rightInputConsumed { + c.consumeRightInput(c.Ctx) c.setupForBuilding() } - if c.numTotalOutputTuples == c.numAlreadyEmitted { + willEmit := c.willEmit() + if willEmit == 0 { if err := c.Close(); err != nil { colexecerror.InternalError(err) } + c.done = true return coldata.ZeroBatch } - willEmit := c.numTotalOutputTuples - c.numAlreadyEmitted c.output, _ = c.unlimitedAllocator.ResetMaybeReallocate( c.outputTypes, c.output, willEmit, c.maxOutputBatchMemSize, ) @@ -120,112 +126,157 @@ func (c *crossJoiner) Next() coldata.Batch { } } c.output.SetLength(willEmit) - c.numAlreadyEmitted += willEmit + c.builderState.numEmittedCurLeftBatch += willEmit + c.builderState.numEmittedTotal += willEmit return c.output } -// consumeInputs determines the kind of information the cross joiner needs from -// its inputs (in some cases, we don't need to buffer all input tuples) and -// consumes the inputs accordingly. -func (c *crossJoiner) consumeInputs(ctx context.Context) { - c.inputsConsumed = true - var needLeftTuples bool +// readNextLeftBatch fetches the next batch from the left input, prepares the +// builder for it (assuming that all rows in the batch contribute to the cross +// product), and returns the length of the batch. +func (c *crossJoiner) readNextLeftBatch() int { + leftBatch := c.inputOne.Next() + c.prepareForNextLeftBatch(leftBatch, 0 /* startIdx */, leftBatch.Length()) + return leftBatch.Length() +} + +// consumeRightInput determines the kind of information the cross joiner needs +// from its right input (in some cases, we don't need to buffer all tuples from +// the right) and consumes the right input accordingly. It also checks whether +// we need any tuples from the left and possibly reads a single batch, depending +// on the join type. +func (c *crossJoiner) consumeRightInput(ctx context.Context) { + c.rightInputConsumed = true var needRightTuples, needOnlyNumRightTuples bool switch c.joinType { case descpb.InnerJoin, descpb.LeftOuterJoin, descpb.RightOuterJoin, descpb.FullOuterJoin: - needLeftTuples = true + c.needLeftTuples = true needRightTuples = true case descpb.LeftSemiJoin: // With LEFT SEMI join we only need to know whether the right input is // empty or not. - c.right.numTuples = c.inputTwo.Next().Length() - needLeftTuples = c.right.numTuples != 0 + c.numRightTuples = c.inputTwo.Next().Length() + c.needLeftTuples = c.numRightTuples != 0 case descpb.RightSemiJoin: // With RIGHT SEMI join we only need to know whether the left input is // empty or not. - c.left.numTuples = c.inputOne.Next().Length() - needRightTuples = c.left.numTuples != 0 + needRightTuples = c.readNextLeftBatch() != 0 case descpb.LeftAntiJoin: // With LEFT ANTI join we only need to know whether the right input is // empty or not. - c.right.numTuples = c.inputTwo.Next().Length() - needLeftTuples = c.right.numTuples == 0 + c.numRightTuples = c.inputTwo.Next().Length() + c.needLeftTuples = c.numRightTuples == 0 case descpb.RightAntiJoin: // With RIGHT ANTI join we only need to know whether the left input is // empty or not. - c.left.numTuples = c.inputOne.Next().Length() - needRightTuples = c.left.numTuples == 0 + needRightTuples = c.readNextLeftBatch() == 0 case descpb.IntersectAllJoin, descpb.ExceptAllJoin: // With set-operation joins we only need the number of tuples from the // right input. - needLeftTuples = true + c.needLeftTuples = true needOnlyNumRightTuples = true default: colexecerror.InternalError(errors.AssertionFailedf("unexpected join type %s", c.joinType.String())) } - if needRightTuples && needOnlyNumRightTuples { - colexecerror.InternalError(errors.AssertionFailedf("both needRightTuples and needOnlyNumRightTuples are true")) - } - - if needLeftTuples { - for { - batch := c.inputOne.Next() - c.left.tuples.Enqueue(ctx, batch) - if batch.Length() == 0 { - break - } - c.left.numTuples += batch.Length() - } - } - if needRightTuples { + if needRightTuples || needOnlyNumRightTuples { for { batch := c.inputTwo.Next() - c.right.tuples.Enqueue(ctx, batch) - if batch.Length() == 0 { - break + if needRightTuples { + c.rightTuples.Enqueue(ctx, batch) } - c.right.numTuples += batch.Length() - } - } - if needOnlyNumRightTuples { - for { - batch := c.inputTwo.Next() if batch.Length() == 0 { break } - c.right.numTuples += batch.Length() + c.numRightTuples += batch.Length() } } } +// setupForBuilding prepares the cross joiner to build the output. This method +// must be called after the right input has been fully "processed" (which might +// mean it wasn't fully read, depending on the join type). func (c *crossJoiner) setupForBuilding() { - c.numTotalOutputTuples = c.calculateOutputCount() - switch c.joinType { case descpb.LeftOuterJoin: - c.isRightAllNulls = c.right.numTuples == 0 + c.isRightAllNulls = c.numRightTuples == 0 case descpb.RightOuterJoin: - c.isLeftAllNulls = c.left.numTuples == 0 + c.isLeftAllNulls = c.readNextLeftBatch() == 0 case descpb.FullOuterJoin: - c.isLeftAllNulls = c.left.numTuples == 0 - c.isRightAllNulls = c.right.numTuples == 0 - } - // In order for buildFrom*Input methods to work in the unmatched cases, we - // "lie" that there is a single tuple on the opposite side which results in - // the builder methods repeating the tuples only once, and that's exactly - // what we want. - if c.isLeftAllNulls { - c.left.numTuples = 1 + c.isLeftAllNulls = c.readNextLeftBatch() == 0 + c.isRightAllNulls = c.numRightTuples == 0 + case descpb.ExceptAllJoin: + // For EXCEPT ALL joins we build # left tuples - # right tuples output + // rows (if positive), so we have to discard first numRightTuples rows + // from the left. + for c.numRightTuples > 0 { + leftBatch := c.inputOne.Next() + c.builderState.left.currentBatch = leftBatch + if leftBatch.Length() == 0 { + break + } else if leftBatch.Length() > c.numRightTuples { + // The current left batch is the first one that contains tuples + // without a "match". + c.prepareForNextLeftBatch(leftBatch, c.numRightTuples, leftBatch.Length()) + break + } + c.numRightTuples -= leftBatch.Length() + } } + // In order for canEmit method to work in the unmatched cases, we "lie" + // that there is a single tuple on the right side which results in the + // builder method repeating the tuples only once, and that's exactly what we + // want. if c.isRightAllNulls { - c.right.numTuples = 1 + c.numRightTuples = 1 } - c.setupBuilder() - if c.isLeftAllNulls { - c.left.numTuples = 0 + c.setupLeftBuilder() +} + +// willEmit returns the number of tuples the cross joiner will emit based on the +// current left batch. If the current left batch is exhausted, then a new left +// batch is fetched. If 0 is returned, then the cross joiner has fully emitted +// the output. +func (c *crossJoiner) willEmit() int { + if c.needLeftTuples { + if c.isLeftAllNulls { + if c.isRightAllNulls { + // This can happen only in FULL OUTER join when both inputs are + // empty. + return 0 + } + // All tuples from the right are unmatched and will be emitted once. + c.builderState.setup.rightNumRepeats = 1 + return c.numRightTuples - c.builderState.numEmittedCurLeftBatch + } + if c.builderState.left.currentBatch == nil || c.canEmit() == 0 { + // Get the next left batch if we haven't fetched one yet or we + // have fully built the output using the current left batch. + if c.readNextLeftBatch() == 0 { + return 0 + } + } + return c.canEmit() } - if c.isRightAllNulls { - c.right.numTuples = 0 + switch c.joinType { + case descpb.LeftSemiJoin, descpb.LeftAntiJoin: + // We don't need the left tuples, and in case of LEFT SEMI/ANTI this + // means that the right input was empty/non-empty, so the cross join + // is empty. + return 0 + case descpb.RightSemiJoin, descpb.RightAntiJoin: + if c.numRightTuples == 0 { + // For RIGHT SEMI, we didn't fetch any right tuples if the left + // input was empty; for RIGHT ANTI - if the left input wasn't + // empty. In both such cases the cross join is empty. + return 0 + } + return c.canEmit() + default: + colexecerror.InternalError(errors.AssertionFailedf( + "unexpectedly don't need left tuples for %s", c.joinType, + )) + // This code is unreachable, but the compiler cannot infer that. + return 0 } } @@ -245,15 +296,12 @@ func (c *crossJoiner) Reset(ctx context.Context) { r.Reset(ctx) } c.crossJoinerBase.Reset(ctx) - c.inputsConsumed = false - c.numTotalOutputTuples = 0 - c.numAlreadyEmitted = 0 + c.rightInputConsumed = false c.isLeftAllNulls = false c.isRightAllNulls = false + c.done = false } -// TODO(yuzefovich): use two separate unlimited allocators giving the right side -// larger limit (since it might need to be read multiple times). func newCrossJoinerBase( unlimitedAllocator *colmem.Allocator, joinType descpb.JoinType, @@ -269,32 +317,22 @@ func newCrossJoinerBase( unlimitedAllocator: unlimitedAllocator, types: leftTypes, canonicalTypeFamilies: typeconv.ToCanonicalTypeFamilies(leftTypes), - tuples: colexecutils.NewSpillingQueue( - &colexecutils.NewSpillingQueueArgs{ - UnlimitedAllocator: unlimitedAllocator, - Types: leftTypes, - MemoryLimit: memoryLimit, - DiskQueueCfg: cfg, - FDSemaphore: fdSemaphore, - DiskAcc: diskAcc, - }, - ), }, right: cjState{ unlimitedAllocator: unlimitedAllocator, types: rightTypes, canonicalTypeFamilies: typeconv.ToCanonicalTypeFamilies(rightTypes), - tuples: colexecutils.NewRewindableSpillingQueue( - &colexecutils.NewSpillingQueueArgs{ - UnlimitedAllocator: unlimitedAllocator, - Types: rightTypes, - MemoryLimit: memoryLimit, - DiskQueueCfg: cfg, - FDSemaphore: fdSemaphore, - DiskAcc: diskAcc, - }, - ), }, + rightTuples: colexecutils.NewRewindableSpillingQueue( + &colexecutils.NewSpillingQueueArgs{ + UnlimitedAllocator: unlimitedAllocator, + Types: rightTypes, + MemoryLimit: memoryLimit, + DiskQueueCfg: cfg, + FDSemaphore: fdSemaphore, + DiskAcc: diskAcc, + }, + ), } if joinType.ShouldIncludeLeftColsInOutput() { base.builderState.rightColOffset = len(leftTypes) @@ -303,12 +341,26 @@ func newCrossJoinerBase( } type crossJoinerBase struct { - initHelper colexecop.InitHelper - joinType descpb.JoinType - left, right cjState - builderState struct { + initHelper colexecop.InitHelper + joinType descpb.JoinType + left, right cjState + numRightTuples int + rightTuples *colexecutils.SpillingQueue + needLeftTuples bool + builderState struct { setup cjBuilderSetupState left, right cjMutableBuilderState + + // numEmittedCurLeftBatch tracks the number of joined rows returned + // based on the current left batch. It is reset on every call to + // prepareForNextLeftBatch. + numEmittedCurLeftBatch int + + // numEmittedTotal tracks the number of rows that have been emitted + // since the crossJoinerBase has been reset. It is only used in RIGHT + // SEMI, RIGHT ANTI, and INTERSECT ALL joins. + numEmittedTotal int + // rightColOffset indicates the number of vectors in the output batch // that should be "skipped" when building from the right input. rightColOffset int @@ -320,142 +372,108 @@ func (b *crossJoinerBase) init(ctx context.Context) { b.initHelper.Init(ctx) } -func (b *crossJoinerBase) setupBuilder() { - switch b.joinType { - case descpb.IntersectAllJoin: - // For INTERSECT ALL joins we build min(left.numTuples, right.numTuples) - // tuples. - if b.left.numTuples < b.right.numTuples { - b.builderState.setup.leftSrcEndIdx = b.left.numTuples - } else { - b.builderState.setup.leftSrcEndIdx = b.right.numTuples - } - case descpb.ExceptAllJoin: - // For EXCEPT ALL joins we build left.numTuples-right.numTuples tuples - // (if positive). - if b.left.numTuples > b.right.numTuples { - b.builderState.setup.leftSrcEndIdx = b.left.numTuples - b.right.numTuples - } - default: - b.builderState.setup.leftSrcEndIdx = b.left.numTuples - } +func (b *crossJoinerBase) setupLeftBuilder() { switch b.joinType { case descpb.LeftSemiJoin, descpb.IntersectAllJoin, descpb.ExceptAllJoin: b.builderState.setup.leftNumRepeats = 1 case descpb.LeftAntiJoin: // LEFT ANTI cross join emits all left tuples repeated once only if the // right input is empty. - if b.right.numTuples == 0 { + if b.numRightTuples == 0 { b.builderState.setup.leftNumRepeats = 1 } default: - b.builderState.setup.leftNumRepeats = b.right.numTuples + b.builderState.setup.leftNumRepeats = b.numRightTuples } +} + +// prepareForNextLeftBatch sets up the crossJoinerBase to build based on a new +// batch coming from the left input. Only rows with ordinals in +// [startIdx, endIdx) range will be used for the cross join. +func (b *crossJoinerBase) prepareForNextLeftBatch(batch coldata.Batch, startIdx, endIdx int) { + b.builderState.numEmittedCurLeftBatch = 0 + b.builderState.left.currentBatch = batch + b.builderState.left.curSrcStartIdx = startIdx + b.builderState.left.numRepeatsIdx = 0 + b.builderState.right.numRepeatsIdx = 0 + + if b.joinType == descpb.IntersectAllJoin { + // Intersect all is special because we need to count how many tuples + // from the right we have already used up. + if b.builderState.numEmittedTotal+endIdx-startIdx >= b.numRightTuples { + // The current left batch is the last one that contains tuples with + // a "match". + b.builderState.setup.leftSrcEndIdx = b.numRightTuples - b.builderState.numEmittedTotal + startIdx + } else { + // The current left batch is still emitted fully. + b.builderState.setup.leftSrcEndIdx = endIdx + } + } else { + b.builderState.setup.leftSrcEndIdx = endIdx + } + switch b.joinType { - case descpb.RightSemiJoin: + case descpb.InnerJoin, descpb.LeftOuterJoin, descpb.RightOuterJoin, descpb.FullOuterJoin: + b.builderState.setup.rightNumRepeats = endIdx - startIdx + case descpb.RightSemiJoin, descpb.RightAntiJoin: b.builderState.setup.rightNumRepeats = 1 - case descpb.RightAntiJoin: - // RIGHT ANTI cross join emits all right tuples repeated once only if - // the left input is empty. - if b.left.numTuples == 0 { - b.builderState.setup.rightNumRepeats = 1 - } - default: - b.builderState.setup.rightNumRepeats = b.left.numTuples } } -// calculateOutputCount returns the total number of tuples that are emitted by -// the cross join given already initialized left and right side states. -func (b *crossJoinerBase) calculateOutputCount() int { +// canEmit returns the number of output rows that can still be emitted based on +// the current left batch. It supports only the case when both left and right +// inputs are not empty. +func (b *crossJoinerBase) canEmit() int { switch b.joinType { - case descpb.InnerJoin: - return b.left.numTuples * b.right.numTuples - case descpb.LeftOuterJoin: - if b.right.numTuples == 0 { - return b.left.numTuples - } - return b.left.numTuples * b.right.numTuples - case descpb.RightOuterJoin: - if b.left.numTuples == 0 { - return b.right.numTuples - } - return b.left.numTuples * b.right.numTuples - case descpb.FullOuterJoin: - if b.left.numTuples == 0 || b.right.numTuples == 0 { - return b.left.numTuples + b.right.numTuples - } - return b.left.numTuples * b.right.numTuples - case descpb.LeftSemiJoin: - if b.right.numTuples == 0 { + case descpb.LeftSemiJoin, descpb.IntersectAllJoin, descpb.ExceptAllJoin: + return b.builderState.setup.leftSrcEndIdx - b.builderState.left.curSrcStartIdx + case descpb.LeftAntiJoin: + if b.numRightTuples != 0 { return 0 } - return b.left.numTuples + return b.builderState.setup.leftSrcEndIdx - b.builderState.left.curSrcStartIdx case descpb.RightSemiJoin: - if b.left.numTuples == 0 { - return 0 - } - return b.right.numTuples - case descpb.LeftAntiJoin: - if b.right.numTuples != 0 { + // RIGHT SEMI cross join emits all right tuples repeated once iff the + // left input is not empty. + if b.builderState.setup.leftSrcEndIdx == b.builderState.left.curSrcStartIdx { return 0 } - return b.left.numTuples + return b.numRightTuples - b.builderState.numEmittedTotal case descpb.RightAntiJoin: - if b.left.numTuples != 0 { - return 0 - } - return b.right.numTuples - case descpb.IntersectAllJoin: - if b.right.numTuples < b.left.numTuples { - return b.right.numTuples - } - return b.left.numTuples - case descpb.ExceptAllJoin: - if b.right.numTuples > b.left.numTuples { + // RIGHT ANTI cross join emits all right tuples repeated once iff the + // left input is empty. + if b.builderState.setup.leftSrcEndIdx != b.builderState.left.curSrcStartIdx { return 0 } - return b.left.numTuples - b.right.numTuples + return b.numRightTuples - b.builderState.numEmittedTotal default: - colexecerror.InternalError(errors.AssertionFailedf("unexpected join type %s", b.joinType.String())) - // Unreachable code. - return 0 + return b.builderState.setup.rightNumRepeats*b.numRightTuples - b.builderState.numEmittedCurLeftBatch } } func (b *crossJoinerBase) Reset(ctx context.Context) { - if b.left.tuples != nil { - b.left.tuples.Reset(ctx) - } - if b.right.tuples != nil { - b.right.tuples.Reset(ctx) + if b.rightTuples != nil { + b.rightTuples.Reset(ctx) } - b.left.numTuples = 0 - b.right.numTuples = 0 + b.numRightTuples = 0 b.builderState.left.reset() b.builderState.right.reset() + b.builderState.numEmittedCurLeftBatch = 0 + b.builderState.numEmittedTotal = 0 } func (b *crossJoinerBase) Close() error { ctx := b.initHelper.EnsureCtx() - var lastErr error - if b.left.tuples != nil { - lastErr = b.left.tuples.Close(ctx) - } - if b.right.tuples != nil { - if err := b.right.tuples.Close(ctx); err != nil { - lastErr = err - } + if b.rightTuples != nil { + return b.rightTuples.Close(ctx) } - return lastErr + return nil } type cjState struct { unlimitedAllocator *colmem.Allocator types []*types.T canonicalTypeFamilies []types.Family - tuples *colexecutils.SpillingQueue - numTuples int } type cjBuilderSetupState struct { @@ -481,16 +499,10 @@ type cjMutableBuilderState struct { // numRepeatsIdx tracks the number of times a "group" has already been // repeated. numRepeatsIdx int - // setOpLeftSrcIdx tracks the current tuple's index from the left input for - // set operation joins. INTERSECT ALL and EXCEPT ALL joins are special - // because they need to build the output partially (namely, for exactly - // leftSrcEndIdx number of tuples which could span multiple batches). - setOpLeftSrcIdx int } func (s *cjMutableBuilderState) reset() { s.currentBatch = nil s.curSrcStartIdx = 0 s.numRepeatsIdx = 0 - s.setOpLeftSrcIdx = 0 } diff --git a/pkg/sql/colexec/colexecjoin/crossjoiner_tmpl.go b/pkg/sql/colexec/colexecjoin/crossjoiner_tmpl.go index 4f097649719c..e01a6da7316e 100644 --- a/pkg/sql/colexec/colexecjoin/crossjoiner_tmpl.go +++ b/pkg/sql/colexec/colexecjoin/crossjoiner_tmpl.go @@ -38,198 +38,125 @@ var ( _ = types.BoolFamily ) +// buildFromLeftBatch is the body of buildFromLeftInput that templates out the +// fact whether the current left batch has a selection vector or not. +// execgen:inline +// execgen:template +func buildFromLeftBatch(b *crossJoinerBase, currentBatch coldata.Batch, sel []int, hasSel bool) { + if !hasSel { + // Remove the unused warning. + _ = sel + } + // We'll be modifying the builder state as we go, but we'll need to be able + // to restore the state to initial for each column. + initialBuilderState := b.builderState.left + bs := &b.builderState.left + leftNumRepeats := b.builderState.setup.leftNumRepeats + leftSrcEndIdx := b.builderState.setup.leftSrcEndIdx + outputCapacity := b.output.Capacity() + var srcStartIdx int + // Loop over every column. + for colIdx := range b.left.types { + if colIdx > 0 { + // Restore the builder state so that this new column started off + // fresh. + *bs = initialBuilderState + } + outStartIdx := destStartIdx + src := currentBatch.ColVec(colIdx) + srcNulls := src.Nulls() + out := b.output.ColVec(colIdx) + outNulls := out.Nulls() + switch b.left.canonicalTypeFamilies[colIdx] { + // {{range .}} + case _CANONICAL_TYPE_FAMILY: + switch b.left.types[colIdx].Width() { + // {{range .WidthOverloads}} + case _TYPE_WIDTH: + srcCol := src.TemplateType() + outCol := out.TemplateType() + if leftNumRepeats == 1 { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each tuple one time. + if hasSel { + srcStartIdx = sel[bs.curSrcStartIdx] + } else { + srcStartIdx = bs.curSrcStartIdx + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNull(outStartIdx) + } else { + val := srcCol.Get(srcStartIdx) + outCol.Set(outStartIdx, val) + } + outStartIdx++ + bs.curSrcStartIdx++ + } + } else { + // Loop over every tuple in the current batch. + for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity { + // Repeat each row leftNumRepeats times. + if hasSel { + srcStartIdx = sel[bs.curSrcStartIdx] + } else { + srcStartIdx = bs.curSrcStartIdx + } + toAppend := leftNumRepeats - bs.numRepeatsIdx + if outStartIdx+toAppend > outputCapacity { + // We don't have enough space to repeat the current + // value the required number of times, so we'll have + // to continue from here on the next call. + toAppend = outputCapacity - outStartIdx + bs.numRepeatsIdx += toAppend + } else { + // We fully processed the current tuple for the + // current column, and before moving on to the next + // one, we need to reset numRepeatsIdx (so that the + // next tuple would be repeated leftNumRepeats + // times). + bs.curSrcStartIdx++ + bs.numRepeatsIdx = 0 + } + if srcNulls.NullAt(srcStartIdx) { + outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) + outStartIdx += toAppend + } else { + val := srcCol.Get(srcStartIdx) + for i := 0; i < toAppend; i++ { + outCol.Set(outStartIdx, val) + outStartIdx++ + } + } + } + } + // {{end}} + } + // {{end}} + default: + colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", b.left.types[colIdx].String())) + } + } +} + // buildFromLeftInput builds part of the output of a cross join that comes from // the vectors of the left input. The new output tuples are put starting at // index destStartIdx and will not exceed the capacity of the output batch. It -// is assumed that setupBuilder has been called. +// is assumed that setupLeftBuilder and prepareForNextLeftBatch have been +// called. // // The goal of this method is to repeat each tuple from the left input -// leftNumRepeats times. For set-operation joins only first setOpLeftSrcIdx -// tuples are built from. +// leftNumRepeats times. Only the tuples in [curSrcStartIdx, leftSrcEndIdx) are +// used from the current left batch. func (b *crossJoinerBase) buildFromLeftInput(ctx context.Context, destStartIdx int) { - var err error currentBatch := b.builderState.left.currentBatch - if currentBatch == nil || b.builderState.left.curSrcStartIdx == currentBatch.Length() { - // We need to get the next batch to build from if it is the first one or - // we have fully processed the previous one. - currentBatch, err = b.left.tuples.Dequeue(ctx) - if err != nil { - colexecerror.InternalError(err) - } - b.builderState.left.currentBatch = currentBatch - b.builderState.left.curSrcStartIdx = 0 - b.builderState.left.numRepeatsIdx = 0 - } - initialBuilderState := b.builderState.left b.left.unlimitedAllocator.PerformOperation( b.output.ColVecs()[:len(b.left.types)], func() { - leftNumRepeats := b.builderState.setup.leftNumRepeats - isSetOp := b.joinType.IsSetOpJoin() - outputCapacity := b.output.Capacity() - batchLength := currentBatch.Length() - for batchLength > 0 { - // Loop over every column. - LeftColLoop: - for colIdx := range b.left.types { - outStartIdx := destStartIdx - src := currentBatch.ColVec(colIdx) - srcNulls := src.Nulls() - out := b.output.ColVec(colIdx) - outNulls := out.Nulls() - switch b.left.canonicalTypeFamilies[colIdx] { - // {{range .}} - case _CANONICAL_TYPE_FAMILY: - switch b.left.types[colIdx].Width() { - // {{range .WidthOverloads}} - case _TYPE_WIDTH: - srcCol := src.TemplateType() - outCol := out.TemplateType() - if leftNumRepeats == 1 { - // Loop over every tuple in the current batch. - for b.builderState.left.curSrcStartIdx < batchLength { - // Repeat each tuple one time. - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - b.builderState.left.setOpLeftSrcIdx++ - } - - srcStartIdx := b.builderState.left.curSrcStartIdx - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNull(outStartIdx) - } else { - val := srcCol.Get(srcStartIdx) - outCol.Set(outStartIdx, val) - } - outStartIdx++ - b.builderState.left.curSrcStartIdx++ - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - } - } else { - // Loop over every tuple in the current batch. - for ; b.builderState.left.curSrcStartIdx < batchLength; b.builderState.left.curSrcStartIdx++ { - // Repeat each row leftNumRepeats times. - srcStartIdx := b.builderState.left.curSrcStartIdx - toAppend := leftNumRepeats - b.builderState.left.numRepeatsIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx - } - - if isSetOp { - if b.builderState.left.setOpLeftSrcIdx == b.builderState.setup.leftSrcEndIdx { - // We have fully materialized first leftSrcEndIdx - // tuples in the current column, so we need to - // either transition to the next column or exit. - if colIdx == len(b.left.types)-1 { - // This is the last column. - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - b.builderState.left.setOpLeftSrcIdx += toAppend - } - - if srcNulls.NullAt(srcStartIdx) { - outNulls.SetNullRange(outStartIdx, outStartIdx+toAppend) - outStartIdx += toAppend - } else { - val := srcCol.Get(srcStartIdx) - for i := 0; i < toAppend; i++ { - outCol.Set(outStartIdx, val) - outStartIdx++ - } - } - - if outStartIdx == outputCapacity { - // We reached the capacity of the output vector, - // so we move to the next column. - if colIdx == len(b.left.types)-1 { - // This is the last column. - b.builderState.left.numRepeatsIdx += toAppend - if b.builderState.left.numRepeatsIdx == leftNumRepeats { - // The current tuple has already been repeated - // the desired number of times, so we advance - // the source index. - b.builderState.left.curSrcStartIdx++ - b.builderState.left.numRepeatsIdx = 0 - } - return - } - // We need to start building the next column - // with the same initial builder state as the - // current column. - b.builderState.left = initialBuilderState - continue LeftColLoop - } - // We fully processed the current tuple for the current - // column, and before moving on to the next one, we need - // to reset numRepeatsIdx (so that the next tuple would - // be repeated leftNumRepeats times). - b.builderState.left.numRepeatsIdx = 0 - } - } - // {{end}} - } - // {{end}} - default: - colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", b.left.types[colIdx].String())) - } - if colIdx == len(b.left.types)-1 { - // We have appended some tuples into the output batch from the current - // batch (the latter is now fully processed), so we need to adjust - // destStartIdx accordingly for the next batch. - destStartIdx = outStartIdx - } else { - b.builderState.left = initialBuilderState - } - } - // We have processed all tuples in the current batch from the - // buffered group, so we need to Dequeue the next one. - currentBatch, err = b.left.tuples.Dequeue(ctx) - if err != nil { - colexecerror.InternalError(err) - } - b.builderState.left.currentBatch = currentBatch - batchLength = currentBatch.Length() - // We have transitioned to building from a new batch, so we - // need to update the builder state to build from the beginning - // of the new batch. - b.builderState.left.curSrcStartIdx = 0 - b.builderState.left.numRepeatsIdx = 0 - // We also need to update 'initialBuilderState' so that the - // builder state gets reset correctly in-between different - // columns in the loop above. - initialBuilderState = b.builderState.left + if sel := currentBatch.Selection(); sel != nil { + buildFromLeftBatch(b, currentBatch, sel, true /* hasSel */) + } else { + buildFromLeftBatch(b, currentBatch, nil, false /* hasSel */) } }, ) @@ -238,31 +165,35 @@ func (b *crossJoinerBase) buildFromLeftInput(ctx context.Context, destStartIdx i // buildFromRightInput builds part of the output of a cross join that comes from // the vectors of the right input. The new output tuples are put starting at // index destStartIdx and will not exceed the capacity of the output batch. It -// is assumed that setupBuilder has been called. +// is assumed that the right input has been fully consumed and is stored in +// b.rightTuples spilling queue. // // The goal of this method is to repeat all tuples from the right input // rightNumRepeats times (i.e. repeating the whole list of tuples at once). func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx int) { var err error + bs := &b.builderState.right + rightNumRepeats := b.builderState.setup.rightNumRepeats b.right.unlimitedAllocator.PerformOperation( b.output.ColVecs()[b.builderState.rightColOffset:], func() { outStartIdx := destStartIdx outputCapacity := b.output.Capacity() - // Repeat the buffered tuples rightNumRepeats times. - for ; b.builderState.right.numRepeatsIdx < b.builderState.setup.rightNumRepeats; b.builderState.right.numRepeatsIdx++ { - currentBatch := b.builderState.right.currentBatch + // Repeat the buffered tuples rightNumRepeats times until we fill + // the output capacity. + for ; outStartIdx < outputCapacity && bs.numRepeatsIdx < rightNumRepeats; bs.numRepeatsIdx++ { + currentBatch := bs.currentBatch if currentBatch == nil { - currentBatch, err = b.right.tuples.Dequeue(ctx) + currentBatch, err = b.rightTuples.Dequeue(ctx) if err != nil { colexecerror.InternalError(err) } - b.builderState.right.currentBatch = currentBatch - b.builderState.right.curSrcStartIdx = 0 + bs.currentBatch = currentBatch + bs.curSrcStartIdx = 0 } batchLength := currentBatch.Length() for batchLength > 0 { - toAppend := batchLength - b.builderState.right.curSrcStartIdx + toAppend := batchLength - bs.curSrcStartIdx if outStartIdx+toAppend > outputCapacity { toAppend = outputCapacity - outStartIdx } @@ -285,10 +216,10 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx // Optimization in the case that group length is 1, use assign // instead of copy. if toAppend == 1 { - if srcNulls.NullAt(b.builderState.right.curSrcStartIdx) { + if srcNulls.NullAt(bs.curSrcStartIdx) { outNulls.SetNull(outStartIdx) } else { - v := srcCol.Get(b.builderState.right.curSrcStartIdx) + v := srcCol.Get(bs.curSrcStartIdx) outCol.Set(outStartIdx, v) } } else { @@ -296,8 +227,8 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx coldata.SliceArgs{ Src: src, DestIdx: outStartIdx, - SrcStartIdx: b.builderState.right.curSrcStartIdx, - SrcEndIdx: b.builderState.right.curSrcStartIdx + toAppend, + SrcStartIdx: bs.curSrcStartIdx, + SrcEndIdx: bs.curSrcStartIdx + toAppend, }, ) } @@ -310,34 +241,28 @@ func (b *crossJoinerBase) buildFromRightInput(ctx context.Context, destStartIdx } outStartIdx += toAppend - if toAppend < batchLength-b.builderState.right.curSrcStartIdx { + if toAppend < batchLength-bs.curSrcStartIdx { // If we haven't materialized all the tuples from the // batch, then we are ready to emit the output batch. - b.builderState.right.curSrcStartIdx += toAppend + bs.curSrcStartIdx += toAppend return } // We have fully processed the current batch, so we need to // get the next one. - currentBatch, err = b.right.tuples.Dequeue(ctx) + currentBatch, err = b.rightTuples.Dequeue(ctx) if err != nil { colexecerror.InternalError(err) } - b.builderState.right.currentBatch = currentBatch + bs.currentBatch = currentBatch batchLength = currentBatch.Length() - b.builderState.right.curSrcStartIdx = 0 - - if outStartIdx == outputCapacity { - // We reached the capacity of the output batch, so we - // can emit it. - return - } + bs.curSrcStartIdx = 0 } // We have fully processed all the batches from the right side, // so we need to Rewind the queue. - if err := b.right.tuples.Rewind(); err != nil { + if err := b.rightTuples.Rewind(); err != nil { colexecerror.InternalError(err) } - b.builderState.right.currentBatch = nil + bs.currentBatch = nil } }) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoinbase.eg.go b/pkg/sql/colexec/colexecjoin/mergejoinbase.eg.go index ce0ffcb2286b..765aa1537a76 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoinbase.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoinbase.eg.go @@ -31,17 +31,13 @@ var ( ) // isBufferedGroupFinished checks to see whether or not the buffered group -// corresponding to input continues in batch. +// corresponding to the first tuple continues in batch. func (o *mergeJoinBase) isBufferedGroupFinished( - input *mergeJoinInput, batch coldata.Batch, rowIdx int, + input *mergeJoinInput, firstTuple []coldata.Vec, batch coldata.Batch, rowIdx int, ) bool { if batch.Length() == 0 { return true } - bufferedGroup := o.bufferedGroup.left - if input == &o.right { - bufferedGroup = o.bufferedGroup.right - } tupleToLookAtIdx := rowIdx sel := batch.Selection() if sel != nil { @@ -62,7 +58,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -73,7 +69,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Bool() + bufferedCol := firstTuple[colIdx].Bool() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Bool() curVal := col.Get(tupleToLookAtIdx) @@ -107,7 +103,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -118,7 +114,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Bytes() + bufferedCol := firstTuple[colIdx].Bytes() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Bytes() curVal := col.Get(tupleToLookAtIdx) @@ -144,7 +140,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -155,7 +151,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Decimal() + bufferedCol := firstTuple[colIdx].Decimal() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Decimal() curVal := col.Get(tupleToLookAtIdx) @@ -180,7 +176,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -191,7 +187,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Int16() + bufferedCol := firstTuple[colIdx].Int16() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Int16() curVal := col.Get(tupleToLookAtIdx) @@ -224,7 +220,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -235,7 +231,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Int32() + bufferedCol := firstTuple[colIdx].Int32() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Int32() curVal := col.Get(tupleToLookAtIdx) @@ -269,7 +265,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -280,7 +276,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Int64() + bufferedCol := firstTuple[colIdx].Int64() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Int64() curVal := col.Get(tupleToLookAtIdx) @@ -317,7 +313,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -328,7 +324,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Float64() + bufferedCol := firstTuple[colIdx].Float64() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Float64() curVal := col.Get(tupleToLookAtIdx) @@ -373,7 +369,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -384,7 +380,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Timestamp() + bufferedCol := firstTuple[colIdx].Timestamp() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Timestamp() curVal := col.Get(tupleToLookAtIdx) @@ -417,7 +413,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -428,7 +424,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Interval() + bufferedCol := firstTuple[colIdx].Interval() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Interval() curVal := col.Get(tupleToLookAtIdx) @@ -454,7 +450,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -465,7 +461,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].JSON() + bufferedCol := firstTuple[colIdx].JSON() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).JSON() curVal := col.Get(tupleToLookAtIdx) @@ -497,7 +493,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -508,7 +504,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].Datum() + bufferedCol := firstTuple[colIdx].Datum() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).Datum() curVal := col.Get(tupleToLookAtIdx) diff --git a/pkg/sql/colexec/colexecjoin/mergejoinbase_tmpl.go b/pkg/sql/colexec/colexecjoin/mergejoinbase_tmpl.go index d7168305d113..8b4b1641473e 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoinbase_tmpl.go +++ b/pkg/sql/colexec/colexecjoin/mergejoinbase_tmpl.go @@ -57,17 +57,13 @@ func _ASSIGN_EQ(_, _, _, _, _, _ interface{}) int { // */}} // isBufferedGroupFinished checks to see whether or not the buffered group -// corresponding to input continues in batch. +// corresponding to the first tuple continues in batch. func (o *mergeJoinBase) isBufferedGroupFinished( - input *mergeJoinInput, batch coldata.Batch, rowIdx int, + input *mergeJoinInput, firstTuple []coldata.Vec, batch coldata.Batch, rowIdx int, ) bool { if batch.Length() == 0 { return true } - bufferedGroup := o.bufferedGroup.left - if input == &o.right { - bufferedGroup = o.bufferedGroup.right - } tupleToLookAtIdx := rowIdx sel := batch.Selection() if sel != nil { @@ -89,7 +85,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( // right side being an input) this check will always return false since // nulls couldn't be buffered up though. // TODO(yuzefovich): consider templating this. - bufferedNull := bufferedGroup.firstTuple[colIdx].MaybeHasNulls() && bufferedGroup.firstTuple[colIdx].Nulls().NullAt(0) + bufferedNull := firstTuple[colIdx].MaybeHasNulls() && firstTuple[colIdx].Nulls().NullAt(0) incomingNull := batch.ColVec(int(colIdx)).MaybeHasNulls() && batch.ColVec(int(colIdx)).Nulls().NullAt(tupleToLookAtIdx) if o.joinType.IsSetOpJoin() { if bufferedNull && incomingNull { @@ -100,7 +96,7 @@ func (o *mergeJoinBase) isBufferedGroupFinished( if bufferedNull || incomingNull { return true } - bufferedCol := bufferedGroup.firstTuple[colIdx].TemplateType() + bufferedCol := firstTuple[colIdx].TemplateType() prevVal := bufferedCol.Get(0) col := batch.ColVec(int(colIdx)).TemplateType() curVal := col.Get(tupleToLookAtIdx) diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner.go b/pkg/sql/colexec/colexecjoin/mergejoiner.go index 45ddc5ad89bb..b8593dcd39b6 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner.go @@ -32,6 +32,161 @@ import ( "github.com/marusama/semaphore" ) +// The merge join operator uses a probe and build approach to generate the join. +// What this means is that instead of going through and expanding the cross +// product row by row, the operator performs two passes. The first pass +// generates a list of groups of matching rows based on the equality columns +// (where a "group" represents a contiguous set of rows that match on the +// equality columns). The second pass is where the groups and their associated +// cross products are materialized into the full output. +// +// A group describes the indexes of rows within the probing batches that are +// equal on the equality columns. For example, if we have a group with indexes +// [0, 3) on the left and [1, 3) on the right, it means that first three rows +// from the left batch match with the 2nd and 3rd row from the right batch. In +// order to produce the output we need to repeat each left row the number of +// times equal to the size of the group on the right side (2 in the example); +// and we need to repeat all rows from the right side of the group the number of +// times equal to the size of the group on the left side (3 in the example). +// +// There is a complication, however, when a group might extend in the next +// batch, and we call such a group a "buffered group". For example, imagine we +// have a batch with rows {0, 0, 1, 2}, then the group containing the row with +// value 2 is not complete with the current batch, and it might (or might not) +// extend into the next batch. +// +// We handle these buffered groups definitely depending on the side of the join: +// - the left buffered group is processed in a streaming fashion, one batch from +// the left input at a time +// - the right buffered group needs to be fully buffered before we can build the +// output. +// +// Let's walk through a concrete example to show the general flow of state +// transitions as well as to better show the terminology we use throughout the +// merge joiner code. +// +// Imagine that we operate on batches of size 3 and have the following input +// data: +// left right +// batch 1 -1 batch 1 -1 +// -1 -2 +// -2 -3 +// batch 2 -2 batch 2 -3 +// -2 -3 +// -2 -3 +// batch 3 -3 batch 3 -3 +// -3 +// -3 +// batch 4 -3 +// +// We start of with reading first batches from both inputs (we store them in +// mjProberState) and performing the probing step. We find that we have a single +// group such that it contains rows with indices [0, 2) on the left and with +// indices [0, 1) on the right. This group is fully contained within the batch +// (i.e. it is not a buffered group), so we'll be able to populate the cross +// product during the build step. The last row with index 2 in the left batch +// starts the left buffered group (we don't know yet whether the second batch +// from the left will have a row with value -2 or not), so we will have to +// process the last row as the buffered group; we append a single row with index +// 1 from the right batch into the right buffered group. +// +// At this point the probing is done, and the merge joiner transitions into +// mjBuildFromBatch. Here we take the single group ([0, 2) on the left and +// [0, 1) on the right) to produce a cross product, so we add two result rows +// to the output batch. We don't emit the output yet because we want to fill up +// the output to capacity. The output is now {(-1, -1), (-1, -1)}. +// +// Since we have something in the buffered group state, we transition into +// building from the buffered group. There, we first have to make sure that we +// buffer all rows (into a spilling queue) that are part of the right buffered +// group; however, in this case we have found the boundaries of the right group +// during the probing, so we don't need to read any new batches - the right +// buffered group only contains a single row with index 1. At the same time, the +// left buffered group was started with a single row with index 2, so we are +// ready to perform the cross product. This produces a single row that we put +// into the output batch. Now the output is at capacity, so we emit {(-1, -1), +// (-1, -1), (-2, -2)}, but the merge joiner remains in mjBuildFromBufferedGroup +// state. +// +// When Next() is called again, we see that we have fully emitted the cross +// product based on the current left batch, so we fetch the second batch from +// the left in continueLeftBufferedGroup(). There, after fetching the second +// batch we first check whether the first row of the batch still belongs to the +// left buffered group (it does), and then we run the ordered distinct in order +// to find the first row that is not part of the current buffered group. In this +// case it turns out that all 3 rows still belong to the same buffered group +// that have value -2 in their equality columns. We produce a cross product of 3 +// rows on the left and a single row on the right, put it into the output and +// emit {(-2, -2), (-2, -2), (-2, -2)}. The merge joiner remains in +// mjBuildFromBufferedGroup state. +// +// When Next() is called, we fetch the third batch from the left and find that +// its first row (value -3) is different from the current left buffered group +// (value -2), so we have finished processing the buffered group for value -2. +// The merge joiner transitions into mjEntry state (where we don't do anything +// since we already have the prober state set up) and then into mjProbe state. +// +// As a reminder, we working with a batch {(-3), (-3), (-3)} from the left and +// {(-1), (-2), (-3)} from the right. Note that on the left we're starting from +// index 0 and from index 2 on the right. During probing, we find that all 3 +// rows from the left and the row with index 2 on the right are part of the same +// group which might extend into the next batches from either side, so we start +// a buffered group on the left and append the single row into the right +// buffered group. +// +// We don't have any groups fully contained within the probing batches to build +// from, so we transition into building from the buffered group. There, we will +// actually read the second and the third batches from the right and append all +// of them into the spilling queue. The right input is now exhausted. We now +// have 5 rows with value -3 in the spilling queue, and we proceed to build the +// cross product with 3 rows from the left. This will result in 15 rows emitted +// in 5 batches. +// +// Once we've emitted those 5 batches, we see that we have fully built the cross +// product based on the third left batch, so we fetch the last fourth batch from +// the left. It has a single row that is still part of the same group, so we +// will build the cross product against 5 rows in the spilling queue of the +// right buffered group. +// +// At this point, we have exhausted both inputs, and we're done. + +// mjState represents the state of the merge joiner. +type mjState int + +const ( + // mjEntry is the entry state of the merge joiner where all the batches and + // indices are properly set, regardless if Next was called the first time or + // the 1000th time. This state also routes into the correct state based on + // the prober state after setup. + mjEntry mjState = iota + + // mjSourceFinished is the state in which one of the input sources has no + // more available batches, thus signaling that the joiner should begin + // wrapping up execution by outputting any remaining groups in state. After + // reaching this state, we can only build from the batch. + mjSourceFinished + + // mjProbe is the main probing state in which the groups for the current + // batch are determined. + mjProbe + + // mjBuildFromBatch indicates that we should be building from the current + // probing batches. Note that in such case we might have multiple groups to + // build. + mjBuildFromBatch + + // mjBuildFromBufferedGroup indicates that we should be building from the + // current left batch and the right buffered group. Note that in such case + // we have at most one group to build and are building the output one batch + // from the left input at a time. + mjBuildFromBufferedGroup + + // mjDone is the final state of the merge joiner in which it'll be returning + // only zero-length batches. In this state, the disk infrastructure is + // cleaned up. + mjDone +) + // group is an ADT representing a contiguous set of rows that match on their // equality columns. type group struct { @@ -53,36 +208,14 @@ type group struct { unmatched bool } -// mjBuildFrom is an indicator of which source we're building the output from. -type mjBuildFrom int - -const ( - // mjBuildFromBatch indicates that we should be building from the current - // probing batches. Note that in such case we might have multiple groups to - // build. - mjBuildFromBatch mjBuildFrom = iota - // mjBuildFromBufferedGroup indicates that we should be building from the - // buffered group. Note that in such case we might have at most one group to - // build. - mjBuildFromBufferedGroup -) - // mjBuilderState contains all the state required to execute the build phase. type mjBuilderState struct { - buildFrom mjBuildFrom - // Fields to identify the groups in the input sources. lGroups []group rGroups []group // outCount keeps record of the current number of rows in the output. outCount int - // outFinished is used to determine if the builder is finished outputting - // the groups from input. - outFinished bool - - totalOutCountFromBufferedGroup int - alreadyEmittedFromBufferedGroup int // Cross product materialization state. left mjBuilderCrossProductState @@ -98,71 +231,47 @@ type mjBuilderCrossProductState struct { numRepeatsIdx int } -// mjBufferedGroup is a helper struct that stores information about the tuples -// from both inputs for the buffered group. -type mjBufferedGroup struct { - // firstTuple stores a single tuple that was first in the buffered group. - firstTuple []coldata.Vec - scratchBatch coldata.Batch -} - type mjBufferedGroupState struct { - // Local buffer for the last left and right groups which is used when the - // group ends with a batch and the group on each side needs to be saved to - // state in order to be able to continue it in the next batch. - left mjBufferedGroup - right mjBufferedGroup - helper *crossJoinerBase - needToReset bool + // leftFirstTuple is the first tuple of the left buffered group. It is set + // only in case the left buffered group spans more than one input batch. + leftFirstTuple []coldata.Vec + // leftGroupStartIdx is the position within the current left batch where the + // left buffered group starts. If the group spans multiple batches, this + // will be set to 0 on all consecutive batches. + // + // Note that proberState.lIdx indicates the exclusive end position for the + // left buffered group within the current batch. + leftGroupStartIdx int + // leftBatchDone indicates whether the output from the current left batch + // has been fully built. + leftBatchDone bool + // rightFirstTuple is the first tuple of the right buffered group. It is set + // only in case the right buffered group spans more than one input batch. + rightFirstTuple []coldata.Vec + // rightScratchBatch is a scratch space for copying the tuples out of the + // right input batches before enqueueing them into the spilling queue. + rightScratchBatch coldata.Batch + + // helper is the building facility for the cross join of the buffered group. + helper *crossJoinerBase } // mjProberState contains all the state required to execute in the probing // phase. type mjProberState struct { // Fields to save the "working" batches to state in between outputs. - lBatch coldata.Batch - rBatch coldata.Batch + lBatch coldata.Batch + rBatch coldata.Batch + // lIdx indicates the index of the first left tuple that hasn't been probed + // yet. lIdx int lLength int + // rIdx indicates the index of the first right tuple that hasn't been probed + // yet. rIdx int rLength int } -// mjState represents the state of the merge joiner. -type mjState int - -const ( - // mjEntry is the entry state of the merge joiner where all the batches and - // indices are properly set, regardless if Next was called the first time or - // the 1000th time. This state also routes into the correct state based on - // the prober state after setup. - mjEntry mjState = iota - - // mjSourceFinished is the state in which one of the input sources has no - // more available batches, thus signaling that the joiner should begin - // wrapping up execution by outputting any remaining groups in state. - mjSourceFinished - - // mjFinishBufferedGroup is the state in which the previous state resulted in - // a group that ended with a batch. Such a group was buffered, and this state - // finishes that group and builds the output. - mjFinishBufferedGroup - - // mjProbe is the main probing state in which the groups for the current - // batch are determined. - mjProbe - - // mjBuild is the state in which the groups determined by the probing states - // are built, i.e. materialized to the output member by creating the cross - // product. - mjBuild - - // mjDone is the final state of the merge joiner in which it'll be returning - // only zero-length batches. In this state, the disk infrastructure is - // cleaned up. - mjDone -) - type mergeJoinInput struct { // eqCols specify the indices of the source table equality columns during the // merge join. @@ -194,21 +303,6 @@ type mergeJoinInput struct { source colexecop.Operator } -// The merge join operator uses a probe and build approach to generate the -// join. What this means is that instead of going through and expanding the -// cross product row by row, the operator performs two passes. -// The first pass generates a list of groups of matching rows based on the -// equality columns (where a "group" represents a contiguous set of rows that -// match on the equality columns). -// The second pass is where the groups and their associated cross products are -// materialized into the full output. - -// Two buffers are used, one for the group on the left table and one for the -// group on the right table. These buffers are only used if the group ends with -// a batch, to make sure that we don't miss any cross product entries while -// expanding the groups (leftGroups and rightGroups) when a group spans -// multiple batches. - // NewMergeJoinOp returns a new merge join operator with the given spec that // implements sort-merge join. It performs a merge on the left and right input // sources, based on the equality columns, assuming both inputs are in sorted @@ -465,11 +559,9 @@ type mergeJoinBase struct { right mergeJoinInput // Output buffer definition. - output coldata.Batch - outputTypes []*types.T - // outputReady is a flag to indicate that merge joiner is ready to emit an - // output batch. - outputReady bool + output coldata.Batch + outputCapacity int + outputTypes []*types.T // Local buffer for the "working" repeated groups. groups circularGroupsBuffer @@ -492,10 +584,8 @@ func (o *mergeJoinBase) Reset(ctx context.Context) { if r, ok := o.right.source.(colexecop.Resetter); ok { r.Reset(ctx) } - o.outputReady = false o.state = mjEntry o.bufferedGroup.helper.Reset(ctx) - o.bufferedGroup.needToReset = false o.proberState.lBatch = nil o.proberState.rBatch = nil o.resetBuilderCrossProductState() @@ -506,10 +596,10 @@ func (o *mergeJoinBase) Init(ctx context.Context) { return } o.outputTypes = o.joinType.MakeOutputTypes(o.left.sourceTypes, o.right.sourceTypes) - o.bufferedGroup.left.firstTuple = o.unlimitedAllocator.NewMemBatchWithFixedCapacity( + o.bufferedGroup.leftFirstTuple = o.unlimitedAllocator.NewMemBatchWithFixedCapacity( o.left.sourceTypes, 1, /* capacity */ ).ColVecs() - o.bufferedGroup.right.firstTuple = o.unlimitedAllocator.NewMemBatchWithFixedCapacity( + o.bufferedGroup.rightFirstTuple = o.unlimitedAllocator.NewMemBatchWithFixedCapacity( o.right.sourceTypes, 1, /* capacity */ ).ColVecs() o.bufferedGroup.helper = newCrossJoinerBase( @@ -532,40 +622,49 @@ func (o *mergeJoinBase) resetBuilderCrossProductState() { o.builderState.right.reset() } -// appendToBufferedGroup appends all the tuples from batch that are part of the -// same group as the ones in the buffered group that corresponds to the input -// source. This needs to happen when a group starts at the end of an input +// startLeftBufferedGroup initializes the left buffered group. It will set the +// first tuple in case the left buffered group doesn't end in the current left +// batch. +func (o *mergeJoinBase) startLeftBufferedGroup(sel []int, groupStartIdx int, groupLength int) { + if groupStartIdx+groupLength < o.proberState.lLength { + // The left buffered group is complete within the current left batch, so + // we don't need to copy the first tuple. + return + } + o.unlimitedAllocator.PerformOperation(o.bufferedGroup.leftFirstTuple, func() { + for colIdx := range o.left.sourceTypes { + o.bufferedGroup.leftFirstTuple[colIdx].Copy( + coldata.SliceArgs{ + Src: o.proberState.lBatch.ColVec(colIdx), + Sel: sel, + DestIdx: 0, + SrcStartIdx: groupStartIdx, + SrcEndIdx: groupStartIdx + 1, + }, + ) + } + }) +} + +// appendToRightBufferedGroup appends the tuples in +// [groupStartIdx; groupStartIdx+groupLength) range from the current right +// batch. This needs to happen when a group starts at the end of an input // batch and can continue into the following batches. +// // A zero-length batch needs to be appended when no more batches will be -// appended to the buffered group. -func (o *mergeJoinBase) appendToBufferedGroup( - input *mergeJoinInput, batch coldata.Batch, sel []int, groupStartIdx int, groupLength int, -) { - var ( - bufferedGroup *mjBufferedGroup - sourceTypes []*types.T - bufferedTuples *colexecutils.SpillingQueue - numBufferedTuples int - ) - if input == &o.left { - sourceTypes = o.left.sourceTypes - bufferedGroup = &o.bufferedGroup.left - bufferedTuples = o.bufferedGroup.helper.left.tuples - numBufferedTuples = o.bufferedGroup.helper.left.numTuples - o.bufferedGroup.helper.left.numTuples += groupLength - } else { - sourceTypes = o.right.sourceTypes - bufferedGroup = &o.bufferedGroup.right - bufferedTuples = o.bufferedGroup.helper.right.tuples - numBufferedTuples = o.bufferedGroup.helper.right.numTuples - o.bufferedGroup.helper.right.numTuples += groupLength - } - if batch.Length() == 0 || groupLength == 0 { +// appended to the buffered group (which can be achieved by specifying an empty +// range with groupLength == 0). +func (o *mergeJoinBase) appendToRightBufferedGroup(sel []int, groupStartIdx int, groupLength int) { + bufferedTuples := o.bufferedGroup.helper.rightTuples + if groupLength == 0 { // We have finished appending to this buffered group, so we need to // Enqueue a zero-length batch per the contract of the spilling queue. bufferedTuples.Enqueue(o.Ctx, coldata.ZeroBatch) return } + sourceTypes := o.right.sourceTypes + numBufferedTuples := o.bufferedGroup.helper.numRightTuples + o.bufferedGroup.helper.numRightTuples += groupLength // TODO(yuzefovich): for LEFT/RIGHT ANTI joins we only need to store the // first tuple (in order to find the boundaries of the groups) since all // of the buffered tuples do have a match and, thus, don't contribute to @@ -574,12 +673,15 @@ func (o *mergeJoinBase) appendToBufferedGroup( // tuples from the left side and count the number of tuples on the right. // TODO(yuzefovich): for LEFT/RIGHT SEMI joins we only need to buffer tuples // from one side (left/right respectively). - if numBufferedTuples == 0 { - o.unlimitedAllocator.PerformOperation(bufferedGroup.firstTuple, func() { + if numBufferedTuples == 0 && groupStartIdx+groupLength == o.proberState.rLength { + // Set the right first tuple only if this is the first call to this + // method for the current right buffered group and if the group doesn't + // end in the current batch. + o.unlimitedAllocator.PerformOperation(o.bufferedGroup.rightFirstTuple, func() { for colIdx := range sourceTypes { - bufferedGroup.firstTuple[colIdx].Copy( + o.bufferedGroup.rightFirstTuple[colIdx].Copy( coldata.SliceArgs{ - Src: batch.ColVec(colIdx), + Src: o.proberState.rBatch.ColVec(colIdx), Sel: sel, DestIdx: 0, SrcStartIdx: groupStartIdx, @@ -593,14 +695,16 @@ func (o *mergeJoinBase) appendToBufferedGroup( // We don't impose any memory limits on the scratch batch because we rely on // the inputs to the merge joiner to produce reasonably sized batches. const maxBatchMemSize = math.MaxInt64 - bufferedGroup.scratchBatch, _ = o.unlimitedAllocator.ResetMaybeReallocate( - input.sourceTypes, bufferedGroup.scratchBatch, groupLength, maxBatchMemSize, + o.bufferedGroup.rightScratchBatch, _ = o.unlimitedAllocator.ResetMaybeReallocate( + sourceTypes, o.bufferedGroup.rightScratchBatch, groupLength, maxBatchMemSize, ) - o.unlimitedAllocator.PerformOperation(bufferedGroup.scratchBatch.ColVecs(), func() { - for colIdx := range input.sourceTypes { - bufferedGroup.scratchBatch.ColVec(colIdx).Copy( + // TODO(yuzefovich): SpillingQueue.Enqueue deep-copies the batch too. Think + // through whether the copy here can be avoided altogether. + o.unlimitedAllocator.PerformOperation(o.bufferedGroup.rightScratchBatch.ColVecs(), func() { + for colIdx := range sourceTypes { + o.bufferedGroup.rightScratchBatch.ColVec(colIdx).Copy( coldata.SliceArgs{ - Src: batch.ColVec(colIdx), + Src: o.proberState.rBatch.ColVec(colIdx), Sel: sel, DestIdx: 0, SrcStartIdx: groupStartIdx, @@ -608,83 +712,105 @@ func (o *mergeJoinBase) appendToBufferedGroup( }, ) } - bufferedGroup.scratchBatch.SetLength(groupLength) + o.bufferedGroup.rightScratchBatch.SetLength(groupLength) }) - bufferedTuples.Enqueue(o.Ctx, bufferedGroup.scratchBatch) + bufferedTuples.Enqueue(o.Ctx, o.bufferedGroup.rightScratchBatch) } -// setBuilderSourceToBatch sets the builder state to use groups from the -// circular group buffer and the batches from input. This happens when we have -// groups that are fully contained within a single input batch from each of the -// sources. -func (o *mergeJoinBase) setBuilderSourceToBatch() { - o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() - o.builderState.buildFrom = mjBuildFromBatch +// sourceFinished returns true if either of input sources has no more rows. +func (o *mergeJoinBase) sourceFinished() bool { + return o.proberState.lLength == 0 || o.proberState.rLength == 0 } -// initProberState sets the batches, lengths, and current indices to the right -// locations given the last iteration of the operator. -func (o *mergeJoinBase) initProberState() { - // If this is the first batch or we're done with the current batch, get the - // next batch. - if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { - o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() - o.proberState.lLength = o.proberState.lBatch.Length() - } - if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { - o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() - o.proberState.rLength = o.proberState.rBatch.Length() +// continueLeftBufferedGroup fetches the next batch from the left input and +// and updates the probing and buffered group states accordingly. +func (o *mergeJoinBase) continueLeftBufferedGroup() { + // Get the next batch from the left. + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + o.bufferedGroup.leftGroupStartIdx = 0 + if o.proberState.lLength == 0 { + // The left input has been fully exhausted. + return } - if o.bufferedGroup.needToReset { - o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + // Check whether the first tuple of this batch is still part of the left + // buffered group. + if o.isBufferedGroupFinished(&o.left, o.bufferedGroup.leftFirstTuple, o.proberState.lBatch, 0 /* rowIdx */) { + return } -} -// nonEmptyBufferedGroup returns true if there is a buffered group that needs -// to be finished. -func (o *mergeJoinBase) nonEmptyBufferedGroup() bool { - return o.bufferedGroup.helper.left.numTuples > 0 || o.bufferedGroup.helper.right.numTuples > 0 -} + // It is ok that we might call Init() multiple times - it'll be a noop after + // the first one. + o.left.distincter.Init(o.Ctx) + o.left.distincter.(colexecop.Resetter).Reset(o.Ctx) + // Ignore the first row of the distincter since we already know that we are + // in the same group and, thus, the row is not distinct, regardless of what + // the distincter outputs. + groupLength := 1 + var sel []int + o.left.distincterInput.SetBatch(o.proberState.lBatch) + o.left.distincter.Next() + + sel = o.proberState.lBatch.Selection() + if sel != nil { + for ; groupLength < o.proberState.lLength; groupLength++ { + if o.left.distinctOutput[sel[groupLength]] { + // We found the beginning of a new group! + break + } + } + } else { + for ; groupLength < o.proberState.lLength; groupLength++ { + if o.left.distinctOutput[groupLength] { + // We found the beginning of a new group! + break + } + } + } -// sourceFinished returns true if either of input sources has no more rows. -func (o *mergeJoinBase) sourceFinished() bool { - return o.proberState.lLength == 0 || o.proberState.rLength == 0 + // Zero out the distinct output for the next time we use the distincter on + // the left input. + copy(o.left.distinctOutput[:o.proberState.lLength], colexecutils.ZeroBoolColumn) + o.proberState.lIdx += groupLength } -// finishBufferedGroup appends a zero-length batch to the buffered group which -// is required by the contract of the spilling queue. -func (o *mergeJoinBase) finishBufferedGroup(input *mergeJoinInput) { - o.appendToBufferedGroup( - input, coldata.ZeroBatch, nil, /* sel */ - 0 /* groupStartIdx */, 0, /* groupLength */ +// finishRightBufferedGroup appends a zero-length batch to the right buffered +// group which is required by the contract of the spilling queue. Note that it +// is safe to call this method multiple times (only the first one is not a +// noop). +func (o *mergeJoinBase) finishRightBufferedGroup() { + o.appendToRightBufferedGroup( + nil /* sel */, 0 /* groupStartIdx */, 0, /* groupLength */ ) } -// completeBufferedGroup extends the buffered group corresponding to input. -// First, we check that the first row in batch is still part of the same group. -// If this is the case, we use the Distinct operator to find the first -// occurrence in batch (or subsequent batches) that doesn't match the current -// group. +// completeRightBufferedGroup extends the right buffered group. It will read all +// tuples from the right input that are part of the current right buffered group +// (which must have been initialized via appendToRightBufferedGroup). +// // NOTE: we will be buffering all batches until we find such non-matching tuple -// (or until we exhaust the input). -// TODO(yuzefovich): this can be refactored so that only the right side does -// unbounded buffering. -// SIDE EFFECT: can append to the buffered group corresponding to the source. -func (o *mergeJoinBase) completeBufferedGroup( - input *mergeJoinInput, batch coldata.Batch, rowIdx int, -) (_ coldata.Batch, idx int, batchLength int) { - batchLength = batch.Length() - if o.isBufferedGroupFinished(input, batch, rowIdx) { - o.finishBufferedGroup(input) - return batch, rowIdx, batchLength +// (or until we exhaust the right input). +func (o *mergeJoinBase) completeRightBufferedGroup() { + // Get the next batch from the right. + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() + // The right input has been fully exhausted. + if o.proberState.rLength == 0 { + o.finishRightBufferedGroup() + return + } + // Check whether the first tuple of this batch is still part of the right + // buffered group. + if o.isBufferedGroupFinished(&o.right, o.bufferedGroup.rightFirstTuple, o.proberState.rBatch, 0 /* rowIdx */) { + o.finishRightBufferedGroup() + return } isBufferedGroupComplete := false // It is ok that we might call Init() multiple times - it'll be a noop after // the first one. - input.distincter.Init(o.Ctx) - input.distincter.(colexecop.Resetter).Reset(o.Ctx) + o.right.distincter.Init(o.Ctx) + o.right.distincter.(colexecop.Resetter).Reset(o.Ctx) // Ignore the first row of the distincter in the first pass since we already // know that we are in the same group and, thus, the row is not distinct, // regardless of what the distincter outputs. @@ -696,22 +822,22 @@ func (o *mergeJoinBase) completeBufferedGroup( // previous iterations had only the matching tuples to the buffered group, // so the distincter - in a sense - compares the incoming tuples to the // first tuple of the first iteration (which we know is the same group). - input.distincterInput.SetBatch(batch) - input.distincter.Next() + o.right.distincterInput.SetBatch(o.proberState.rBatch) + o.right.distincter.Next() - sel = batch.Selection() + sel = o.proberState.rBatch.Selection() var groupLength int if sel != nil { - for groupLength = loopStartIndex; groupLength < batchLength; groupLength++ { - if input.distinctOutput[sel[groupLength]] { + for groupLength = loopStartIndex; groupLength < o.proberState.rLength; groupLength++ { + if o.right.distinctOutput[sel[groupLength]] { // We found the beginning of a new group! isBufferedGroupComplete = true break } } } else { - for groupLength = loopStartIndex; groupLength < batchLength; groupLength++ { - if input.distinctOutput[groupLength] { + for groupLength = loopStartIndex; groupLength < o.proberState.rLength; groupLength++ { + if o.right.distinctOutput[groupLength] { // We found the beginning of a new group! isBufferedGroupComplete = true break @@ -720,42 +846,26 @@ func (o *mergeJoinBase) completeBufferedGroup( } // Zero out the distinct output for the next pass. - copy(input.distinctOutput[:batchLength], colexecutils.ZeroBoolColumn) + copy(o.right.distinctOutput[:o.proberState.rLength], colexecutils.ZeroBoolColumn) loopStartIndex = 0 // Buffer all the tuples that are part of the buffered group. - o.appendToBufferedGroup(input, batch, sel, rowIdx, groupLength) - rowIdx += groupLength + o.appendToRightBufferedGroup(sel, o.proberState.rIdx, groupLength) + o.proberState.rIdx += groupLength if !isBufferedGroupComplete { // The buffered group is still not complete which means that we have // just appended all the tuples from batch to it, so we need to get a // fresh batch from the input. - rowIdx, batch = 0, input.source.Next() - batchLength = batch.Length() - if batchLength == 0 { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() + if o.proberState.rLength == 0 { // The input has been exhausted, so the buffered group is now complete. isBufferedGroupComplete = true - o.finishBufferedGroup(input) } } } - - return batch, rowIdx, batchLength -} - -// finishProbe completes the buffered groups on both sides of the input. -func (o *mergeJoinBase) finishProbe() { - o.proberState.lBatch, o.proberState.lIdx, o.proberState.lLength = o.completeBufferedGroup( - &o.left, - o.proberState.lBatch, - o.proberState.lIdx, - ) - o.proberState.rBatch, o.proberState.rIdx, o.proberState.rLength = o.completeBufferedGroup( - &o.right, - o.proberState.rBatch, - o.proberState.rIdx, - ) + o.finishRightBufferedGroup() } func (o *mergeJoinBase) Close() error { diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go index db453acee5f5..083dded076b4 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinExceptAllOp{} func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -214,16 +213,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -437,16 +436,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -652,16 +651,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -899,16 +898,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1154,16 +1153,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1410,16 +1409,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1693,16 +1692,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1948,16 +1947,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2170,16 +2169,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2403,16 +2402,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2630,16 +2629,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2727,7 +2726,6 @@ EqLoop: func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2893,16 +2891,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3116,16 +3114,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3331,16 +3329,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3578,16 +3576,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3833,16 +3831,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4089,16 +4087,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4372,16 +4370,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4627,16 +4625,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4849,16 +4847,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5082,16 +5080,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5309,16 +5307,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5406,7 +5404,6 @@ EqLoop: func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -5572,16 +5569,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5795,16 +5792,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6010,16 +6007,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6257,16 +6254,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6512,16 +6509,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6768,16 +6765,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7051,16 +7048,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7306,16 +7303,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7528,16 +7525,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7761,16 +7758,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7988,16 +7985,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8085,7 +8082,6 @@ EqLoop: func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -8251,16 +8247,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8474,16 +8470,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8689,16 +8685,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8936,16 +8932,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9191,16 +9187,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9447,16 +9443,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9730,16 +9726,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9985,16 +9981,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10207,16 +10203,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10440,16 +10436,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10667,16 +10663,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10790,7 +10786,6 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -10838,8 +10833,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10903,8 +10898,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10968,8 +10963,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11032,8 +11027,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11093,8 +11088,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11155,8 +11150,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11220,8 +11215,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11285,8 +11280,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11350,8 +11345,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11415,8 +11410,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11480,8 +11475,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11554,8 +11549,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11618,8 +11613,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11682,8 +11677,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11745,8 +11740,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11805,8 +11800,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11866,8 +11861,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11930,8 +11925,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11994,8 +11989,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12058,8 +12053,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12122,8 +12117,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12186,8 +12181,8 @@ func (o *mergeJoinExceptAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12258,7 +12253,6 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -12294,8 +12288,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12361,8 +12355,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12428,8 +12422,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12494,8 +12488,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12557,8 +12551,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12621,8 +12615,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12688,8 +12682,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12755,8 +12749,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12822,8 +12816,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12889,8 +12883,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12956,8 +12950,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13033,8 +13027,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13100,8 +13094,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13167,8 +13161,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13233,8 +13227,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13296,8 +13290,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13360,8 +13354,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13427,8 +13421,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13494,8 +13488,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13561,8 +13555,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13628,8 +13622,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13695,8 +13689,8 @@ func (o *mergeJoinExceptAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -13780,20 +13774,6 @@ func (o *mergeJoinExceptAllOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinExceptAllOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -13820,14 +13800,13 @@ func (o *mergeJoinExceptAllOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinExceptAllOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinExceptAllOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { if !groups[i].unmatched { // "Matched" groups are not outputted in LEFT ANTI, RIGHT ANTI, @@ -13835,51 +13814,152 @@ func (o *mergeJoinExceptAllOp) calculateOutputCount(groups []group) int { // they do not contribute to the output count. continue } - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinExceptAllOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinExceptAllOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups - } + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinExceptAllOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + // For EXCEPT ALL joins we build # left tuples - # right tuples output rows + // (if positive), so we have to discard first numRightTuples rows from the + // left. + numSkippedLeft := 0 + for { + groupLength := o.proberState.lIdx - o.bufferedGroup.leftGroupStartIdx + if numSkippedLeft+groupLength > o.bufferedGroup.helper.numRightTuples { + // The current left batch is the first one that contains tuples + // without a "match". + break + } + numSkippedLeft += groupLength + var groupFinished bool + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + groupFinished = true } else { - o.builderState.outFinished = true + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + groupFinished = o.proberState.lIdx == 0 } - o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) + if groupFinished { + // We have less matching tuples on the left than on the right, so we + // don't emit any output for this buffered group. + o.bufferedGroup.helper.Reset(o.Ctx) + o.state = mjEntry + return } + } + // We might need to skip some tuples in the current left batch since they + // still had matches with the right side. + toSkipInThisBatch := o.bufferedGroup.helper.numRightTuples - numSkippedLeft + startIdx := o.bufferedGroup.leftGroupStartIdx + toSkipInThisBatch - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinExceptAllOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) + } + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount + } else { + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) + } + o.builderState.outCount += willEmit + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false + } } } @@ -13887,30 +13967,29 @@ func (o *mergeJoinExceptAllOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] // At least one of the sources is finished. If it was the right one, @@ -13919,49 +13998,51 @@ func (o *mergeJoinExceptAllOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.lIdx < o.proberState.lLength { o.exhaustLeftSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the left into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go index a613f1b7086b..29a35664057a 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinFullOuterOp{} func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -211,16 +210,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -459,16 +458,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -691,16 +690,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -955,16 +954,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1238,16 +1237,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1522,16 +1521,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1833,16 +1832,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2124,16 +2123,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2370,16 +2369,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2620,16 +2619,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2870,16 +2869,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2989,7 +2988,6 @@ EqLoop: func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -3152,16 +3150,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3400,16 +3398,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3632,16 +3630,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3896,16 +3894,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4179,16 +4177,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4463,16 +4461,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4774,16 +4772,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5065,16 +5063,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5311,16 +5309,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5561,16 +5559,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5811,16 +5809,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5930,7 +5928,6 @@ EqLoop: func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -6093,16 +6090,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6341,16 +6338,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6573,16 +6570,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6837,16 +6834,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7120,16 +7117,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7404,16 +7401,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7715,16 +7712,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8006,16 +8003,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8252,16 +8249,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8502,16 +8499,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8752,16 +8749,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8871,7 +8868,6 @@ EqLoop: func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -9034,16 +9030,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9282,16 +9278,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9514,16 +9510,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9778,16 +9774,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10061,16 +10057,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10345,16 +10341,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10656,16 +10652,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -10947,16 +10943,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -11193,16 +11189,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -11443,16 +11439,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -11693,16 +11689,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -11838,7 +11834,6 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -11883,8 +11878,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -11948,8 +11943,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12013,8 +12008,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12077,8 +12072,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12138,8 +12133,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12200,8 +12195,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12265,8 +12260,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12330,8 +12325,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12395,8 +12390,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12460,8 +12455,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12525,8 +12520,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12599,8 +12594,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12663,8 +12658,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12727,8 +12722,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12790,8 +12785,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12850,8 +12845,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12911,8 +12906,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -12975,8 +12970,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -13039,8 +13034,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -13103,8 +13098,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -13167,8 +13162,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -13231,8 +13226,8 @@ func (o *mergeJoinFullOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -13306,7 +13301,6 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -13342,8 +13336,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13411,8 +13405,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13480,8 +13474,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13548,8 +13542,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13613,8 +13607,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13679,8 +13673,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13748,8 +13742,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13817,8 +13811,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13886,8 +13880,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -13955,8 +13949,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14024,8 +14018,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14103,8 +14097,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14172,8 +14166,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14241,8 +14235,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14309,8 +14303,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14374,8 +14368,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14440,8 +14434,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14509,8 +14503,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14578,8 +14572,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14647,8 +14641,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14716,8 +14710,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14785,8 +14779,8 @@ func (o *mergeJoinFullOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -14872,20 +14866,6 @@ func (o *mergeJoinFullOuterOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinFullOuterOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -14938,62 +14918,128 @@ func (o *mergeJoinFullOuterOp) exhaustRightSource() { o.proberState.rIdx = o.proberState.rLength } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinFullOuterOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinFullOuterOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinFullOuterOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinFullOuterOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinFullOuterOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinFullOuterOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -15001,30 +15047,29 @@ func (o *mergeJoinFullOuterOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] // At least one of the sources is finished. If it was the right one, @@ -15033,10 +15078,6 @@ func (o *mergeJoinFullOuterOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.lIdx < o.proberState.lLength { o.exhaustLeftSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the left into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } // At least one of the sources is finished. If it was the left one, // then we need to emit remaining tuples from the right source with @@ -15044,49 +15085,51 @@ func (o *mergeJoinFullOuterOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.rIdx < o.proberState.rLength { o.exhaustRightSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the right into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go index c9d8cf562b80..0ff915b3120c 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinInnerOp{} func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -189,16 +188,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -334,16 +333,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -479,16 +478,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -656,16 +655,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -830,16 +829,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1005,16 +1004,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1207,16 +1206,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1373,16 +1372,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1518,16 +1517,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1681,16 +1680,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1832,16 +1831,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1882,7 +1881,6 @@ EqLoop: func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2023,16 +2021,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2168,16 +2166,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2313,16 +2311,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2490,16 +2488,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2664,16 +2662,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2839,16 +2837,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3041,16 +3039,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3207,16 +3205,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3352,16 +3350,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3515,16 +3513,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3666,16 +3664,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3716,7 +3714,6 @@ EqLoop: func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -3857,16 +3854,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4002,16 +3999,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4147,16 +4144,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4324,16 +4321,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4498,16 +4495,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4673,16 +4670,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4875,16 +4872,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5041,16 +5038,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5186,16 +5183,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5349,16 +5346,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5500,16 +5497,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5550,7 +5547,6 @@ EqLoop: func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -5691,16 +5687,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5836,16 +5832,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5981,16 +5977,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6158,16 +6154,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6332,16 +6328,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6507,16 +6503,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6709,16 +6705,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6875,16 +6871,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7020,16 +7016,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7183,16 +7179,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7334,16 +7330,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7410,7 +7406,6 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -7455,8 +7450,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7517,8 +7512,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7579,8 +7574,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7640,8 +7635,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7698,8 +7693,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7757,8 +7752,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7819,8 +7814,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7881,8 +7876,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7943,8 +7938,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8005,8 +8000,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8067,8 +8062,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8138,8 +8133,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8199,8 +8194,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8260,8 +8255,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8320,8 +8315,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8377,8 +8372,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8435,8 +8430,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8496,8 +8491,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8557,8 +8552,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8618,8 +8613,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8679,8 +8674,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8740,8 +8735,8 @@ func (o *mergeJoinInnerOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8812,7 +8807,6 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -8848,8 +8842,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8915,8 +8909,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8982,8 +8976,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9048,8 +9042,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9111,8 +9105,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9175,8 +9169,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9242,8 +9236,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9309,8 +9303,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9376,8 +9370,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9443,8 +9437,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9510,8 +9504,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9587,8 +9581,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9654,8 +9648,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9721,8 +9715,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9787,8 +9781,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9850,8 +9844,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9914,8 +9908,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9981,8 +9975,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10048,8 +10042,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10115,8 +10109,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10182,8 +10176,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10249,8 +10243,8 @@ func (o *mergeJoinInnerOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10334,20 +10328,6 @@ func (o *mergeJoinInnerOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinInnerOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -10362,62 +10342,128 @@ func (o *mergeJoinInnerOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinInnerOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinInnerOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinInnerOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinInnerOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinInnerOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinInnerOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -10425,65 +10471,75 @@ func (o *mergeJoinInnerOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + o.builderState.lGroups = o.builderState.lGroups[:0] + o.builderState.rGroups = o.builderState.rGroups[:0] + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go index 11c0e2bfde2d..e785de22a4c0 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinIntersectAllOp{} func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -204,16 +203,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -368,16 +367,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -532,16 +531,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -728,16 +727,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -921,16 +920,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1115,16 +1114,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1336,16 +1335,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1521,16 +1520,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1685,16 +1684,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1867,16 +1866,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2037,16 +2036,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2091,7 +2090,6 @@ EqLoop: func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2247,16 +2245,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2411,16 +2409,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2575,16 +2573,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2771,16 +2769,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2964,16 +2962,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3158,16 +3156,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3379,16 +3377,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3564,16 +3562,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3728,16 +3726,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3910,16 +3908,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4080,16 +4078,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4134,7 +4132,6 @@ EqLoop: func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -4290,16 +4287,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4454,16 +4451,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4618,16 +4615,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4814,16 +4811,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5007,16 +5004,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5201,16 +5198,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5422,16 +5419,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5607,16 +5604,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5771,16 +5768,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5953,16 +5950,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6123,16 +6120,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6177,7 +6174,6 @@ EqLoop: func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -6333,16 +6329,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6497,16 +6493,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6661,16 +6657,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6857,16 +6853,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7050,16 +7046,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7244,16 +7240,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7465,16 +7461,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7650,16 +7646,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7814,16 +7810,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7996,16 +7992,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8166,16 +8162,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8246,7 +8242,6 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -8291,8 +8286,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8353,8 +8348,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8415,8 +8410,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8476,8 +8471,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8534,8 +8529,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8593,8 +8588,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8655,8 +8650,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8717,8 +8712,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8779,8 +8774,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8841,8 +8836,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8903,8 +8898,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8974,8 +8969,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9035,8 +9030,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9096,8 +9091,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9156,8 +9151,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9213,8 +9208,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9271,8 +9266,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9332,8 +9327,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9393,8 +9388,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9454,8 +9449,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9515,8 +9510,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9576,8 +9571,8 @@ func (o *mergeJoinIntersectAllOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9648,7 +9643,6 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -9684,8 +9678,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9751,8 +9745,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9818,8 +9812,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9884,8 +9878,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9947,8 +9941,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10011,8 +10005,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10078,8 +10072,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10145,8 +10139,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10212,8 +10206,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10279,8 +10273,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10346,8 +10340,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10423,8 +10417,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10490,8 +10484,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10557,8 +10551,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10623,8 +10617,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10686,8 +10680,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10750,8 +10744,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10817,8 +10811,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10884,8 +10878,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10951,8 +10945,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11018,8 +11012,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11085,8 +11079,8 @@ func (o *mergeJoinIntersectAllOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11170,20 +11164,6 @@ func (o *mergeJoinIntersectAllOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinIntersectAllOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -11198,60 +11178,134 @@ func (o *mergeJoinIntersectAllOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinIntersectAllOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinIntersectAllOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinIntersectAllOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinIntersectAllOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinIntersectAllOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinIntersectAllOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if bg.helper.builderState.numEmittedTotal == bg.helper.numRightTuples { + // For INTERSECT ALL joins we build min(# left tuples, # right + // tuples), and we have already reached the number of tuples + // from the right. Thus, we have to skip all tuples from the + // left that are part of the buffered group since they don't + // have a match. + skipLeftBufferedGroup = true + } + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -11259,65 +11313,75 @@ func (o *mergeJoinIntersectAllOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + o.builderState.lGroups = o.builderState.lGroups[:0] + o.builderState.rGroups = o.builderState.rGroups[:0] + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go index b674d672e283..ddec5991258b 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinLeftAntiOp{} func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -200,16 +199,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -396,16 +395,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -584,16 +583,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -804,16 +803,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1032,16 +1031,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1261,16 +1260,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1517,16 +1516,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1745,16 +1744,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1940,16 +1939,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2146,16 +2145,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2346,16 +2345,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2430,7 +2429,6 @@ EqLoop: func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2582,16 +2580,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2778,16 +2776,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2966,16 +2964,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3186,16 +3184,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3414,16 +3412,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3643,16 +3641,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3899,16 +3897,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4127,16 +4125,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4322,16 +4320,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4528,16 +4526,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4728,16 +4726,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4812,7 +4810,6 @@ EqLoop: func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -4964,16 +4961,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5160,16 +5157,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5348,16 +5345,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5568,16 +5565,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5796,16 +5793,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6025,16 +6022,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6281,16 +6278,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6509,16 +6506,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6704,16 +6701,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6910,16 +6907,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7110,16 +7107,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7194,7 +7191,6 @@ EqLoop: func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -7346,16 +7342,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7542,16 +7538,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7730,16 +7726,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7950,16 +7946,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8178,16 +8174,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8407,16 +8403,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8663,16 +8659,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8891,16 +8887,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9086,16 +9082,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9292,16 +9288,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9492,16 +9488,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9602,7 +9598,6 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -9650,8 +9645,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9715,8 +9710,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9780,8 +9775,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9844,8 +9839,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9905,8 +9900,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9967,8 +9962,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10032,8 +10027,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10097,8 +10092,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10162,8 +10157,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10227,8 +10222,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10292,8 +10287,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10366,8 +10361,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10430,8 +10425,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10494,8 +10489,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10557,8 +10552,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10617,8 +10612,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10678,8 +10673,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10742,8 +10737,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10806,8 +10801,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10870,8 +10865,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10934,8 +10929,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10998,8 +10993,8 @@ func (o *mergeJoinLeftAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11070,7 +11065,6 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -11106,8 +11100,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11173,8 +11167,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11240,8 +11234,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11306,8 +11300,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11369,8 +11363,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11433,8 +11427,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11500,8 +11494,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11567,8 +11561,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11634,8 +11628,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11701,8 +11695,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11768,8 +11762,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11845,8 +11839,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11912,8 +11906,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11979,8 +11973,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12045,8 +12039,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12108,8 +12102,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12172,8 +12166,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12239,8 +12233,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12306,8 +12300,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12373,8 +12367,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12440,8 +12434,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12507,8 +12501,8 @@ func (o *mergeJoinLeftAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12592,20 +12586,6 @@ func (o *mergeJoinLeftAntiOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinLeftAntiOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -12632,14 +12612,13 @@ func (o *mergeJoinLeftAntiOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinLeftAntiOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinLeftAntiOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { if !groups[i].unmatched { // "Matched" groups are not outputted in LEFT ANTI, RIGHT ANTI, @@ -12647,51 +12626,118 @@ func (o *mergeJoinLeftAntiOp) calculateOutputCount(groups []group) int { // they do not contribute to the output count. continue } - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinLeftAntiOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinLeftAntiOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinLeftAntiOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinLeftAntiOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -12699,30 +12745,29 @@ func (o *mergeJoinLeftAntiOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] // At least one of the sources is finished. If it was the right one, @@ -12731,49 +12776,51 @@ func (o *mergeJoinLeftAntiOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.lIdx < o.proberState.lLength { o.exhaustLeftSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the left into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go index fd05374386f7..823ca30d0842 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinLeftOuterOp{} func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -200,16 +199,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -397,16 +396,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -586,16 +585,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -807,16 +806,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1036,16 +1035,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1266,16 +1265,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1523,16 +1522,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1752,16 +1751,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1948,16 +1947,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2155,16 +2154,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2356,16 +2355,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2441,7 +2440,6 @@ EqLoop: func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2593,16 +2591,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2790,16 +2788,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2979,16 +2977,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3200,16 +3198,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3429,16 +3427,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3659,16 +3657,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3916,16 +3914,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4145,16 +4143,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4341,16 +4339,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4548,16 +4546,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4749,16 +4747,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4834,7 +4832,6 @@ EqLoop: func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -4986,16 +4983,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5183,16 +5180,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5372,16 +5369,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5593,16 +5590,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5822,16 +5819,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6052,16 +6049,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6309,16 +6306,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6538,16 +6535,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6734,16 +6731,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6941,16 +6938,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7142,16 +7139,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7227,7 +7224,6 @@ EqLoop: func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -7379,16 +7375,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7576,16 +7572,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7765,16 +7761,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7986,16 +7982,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8215,16 +8211,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8445,16 +8441,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8702,16 +8698,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8931,16 +8927,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9127,16 +9123,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9334,16 +9330,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9535,16 +9531,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9646,7 +9642,6 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -9691,8 +9686,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9753,8 +9748,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9815,8 +9810,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9876,8 +9871,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9934,8 +9929,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9993,8 +9988,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10055,8 +10050,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10117,8 +10112,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10179,8 +10174,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10241,8 +10236,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10303,8 +10298,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10374,8 +10369,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10435,8 +10430,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10496,8 +10491,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10556,8 +10551,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10613,8 +10608,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10671,8 +10666,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10732,8 +10727,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10793,8 +10788,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10854,8 +10849,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10915,8 +10910,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10976,8 +10971,8 @@ func (o *mergeJoinLeftOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11048,7 +11043,6 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -11084,8 +11078,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11153,8 +11147,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11222,8 +11216,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11290,8 +11284,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11355,8 +11349,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11421,8 +11415,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11490,8 +11484,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11559,8 +11553,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11628,8 +11622,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11697,8 +11691,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11766,8 +11760,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11845,8 +11839,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11914,8 +11908,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -11983,8 +11977,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12051,8 +12045,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12116,8 +12110,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12182,8 +12176,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12251,8 +12245,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12320,8 +12314,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12389,8 +12383,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12458,8 +12452,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12527,8 +12521,8 @@ func (o *mergeJoinLeftOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if rightGroup.nullGroup { @@ -12614,20 +12608,6 @@ func (o *mergeJoinLeftOuterOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinLeftOuterOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -12662,62 +12642,128 @@ func (o *mergeJoinLeftOuterOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinLeftOuterOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinLeftOuterOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinLeftOuterOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinLeftOuterOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinLeftOuterOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinLeftOuterOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -12725,30 +12771,29 @@ func (o *mergeJoinLeftOuterOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] // At least one of the sources is finished. If it was the right one, @@ -12757,49 +12802,51 @@ func (o *mergeJoinLeftOuterOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.lIdx < o.proberState.lLength { o.exhaustLeftSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the left into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go index 1460f19892e7..7a1f6af3fa0e 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinLeftSemiOp{} func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -189,16 +188,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -333,16 +332,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -477,16 +476,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -653,16 +652,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -826,16 +825,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1000,16 +999,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1201,16 +1200,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1366,16 +1365,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1510,16 +1509,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1672,16 +1671,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1822,16 +1821,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1871,7 +1870,6 @@ EqLoop: func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2012,16 +2010,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2156,16 +2154,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2300,16 +2298,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2476,16 +2474,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2649,16 +2647,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2823,16 +2821,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3024,16 +3022,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3189,16 +3187,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3333,16 +3331,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3495,16 +3493,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3645,16 +3643,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3694,7 +3692,6 @@ EqLoop: func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -3835,16 +3832,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3979,16 +3976,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4123,16 +4120,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4299,16 +4296,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4472,16 +4469,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4646,16 +4643,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4847,16 +4844,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5012,16 +5009,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5156,16 +5153,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5318,16 +5315,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5468,16 +5465,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5517,7 +5514,6 @@ EqLoop: func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -5658,16 +5654,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5802,16 +5798,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5946,16 +5942,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6122,16 +6118,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6295,16 +6291,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6469,16 +6465,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6670,16 +6666,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6835,16 +6831,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6979,16 +6975,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7141,16 +7137,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7291,16 +7287,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7366,7 +7362,6 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -7411,8 +7406,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7473,8 +7468,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7535,8 +7530,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7596,8 +7591,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7654,8 +7649,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7713,8 +7708,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7775,8 +7770,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7837,8 +7832,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7899,8 +7894,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7961,8 +7956,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8023,8 +8018,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8094,8 +8089,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8155,8 +8150,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8216,8 +8211,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8276,8 +8271,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8333,8 +8328,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8391,8 +8386,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8452,8 +8447,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8513,8 +8508,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8574,8 +8569,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8635,8 +8630,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8696,8 +8691,8 @@ func (o *mergeJoinLeftSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8768,7 +8763,6 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -8804,8 +8798,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8871,8 +8865,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8938,8 +8932,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9004,8 +8998,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9067,8 +9061,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9131,8 +9125,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9198,8 +9192,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9265,8 +9259,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9332,8 +9326,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9399,8 +9393,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9466,8 +9460,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9543,8 +9537,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9610,8 +9604,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9677,8 +9671,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9743,8 +9737,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9806,8 +9800,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9870,8 +9864,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9937,8 +9931,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10004,8 +9998,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10071,8 +10065,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10138,8 +10132,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10205,8 +10199,8 @@ func (o *mergeJoinLeftSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10290,20 +10284,6 @@ func (o *mergeJoinLeftSemiOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinLeftSemiOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -10318,60 +10298,126 @@ func (o *mergeJoinLeftSemiOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinLeftSemiOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinLeftSemiOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinLeftSemiOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinLeftSemiOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinLeftSemiOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinLeftSemiOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -10379,65 +10425,75 @@ func (o *mergeJoinLeftSemiOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + o.builderState.lGroups = o.builderState.lGroups[:0] + o.builderState.rGroups = o.builderState.rGroups[:0] + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go index b4509265bfd2..224822d898b9 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinRightAntiOp{} func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -200,16 +199,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -395,16 +394,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -582,16 +581,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -801,16 +800,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1028,16 +1027,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1256,16 +1255,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1511,16 +1510,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1738,16 +1737,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1932,16 +1931,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2137,16 +2136,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2336,16 +2335,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2419,7 +2418,6 @@ EqLoop: func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2571,16 +2569,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2766,16 +2764,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2953,16 +2951,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3172,16 +3170,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3399,16 +3397,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3627,16 +3625,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3882,16 +3880,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4109,16 +4107,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4303,16 +4301,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4508,16 +4506,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4707,16 +4705,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4790,7 +4788,6 @@ EqLoop: func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -4942,16 +4939,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5137,16 +5134,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5324,16 +5321,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5543,16 +5540,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5770,16 +5767,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5998,16 +5995,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6253,16 +6250,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6480,16 +6477,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6674,16 +6671,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6879,16 +6876,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7078,16 +7075,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7161,7 +7158,6 @@ EqLoop: func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -7313,16 +7309,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7508,16 +7504,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7695,16 +7691,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7914,16 +7910,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8141,16 +8137,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8369,16 +8365,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8624,16 +8620,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8851,16 +8847,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9045,16 +9041,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9250,16 +9246,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9449,16 +9445,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9558,7 +9554,6 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -9603,8 +9598,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9668,8 +9663,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9733,8 +9728,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9797,8 +9792,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9858,8 +9853,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9920,8 +9915,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9985,8 +9980,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10050,8 +10045,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10115,8 +10110,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10180,8 +10175,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10245,8 +10240,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10319,8 +10314,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10383,8 +10378,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10447,8 +10442,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10510,8 +10505,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10570,8 +10565,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10631,8 +10626,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10695,8 +10690,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10759,8 +10754,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10823,8 +10818,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10887,8 +10882,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10951,8 +10946,8 @@ func (o *mergeJoinRightAntiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -11026,7 +11021,6 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -11062,8 +11056,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11129,8 +11123,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11196,8 +11190,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11262,8 +11256,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11325,8 +11319,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11389,8 +11383,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11456,8 +11450,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11523,8 +11517,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11590,8 +11584,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11657,8 +11651,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11724,8 +11718,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11801,8 +11795,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11868,8 +11862,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11935,8 +11929,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12001,8 +11995,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12064,8 +12058,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12128,8 +12122,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12195,8 +12189,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12262,8 +12256,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12329,8 +12323,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12396,8 +12390,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12463,8 +12457,8 @@ func (o *mergeJoinRightAntiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12548,20 +12542,6 @@ func (o *mergeJoinRightAntiOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinRightAntiOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -12586,14 +12566,13 @@ func (o *mergeJoinRightAntiOp) exhaustRightSource() { o.proberState.rIdx = o.proberState.rLength } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinRightAntiOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinRightAntiOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { if !groups[i].unmatched { // "Matched" groups are not outputted in LEFT ANTI, RIGHT ANTI, @@ -12601,49 +12580,116 @@ func (o *mergeJoinRightAntiOp) calculateOutputCount(groups []group) int { // they do not contribute to the output count. continue } - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinRightAntiOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinRightAntiOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.rGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + numBuilt := o.numBuiltFromBatch(o.builderState.rGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinRightAntiOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinRightAntiOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -12651,30 +12697,29 @@ func (o *mergeJoinRightAntiOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] // At least one of the sources is finished. If it was the left one, @@ -12683,49 +12728,51 @@ func (o *mergeJoinRightAntiOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.rIdx < o.proberState.rLength { o.exhaustRightSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the right into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go index 4bc582ea5d27..b5661129f242 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinRightOuterOp{} func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -200,16 +199,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -396,16 +395,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -584,16 +583,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -804,16 +803,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1032,16 +1031,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1261,16 +1260,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1517,16 +1516,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1745,16 +1744,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1940,16 +1939,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2146,16 +2145,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2346,16 +2345,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2430,7 +2429,6 @@ EqLoop: func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2582,16 +2580,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2778,16 +2776,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2966,16 +2964,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3186,16 +3184,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3414,16 +3412,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3643,16 +3641,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3899,16 +3897,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4127,16 +4125,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4322,16 +4320,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4528,16 +4526,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4728,16 +4726,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4812,7 +4810,6 @@ EqLoop: func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -4964,16 +4961,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5160,16 +5157,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5348,16 +5345,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5568,16 +5565,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5796,16 +5793,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6025,16 +6022,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6281,16 +6278,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6509,16 +6506,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6704,16 +6701,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6910,16 +6907,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7110,16 +7107,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7194,7 +7191,6 @@ EqLoop: func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -7346,16 +7342,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7542,16 +7538,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7730,16 +7726,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7950,16 +7946,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8178,16 +8174,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8407,16 +8403,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8663,16 +8659,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -8891,16 +8887,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9086,16 +9082,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9292,16 +9288,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9492,16 +9488,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -9602,7 +9598,6 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -9647,8 +9642,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9712,8 +9707,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9777,8 +9772,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9841,8 +9836,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9902,8 +9897,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -9964,8 +9959,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10029,8 +10024,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10094,8 +10089,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10159,8 +10154,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10224,8 +10219,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10289,8 +10284,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10363,8 +10358,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10427,8 +10422,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10491,8 +10486,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10554,8 +10549,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10614,8 +10609,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10675,8 +10670,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10739,8 +10734,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10803,8 +10798,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10867,8 +10862,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10931,8 +10926,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -10995,8 +10990,8 @@ func (o *mergeJoinRightOuterOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } if leftGroup.nullGroup { @@ -11070,7 +11065,6 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -11106,8 +11100,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11173,8 +11167,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11240,8 +11234,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11306,8 +11300,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11369,8 +11363,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11433,8 +11427,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11500,8 +11494,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11567,8 +11561,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11634,8 +11628,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11701,8 +11695,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11768,8 +11762,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11845,8 +11839,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11912,8 +11906,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -11979,8 +11973,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12045,8 +12039,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12108,8 +12102,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12172,8 +12166,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12239,8 +12233,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12306,8 +12300,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12373,8 +12367,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12440,8 +12434,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12507,8 +12501,8 @@ func (o *mergeJoinRightOuterOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -12592,20 +12586,6 @@ func (o *mergeJoinRightOuterOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinRightOuterOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -12638,62 +12618,128 @@ func (o *mergeJoinRightOuterOp) exhaustRightSource() { o.proberState.rIdx = o.proberState.rLength } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinRightOuterOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinRightOuterOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinRightOuterOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinRightOuterOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinRightOuterOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinRightOuterOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -12701,30 +12747,29 @@ func (o *mergeJoinRightOuterOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] // At least one of the sources is finished. If it was the left one, @@ -12733,49 +12778,51 @@ func (o *mergeJoinRightOuterOp) Next() coldata.Batch { // finished, then there is nothing left to do. if o.proberState.rIdx < o.proberState.rLength { o.exhaustRightSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the right into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go index a864e99a40b7..6eca76b2c3cc 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go @@ -48,7 +48,6 @@ var _ colexecop.Operator = &mergeJoinRightSemiOp{} func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -189,16 +188,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -332,16 +331,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -475,16 +474,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -650,16 +649,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -822,16 +821,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -995,16 +994,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1195,16 +1194,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1359,16 +1358,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1502,16 +1501,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1663,16 +1662,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1812,16 +1811,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -1860,7 +1859,6 @@ EqLoop: func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -2001,16 +1999,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2144,16 +2142,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2287,16 +2285,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2462,16 +2460,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2634,16 +2632,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -2807,16 +2805,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3007,16 +3005,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3171,16 +3169,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3314,16 +3312,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3475,16 +3473,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3624,16 +3622,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3672,7 +3670,6 @@ EqLoop: func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -3813,16 +3810,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -3956,16 +3953,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4099,16 +4096,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4274,16 +4271,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4446,16 +4443,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4619,16 +4616,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4819,16 +4816,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -4983,16 +4980,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5126,16 +5123,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5287,16 +5284,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5436,16 +5433,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5484,7 +5481,6 @@ EqLoop: func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -5625,16 +5621,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5768,16 +5764,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -5911,16 +5907,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6086,16 +6082,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6258,16 +6254,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6431,16 +6427,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6631,16 +6627,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6795,16 +6791,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -6938,16 +6934,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7099,16 +7095,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7248,16 +7244,16 @@ EqLoop: curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -7322,7 +7318,6 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -7367,8 +7362,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7429,8 +7424,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7491,8 +7486,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7552,8 +7547,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7610,8 +7605,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7669,8 +7664,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7731,8 +7726,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7793,8 +7788,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7855,8 +7850,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7917,8 +7912,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -7979,8 +7974,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8050,8 +8045,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8111,8 +8106,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8172,8 +8167,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8232,8 +8227,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8289,8 +8284,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8347,8 +8342,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8408,8 +8403,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8469,8 +8464,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8530,8 +8525,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8591,8 +8586,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8652,8 +8647,8 @@ func (o *mergeJoinRightSemiOp) buildLeftGroupsFromBatch( repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8724,7 +8719,6 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -8760,8 +8754,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8827,8 +8821,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8894,8 +8888,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -8960,8 +8954,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9023,8 +9017,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9087,8 +9081,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9154,8 +9148,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9221,8 +9215,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9288,8 +9282,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9355,8 +9349,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9422,8 +9416,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9499,8 +9493,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9566,8 +9560,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9633,8 +9627,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9699,8 +9693,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9762,8 +9756,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9826,8 +9820,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9893,8 +9887,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -9960,8 +9954,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10027,8 +10021,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10094,8 +10088,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10161,8 +10155,8 @@ func (o *mergeJoinRightSemiOp) buildRightGroupsFromBatch( o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } { @@ -10246,20 +10240,6 @@ func (o *mergeJoinRightSemiOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoinRightSemiOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -10274,58 +10254,129 @@ func (o *mergeJoinRightSemiOp) exhaustRightSource() { // ignored in all joins except for RIGHT OUTER and FULL OUTER. } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoinRightSemiOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoinRightSemiOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoinRightSemiOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoinRightSemiOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - o.builderState.outCount = o.calculateOutputCount(o.builderState.rGroups) - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + numBuilt := o.numBuiltFromBatch(o.builderState.rGroups) + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinRightSemiOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + startIdx := o.bufferedGroup.leftGroupStartIdx + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoinRightSemiOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + // For RIGHT SEMI joins we have already fully built the output based + // on all tuples in the right buffered group using the match from + // the current left batch. This allows us to simply skip all tuples + // that are part of the left buffered group. + skipLeftBufferedGroup = true + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true + } + if willEmit > 0 && len(o.outputTypes) != 0 { + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) } o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - } } @@ -10333,65 +10384,75 @@ func (o *mergeJoinRightSemiOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + o.builderState.lGroups = o.builderState.lGroups[:0] + o.builderState.rGroups = o.builderState.rGroups[:0] + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go b/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go index d509a9b0ac40..620994a13745 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go @@ -267,16 +267,16 @@ func _PROBE_SWITCH(_JOIN_TYPE joinTypeInfo, _SEL_PERMUTATION selPermutation) { / curRIdx++ } - // Last equality column and either group is incomplete. Save state - // and have it handled in the next iteration. + // Last equality column and either group is incomplete. if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { - o.appendToBufferedGroup(&o.left, o.proberState.lBatch, lSel, beginLIdx, lGroupLength) + // Store the state about the buffered group. + o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) + o.bufferedGroup.leftGroupStartIdx = beginLIdx o.proberState.lIdx = lGroupLength + beginLIdx - o.appendToBufferedGroup(&o.right, o.proberState.rBatch, rSel, beginRIdx, rGroupLength) + o.appendToRightBufferedGroup(rSel, beginRIdx, rGroupLength) o.proberState.rIdx = rGroupLength + beginRIdx - o.groups.finishedCol() - break EqLoop + return } if eqColIdx < len(o.left.eqCols)-1 { @@ -638,7 +638,6 @@ func _PROCESS_NOT_LAST_GROUP_IN_COLUMN_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} func (o *mergeJoin_JOIN_TYPE_STRINGOp) probeBodyLSel_IS_L_SELRSel_IS_R_SEL() { lSel := o.proberState.lBatch.Selection() rSel := o.proberState.rBatch.Selection() -EqLoop: for eqColIdx := 0; eqColIdx < len(o.left.eqCols); eqColIdx++ { leftColIdx := o.left.eqCols[eqColIdx] rightColIdx := o.right.eqCols[eqColIdx] @@ -706,8 +705,8 @@ func _LEFT_SWITCH(_JOIN_TYPE joinTypeInfo, _HAS_SELECTION bool) { // */}} repeatsLeft := leftGroup.numRepeats - o.builderState.left.numRepeatsIdx toAppend := repeatsLeft - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } // {{if or _JOIN_TYPE.IsRightOuter _JOIN_TYPE.IsRightAnti}} @@ -791,7 +790,6 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) buildLeftGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[:len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. LeftColLoop: for colIdx := range input.sourceTypes { @@ -844,8 +842,8 @@ func _RIGHT_SWITCH(_JOIN_TYPE joinTypeInfo, _HAS_SELECTION bool) { // */}} o.builderState.right.curSrcStartIdx = rightGroup.rowStartIdx } toAppend := rightGroup.rowEndIdx - o.builderState.right.curSrcStartIdx - if outStartIdx+toAppend > outputCapacity { - toAppend = outputCapacity - outStartIdx + if outStartIdx+toAppend > o.outputCapacity { + toAppend = o.outputCapacity - outStartIdx } // {{if _JOIN_TYPE.IsLeftOuter}} @@ -944,7 +942,6 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) buildRightGroupsFromBatch( o.unlimitedAllocator.PerformOperation( o.output.ColVecs()[colOffset:colOffset+len(input.sourceTypes)], func() { - outputCapacity := o.output.Capacity() // Loop over every column. RightColLoop: for colIdx := range input.sourceTypes { @@ -992,20 +989,6 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) probe() { } } -// setBuilderSourceToBufferedGroup sets up the builder state to use the -// buffered group. -func (o *mergeJoin_JOIN_TYPE_STRINGOp) setBuilderSourceToBufferedGroup() { - o.builderState.buildFrom = mjBuildFromBufferedGroup - o.bufferedGroup.helper.setupBuilder() - o.builderState.totalOutCountFromBufferedGroup = o.bufferedGroup.helper.calculateOutputCount() - o.builderState.alreadyEmittedFromBufferedGroup = 0 - - // We cannot yet reset the buffered groups because the builder will be taking - // input from them. The actual reset will take place on the next call to - // initProberState(). - o.bufferedGroup.needToReset = true -} - // exhaustLeftSource sets up the builder to process any remaining tuples from // the left source. It should only be called when the right source has been // exhausted. @@ -1082,14 +1065,13 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) exhaustRightSource() { // {{end}} } -// calculateOutputCount uses the toBuild field of each group and the output -// batch size to determine the output count. Note that as soon as a group is +// numBuiltFromBatch uses the toBuild field of each group and the output +// capacity to determine the output count. Note that as soon as a group is // materialized partially or fully to output, its toBuild field is updated -// accordingly. -func (o *mergeJoin_JOIN_TYPE_STRINGOp) calculateOutputCount(groups []group) int { - count := o.builderState.outCount - outputCapacity := o.output.Capacity() - +// accordingly. The number of tuples that will be built from batch during the +// current iteration is returned. +func (o *mergeJoin_JOIN_TYPE_STRINGOp) numBuiltFromBatch(groups []group) (numBuilt int) { + outCount := o.builderState.outCount for i := 0; i < len(groups); i++ { // {{if or _JOIN_TYPE.IsLeftAnti _JOIN_TYPE.IsRightAnti}} if !groups[i].unmatched { @@ -1099,65 +1081,190 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) calculateOutputCount(groups []group) int continue } // {{end}} - count += groups[i].toBuild + outCount += groups[i].toBuild groups[i].toBuild = 0 - if count > o.output.Capacity() { - groups[i].toBuild = count - outputCapacity - count = outputCapacity - return count + if outCount > o.outputCapacity { + groups[i].toBuild = outCount - o.outputCapacity + return o.outputCapacity - o.builderState.outCount } } - o.builderState.outFinished = true - return count + return outCount - o.builderState.outCount } -// build creates the cross product, and writes it to the output member. -func (o *mergeJoin_JOIN_TYPE_STRINGOp) build() { +// buildFromBatch builds as many output rows as possible from the groups that +// were complete in the probing batches. New rows are put starting at +// o.builderState.outCount position until either the capacity is reached or all +// groups are processed. +func (o *mergeJoin_JOIN_TYPE_STRINGOp) buildFromBatch() { outStartIdx := o.builderState.outCount - switch o.builderState.buildFrom { - case mjBuildFromBatch: - // {{if or _JOIN_TYPE.IsRightSemi _JOIN_TYPE.IsRightAnti}} - o.builderState.outCount = o.calculateOutputCount(o.builderState.rGroups) - // {{else}} - o.builderState.outCount = o.calculateOutputCount(o.builderState.lGroups) + // {{if or _JOIN_TYPE.IsRightSemi _JOIN_TYPE.IsRightAnti}} + numBuilt := o.numBuiltFromBatch(o.builderState.rGroups) + // {{else}} + numBuilt := o.numBuiltFromBatch(o.builderState.lGroups) + // {{end}} + o.builderState.outCount += numBuilt + if numBuilt > 0 && len(o.outputTypes) != 0 { + // We will be actually building the output if we have columns in the output + // batch (meaning that we're not doing query like 'SELECT count(*) ...') + // and when builderState.outCount has increased (meaning that we have + // something to build). + colOffsetForRightGroups := 0 + // {{if not (or _JOIN_TYPE.IsRightSemi _JOIN_TYPE.IsRightAnti)}} + o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) + colOffsetForRightGroups = len(o.left.sourceTypes) + _ = colOffsetForRightGroups // {{end}} - if o.output.Width() != 0 && o.builderState.outCount > outStartIdx { - // We will be actually building the output if we have columns in the output - // batch (meaning that we're not doing query like 'SELECT count(*) ...') - // and when builderState.outCount has increased (meaning that we have - // something to build). - colOffsetForRightGroups := 0 - // {{if not (or _JOIN_TYPE.IsRightSemi _JOIN_TYPE.IsRightAnti)}} - o.buildLeftGroupsFromBatch(o.builderState.lGroups, &o.left, o.proberState.lBatch, outStartIdx) - colOffsetForRightGroups = len(o.left.sourceTypes) - _ = colOffsetForRightGroups - // {{end}} - // {{if not (or _JOIN_TYPE.IsLeftSemi _JOIN_TYPE.IsLeftAnti)}} - o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + // {{if not (or _JOIN_TYPE.IsLeftSemi _JOIN_TYPE.IsLeftAnti)}} + o.buildRightGroupsFromBatch(o.builderState.rGroups, colOffsetForRightGroups, &o.right, o.proberState.rBatch, outStartIdx) + // {{end}} + } +} + +// transitionIntoBuildingFromBufferedGroup should be called once we have +// non-empty right buffered group in order to setup the buffered group builder. +// +// It will complete the right buffered group (meaning it'll read all batches +// from the right input until either the new group is found or the input is +// exhausted). +// +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoin_JOIN_TYPE_STRINGOp) transitionIntoBuildingFromBufferedGroup() { + if o.proberState.rIdx == o.proberState.rLength { + // The right buffered group might extend into the next batch, so we have + // to complete it first. + o.completeRightBufferedGroup() + } + + o.bufferedGroup.helper.setupLeftBuilder() + + // {{if and _JOIN_TYPE.IsLeftAnti _JOIN_TYPE.IsSetOp}} + // For EXCEPT ALL joins we build # left tuples - # right tuples output rows + // (if positive), so we have to discard first numRightTuples rows from the + // left. + numSkippedLeft := 0 + for { + groupLength := o.proberState.lIdx - o.bufferedGroup.leftGroupStartIdx + if numSkippedLeft+groupLength > o.bufferedGroup.helper.numRightTuples { + // The current left batch is the first one that contains tuples + // without a "match". + break + } + numSkippedLeft += groupLength + var groupFinished bool + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + groupFinished = true + } else { + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + groupFinished = o.proberState.lIdx == 0 + } + if groupFinished { + // We have less matching tuples on the left than on the right, so we + // don't emit any output for this buffered group. + o.bufferedGroup.helper.Reset(o.Ctx) + o.state = mjEntry + return + } + } + // We might need to skip some tuples in the current left batch since they + // still had matches with the right side. + toSkipInThisBatch := o.bufferedGroup.helper.numRightTuples - numSkippedLeft + startIdx := o.bufferedGroup.leftGroupStartIdx + toSkipInThisBatch + // {{else}} + startIdx := o.bufferedGroup.leftGroupStartIdx + // {{end}} + + o.bufferedGroup.helper.prepareForNextLeftBatch(o.proberState.lBatch, startIdx, o.proberState.lIdx) + o.state = mjBuildFromBufferedGroup +} + +// buildFromBufferedGroup builds the output based on the current buffered group +// and puts new tuples starting at position b.builderState.outCount. It returns +// true once the output for the buffered group has been fully populated. +// +// It is assumed that transitionIntoBuildingFromBufferedGroup has been called. +// +// For a more detailed explanation and an example please refer to the comment at +// the top of mergejoiner.go. +func (o *mergeJoin_JOIN_TYPE_STRINGOp) buildFromBufferedGroup() (bufferedGroupComplete bool) { + bg := &o.bufferedGroup + // Iterate until either we use up the whole capacity of the output batch or + // we complete the buffered group. + for { + if bg.helper.builderState.left.curSrcStartIdx == o.proberState.lLength { + // The output has been fully built from the current left batch. + bg.leftBatchDone = true + } + if bg.leftBatchDone { + // The current left batch has been fully processed with regards to + // the buffered group. + bg.leftBatchDone = false + if o.proberState.lIdx < o.proberState.lLength { + // The group on the left is finished within the current left + // batch. + return true + } + var skipLeftBufferedGroup bool + // {{if _JOIN_TYPE.IsRightSemi}} + // For RIGHT SEMI joins we have already fully built the output based + // on all tuples in the right buffered group using the match from + // the current left batch. This allows us to simply skip all tuples + // that are part of the left buffered group. + skipLeftBufferedGroup = true + // {{else if and _JOIN_TYPE.IsLeftSemi _JOIN_TYPE.IsSetOp}} + if bg.helper.builderState.numEmittedTotal == bg.helper.numRightTuples { + // For INTERSECT ALL joins we build min(# left tuples, # right + // tuples), and we have already reached the number of tuples + // from the right. Thus, we have to skip all tuples from the + // left that are part of the buffered group since they don't + // have a match. + skipLeftBufferedGroup = true + } // {{end}} + if skipLeftBufferedGroup { + // Keep fetching the next batch from the left input until we + // either find the start of the new group or we exhaust the + // input. + for o.proberState.lIdx == o.proberState.lLength && o.proberState.lLength > 0 { + o.continueLeftBufferedGroup() + } + return true + } + // Fetch the next batch from the left input and calculate the + // boundaries of the buffered group. + o.continueLeftBufferedGroup() + if o.proberState.lIdx == 0 { + return true + } + bg.helper.prepareForNextLeftBatch( + o.proberState.lBatch, bg.leftGroupStartIdx, o.proberState.lIdx, + ) } - case mjBuildFromBufferedGroup: - willEmit := o.builderState.totalOutCountFromBufferedGroup - o.builderState.alreadyEmittedFromBufferedGroup - if o.builderState.outCount+willEmit > o.output.Capacity() { - willEmit = o.output.Capacity() - o.builderState.outCount + willEmit := bg.helper.canEmit() + if o.builderState.outCount+willEmit > o.outputCapacity { + willEmit = o.outputCapacity - o.builderState.outCount } else { - o.builderState.outFinished = true + bg.leftBatchDone = true } - o.builderState.outCount += willEmit - o.builderState.alreadyEmittedFromBufferedGroup += willEmit - if o.output.Width() != 0 && willEmit > 0 { + if willEmit > 0 && len(o.outputTypes) != 0 { // {{if not (or _JOIN_TYPE.IsRightSemi _JOIN_TYPE.IsRightAnti)}} - o.bufferedGroup.helper.buildFromLeftInput(o.Ctx, outStartIdx) + bg.helper.buildFromLeftInput(o.Ctx, o.builderState.outCount) // {{end}} // {{if not (or _JOIN_TYPE.IsLeftSemi _JOIN_TYPE.IsLeftAnti)}} - o.bufferedGroup.helper.buildFromRightInput(o.Ctx, outStartIdx) + bg.helper.buildFromRightInput(o.Ctx, o.builderState.outCount) // {{end}} } - - default: - colexecerror.InternalError(errors.AssertionFailedf("unsupported mjBuildFrom %d", o.builderState.buildFrom)) - + o.builderState.outCount += willEmit + bg.helper.builderState.numEmittedCurLeftBatch += willEmit + bg.helper.builderState.numEmittedTotal += willEmit + if o.builderState.outCount == o.outputCapacity { + return false + } } } @@ -1167,18 +1274,8 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) build() { // builder. func _SOURCE_FINISHED_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} // {{define "sourceFinishedSwitch" -}} - o.outputReady = true - o.builderState.buildFrom = mjBuildFromBatch - // {{if or $.JoinType.IsInner (or $.JoinType.IsLeftSemi $.JoinType.IsRightSemi)}} - o.setBuilderSourceToBufferedGroup() - // {{else}} - // Next, we need to make sure that builder state is set up for a case when - // neither exhaustLeftSource nor exhaustRightSource is called below. In such - // scenario the merge joiner is done, so it'll be outputting zero-length - // batches from now on. o.builderState.lGroups = o.builderState.lGroups[:0] o.builderState.rGroups = o.builderState.rGroups[:0] - // {{end}} // {{if or $.JoinType.IsLeftOuter $.JoinType.IsLeftAnti}} // At least one of the sources is finished. If it was the right one, // then we need to emit remaining tuples from the left source with @@ -1186,10 +1283,6 @@ func _SOURCE_FINISHED_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} // finished, then there is nothing left to do. if o.proberState.lIdx < o.proberState.lLength { o.exhaustLeftSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the left into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } // {{end}} // {{if or $.JoinType.IsRightOuter $.JoinType.IsRightAnti}} @@ -1199,10 +1292,6 @@ func _SOURCE_FINISHED_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} // finished, then there is nothing left to do. if o.proberState.rIdx < o.proberState.rLength { o.exhaustRightSource() - // We unset o.outputReady here because we want to put as many unmatched - // tuples from the right into the output batch. Once outCount reaches the - // desired output batch size, the output will be returned. - o.outputReady = false } // {{end}} // {{end}} @@ -1215,63 +1304,74 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) Next() coldata.Batch { o.output, _ = o.unlimitedAllocator.ResetMaybeReallocate( o.outputTypes, o.output, 1 /* minDesiredCapacity */, o.memoryLimit, ) + o.outputCapacity = o.output.Capacity() o.bufferedGroup.helper.output = o.output + o.builderState.outCount = 0 for { switch o.state { case mjEntry: - o.initProberState() - - if o.nonEmptyBufferedGroup() { - o.state = mjFinishBufferedGroup - break + // If this is the first batch or we're done with the current batch, + // get the next batch. + if o.proberState.lBatch == nil || (o.proberState.lLength != 0 && o.proberState.lIdx == o.proberState.lLength) { + o.proberState.lIdx, o.proberState.lBatch = 0, o.left.source.Next() + o.proberState.lLength = o.proberState.lBatch.Length() + } + if o.proberState.rBatch == nil || (o.proberState.rLength != 0 && o.proberState.rIdx == o.proberState.rLength) { + o.proberState.rIdx, o.proberState.rBatch = 0, o.right.source.Next() + o.proberState.rLength = o.proberState.rBatch.Length() } - if o.sourceFinished() { o.state = mjSourceFinished break } - o.state = mjProbe + case mjSourceFinished: _SOURCE_FINISHED_SWITCH(_JOIN_TYPE) - o.state = mjBuild - case mjFinishBufferedGroup: - o.finishProbe() - o.setBuilderSourceToBufferedGroup() - o.state = mjBuild + if len(o.builderState.lGroups) == 0 && len(o.builderState.rGroups) == 0 { + o.state = mjDone + o.output.SetLength(o.builderState.outCount) + return o.output + } + o.state = mjBuildFromBatch + case mjProbe: o.probe() - o.setBuilderSourceToBatch() - o.state = mjBuild - case mjBuild: - o.build() - - if o.builderState.outFinished { + o.builderState.lGroups, o.builderState.rGroups = o.groups.getGroups() + if len(o.builderState.lGroups) > 0 || len(o.builderState.rGroups) > 0 { + o.state = mjBuildFromBatch + } else if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { o.state = mjEntry - o.builderState.outFinished = false } - if o.outputReady || o.builderState.outCount == o.output.Capacity() { - if o.builderState.outCount == 0 { - // We have already fully emitted the result of the join, so we - // transition to "finished" state. - o.state = mjDone - continue - } + case mjBuildFromBatch: + o.buildFromBatch() + if o.builderState.outCount == o.outputCapacity { o.output.SetLength(o.builderState.outCount) - // Reset builder out count. - o.builderState.outCount = 0 - o.outputReady = false return o.output } - case mjDone: - // Note that resetting of buffered group will close disk queues - // (if there are any). - if o.bufferedGroup.needToReset { + if o.bufferedGroup.helper.numRightTuples != 0 { + o.transitionIntoBuildingFromBufferedGroup() + } else { + o.state = mjEntry + } + + case mjBuildFromBufferedGroup: + bufferedGroupComplete := o.buildFromBufferedGroup() + if bufferedGroupComplete { o.bufferedGroup.helper.Reset(o.Ctx) - o.bufferedGroup.needToReset = false + o.state = mjEntry } + if o.builderState.outCount == o.outputCapacity { + o.output.SetLength(o.builderState.outCount) + return o.output + } + + case mjDone: return coldata.ZeroBatch + default: colexecerror.InternalError(errors.AssertionFailedf("unexpected merge joiner state in Next: %v", o.state)) } diff --git a/pkg/sql/colexec/crossjoiner_test.go b/pkg/sql/colexec/crossjoiner_test.go index 05eefdd2a6e4..b138175c9a0e 100644 --- a/pkg/sql/colexec/crossjoiner_test.go +++ b/pkg/sql/colexec/crossjoiner_test.go @@ -190,7 +190,7 @@ func getCJTestCases() []*joinTestCase { leftTypes: []*types.T{types.Int}, rightTypes: []*types.T{types.Int}, leftTuples: colexectestutils.Tuples{{0}, {1}, {2}}, - rightTuples: colexectestutils.Tuples{{3}}, + rightTuples: colexectestutils.Tuples{{3}, {4}}, leftOutCols: []uint32{0}, joinType: descpb.LeftSemiJoin, expected: colexectestutils.Tuples{{0}, {1}, {2}}, @@ -254,7 +254,7 @@ func getCJTestCases() []*joinTestCase { expected: colexectestutils.Tuples{{0}, {1}, {2}, {3}}, }, { - description: "intersect all join, right non-empty", + description: "intersect all join, right smaller", leftTypes: []*types.T{types.Int}, rightTypes: []*types.T{types.Int}, leftTuples: colexectestutils.Tuples{{0}, {1}, {2}, {3}, {4}}, @@ -263,6 +263,16 @@ func getCJTestCases() []*joinTestCase { joinType: descpb.IntersectAllJoin, expected: colexectestutils.Tuples{{0}, {1}, {2}}, }, + { + description: "intersect all join, right larger", + leftTypes: []*types.T{types.Int}, + rightTypes: []*types.T{types.Int}, + leftTuples: colexectestutils.Tuples{{0}, {1}, {2}}, + rightTuples: colexectestutils.Tuples{{3}, {nil}, {3}, {3}, {4}}, + leftOutCols: []uint32{0}, + joinType: descpb.IntersectAllJoin, + expected: colexectestutils.Tuples{{0}, {1}, {2}}, + }, { description: "intersect all join, left empty", leftTypes: []*types.T{types.Int}, @@ -288,14 +298,26 @@ func getCJTestCases() []*joinTestCase { expected: colexectestutils.Tuples{}, }, { - description: "except all join, right non-empty", + description: "except all join, right smaller", leftTypes: []*types.T{types.Int}, rightTypes: []*types.T{types.Int}, leftTuples: colexectestutils.Tuples{{0}, {1}, {2}, {3}, {4}}, rightTuples: colexectestutils.Tuples{{3}, {nil}, {3}}, leftOutCols: []uint32{0}, joinType: descpb.ExceptAllJoin, - expected: colexectestutils.Tuples{{0}, {1}}, + expected: colexectestutils.Tuples{{3}, {4}}, + }, + { + description: "except all join, right larger", + leftTypes: []*types.T{types.Int}, + rightTypes: []*types.T{types.Int}, + leftTuples: colexectestutils.Tuples{{0}, {1}, {2}}, + rightTuples: colexectestutils.Tuples{{3}, {nil}, {3}, {3}, {4}}, + leftOutCols: []uint32{0}, + joinType: descpb.ExceptAllJoin, + // Injecting nulls into the right input won't change the output. + skipAllNullsInjection: true, + expected: colexectestutils.Tuples{}, }, { description: "except all join, left empty", diff --git a/pkg/sql/colexec/mergejoiner_test.go b/pkg/sql/colexec/mergejoiner_test.go index 77006a79d576..81f7c1c3c940 100644 --- a/pkg/sql/colexec/mergejoiner_test.go +++ b/pkg/sql/colexec/mergejoiner_test.go @@ -1612,6 +1612,33 @@ func getMJTestCases() []*joinTestCase { rightEqColsAreKey: true, expected: colexectestutils.Tuples{{1}}, }, + { + description: "LEFT SEMI join with non-unique eq column", + joinType: descpb.LeftSemiJoin, + leftTypes: []*types.T{types.Int, types.Int}, + rightTypes: []*types.T{types.Int, types.Int}, + leftTuples: colexectestutils.Tuples{{nil, 4}, {nil, 2}, {0, nil}, {0, 3}, {0, nil}, {1, nil}, {3, 3}, {3, nil}, {3, 0}, {4, 0}}, + rightTuples: colexectestutils.Tuples{{1, nil}, {nil, nil}, {nil, 0}, {3, 1}, {3, 1}, {1, 1}, {nil, 2}, {2, 2}, {3, 3}, {2, 4}}, + leftEqCols: []uint32{0}, + rightEqCols: []uint32{1}, + leftOutCols: []uint32{0, 1}, + expected: colexectestutils.Tuples{{0, nil}, {0, 3}, {0, nil}, {1, nil}, {3, 3}, {3, nil}, {3, 0}, {4, 0}}, + }, + { + description: "FULL OUTER join with nulls and DESC", + joinType: descpb.FullOuterJoin, + leftTypes: []*types.T{types.Int, types.Int}, + rightTypes: []*types.T{types.Int, types.Int}, + leftTuples: colexectestutils.Tuples{{0, 4}, {nil, 0}, {3, 0}}, + rightTuples: colexectestutils.Tuples{{0, 1}, {nil, 0}, {4, nil}}, + leftOutCols: []uint32{0, 1}, + rightOutCols: []uint32{0, 1}, + leftEqCols: []uint32{1}, + rightEqCols: []uint32{1}, + leftDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC}, + rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC}, + expected: colexectestutils.Tuples{{0, 4, nil, nil}, {nil, nil, 0, 1}, {nil, 0, nil, 0}, {3, 0, nil, 0}, {nil, nil, 4, nil}}, + }, } return withMirrors(mjTestCases) } diff --git a/pkg/sql/logictest/testdata/logic_test/cross_join b/pkg/sql/logictest/testdata/logic_test/cross_join new file mode 100644 index 000000000000..919b74bcfc31 --- /dev/null +++ b/pkg/sql/logictest/testdata/logic_test/cross_join @@ -0,0 +1,84 @@ +# Check that the cross join is performed in a streaming fashion with regards to +# the left input (if it wasn't, the query below would hit the temporary disk +# storage limit in fakedist-disk config). + +statement ok +CREATE TABLE t ( + i2 INT2, + i4 INT4, + i8 INT8, + f4 FLOAT4, + f8 FLOAT8, + s STRING, + c CHAR, + b BYTES, + dc DECIMAL, + ival INTERVAL, + oid OID, + tstz TIMESTAMPTZ, + ts TIMESTAMP, + da DATE, + inet INET, + vb VARBIT +); +INSERT + INTO t +SELECT i::INT2, + i::INT4, + i::INT8, + i::FLOAT4, + i::FLOAT8, + i::STRING, + i::CHAR, + i::STRING::BYTES, + i::DECIMAL, + i::INTERVAL, + i::OID, + i::TIMESTAMPTZ, + i::TIMESTAMP, + i::DATE, + ('127.0.0.' || i::STRING)::INET, + i::VARBIT + FROM ( + SELECT i FROM generate_series(1, 2) as t(i) + UNION ALL SELECT NULL + ); +SELECT * + FROM ( + SELECT a.i2, + b.i4, + c.i8, + d.f4, + e.f8, + f.s, + g.c, + h.b, + i.dc, + j.ival, + k.oid, + l.tstz, + m.ts, + n.da, + o.inet, + p.vb, + random() AS r + FROM t AS a, + t AS b, + t AS c, + t AS d, + t AS e, + t AS f, + t AS g, + t AS h, + t AS i, + t AS j, + t AS k, + t AS l, + t AS m, + t AS n, + t AS o, + t AS p + ) + WHERE r < .01 + LIMIT 1 + diff --git a/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local b/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local index 0cc75705a171..aba3ce5f0de5 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local +++ b/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local @@ -447,3 +447,28 @@ EXPLAIN (VEC) DELETE FROM t70438 WHERE k=3 OR v=6 └ *colexec.SerialUnorderedSynchronizer ├ *colfetcher.ColBatchScan └ *colfetcher.ColBatchScan + +# Some tests for set-op cross joins. +statement ok +CREATE TABLE t (); +CREATE TABLE u (); +INSERT INTO t (rowid) VALUES (1), (2); +INSERT INTO u (rowid) VALUES (1); + +query T +EXPLAIN (VEC) SELECT * FROM t INTERSECT ALL SELECT * FROM u +---- +│ +└ Node 1 + └ *colexecjoin.crossJoiner + ├ *colfetcher.ColBatchScan + └ *colfetcher.ColBatchScan + +query T +EXPLAIN (VEC) SELECT * FROM t EXCEPT ALL SELECT * FROM u +---- +│ +└ Node 1 + └ *colexecjoin.crossJoiner + ├ *colfetcher.ColBatchScan + └ *colfetcher.ColBatchScan From 7b5a0a9a3f51fc3d87a1342240519ffd04b26d73 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Mon, 16 Aug 2021 20:40:42 -0700 Subject: [PATCH 102/205] colexecjoin: improve probing in the merge joiner with nulls For non set-operation joins whenever we have nulls in both columns we can advance both pointers since neither of the rows will have a match. This commit takes advantage of this observation as well as refactors (hopefully making it cleaner) the probing mechanism a bit. Release note: None --- .../colexecjoin/mergejoiner_exceptall.eg.go | 1368 +++++----- .../colexecjoin/mergejoiner_fullouter.eg.go | 2424 +++++++++-------- .../colexecjoin/mergejoiner_inner.eg.go | 1456 ++++++---- .../mergejoiner_intersectall.eg.go | 884 +++--- .../colexecjoin/mergejoiner_leftanti.eg.go | 1940 +++++++------ .../colexecjoin/mergejoiner_leftouter.eg.go | 1940 +++++++------ .../colexecjoin/mergejoiner_leftsemi.eg.go | 1456 ++++++---- .../colexecjoin/mergejoiner_rightanti.eg.go | 1940 +++++++------ .../colexecjoin/mergejoiner_rightouter.eg.go | 1940 +++++++------ .../colexecjoin/mergejoiner_rightsemi.eg.go | 1456 ++++++---- .../colexec/colexecjoin/mergejoiner_tmpl.go | 56 +- .../colexec/colexecjoin/mergejoiner_util.go | 6 + pkg/sql/colexec/mergejoiner_test.go | 13 + 13 files changed, 9726 insertions(+), 7153 deletions(-) diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go index 083dded076b4..86e91620a528 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_exceptall.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,19 +77,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -214,7 +213,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -225,7 +224,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -288,7 +287,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -298,10 +297,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -323,19 +327,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -437,7 +439,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -448,7 +450,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -503,7 +505,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -513,10 +515,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -538,19 +545,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -652,7 +657,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -663,7 +668,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -718,7 +723,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -728,10 +733,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -752,19 +762,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -899,7 +907,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -910,7 +918,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -976,7 +984,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -986,10 +994,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -1007,19 +1020,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1154,7 +1165,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1165,7 +1176,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -1231,7 +1242,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1241,10 +1252,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -1263,19 +1279,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1410,7 +1424,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1421,7 +1435,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -1487,7 +1501,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1497,10 +1511,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1522,19 +1541,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1693,7 +1710,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1704,7 +1721,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -1778,7 +1795,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1788,10 +1805,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1813,19 +1835,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1948,7 +1968,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1959,7 +1979,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -2021,7 +2041,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2031,10 +2051,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -2056,19 +2081,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -2170,7 +2193,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2181,7 +2204,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -2236,7 +2259,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2246,10 +2269,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -2271,19 +2299,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -2403,7 +2429,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2414,7 +2440,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -2475,7 +2501,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2485,10 +2511,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -2510,19 +2541,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -2630,7 +2659,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2641,7 +2670,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -2698,7 +2727,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2708,10 +2737,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2732,6 +2766,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -2754,19 +2789,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -2892,7 +2925,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2903,7 +2936,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -2966,7 +2999,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2976,10 +3009,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -3001,19 +3039,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3115,7 +3151,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3126,7 +3162,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -3181,7 +3217,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3191,10 +3227,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -3216,19 +3257,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3330,7 +3369,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3341,7 +3380,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -3396,7 +3435,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3406,10 +3445,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -3430,19 +3474,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3577,7 +3619,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3588,7 +3630,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -3654,7 +3696,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3664,10 +3706,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -3685,19 +3732,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3832,7 +3877,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3843,7 +3888,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -3909,7 +3954,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3919,10 +3964,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -3941,19 +3991,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -4088,7 +4136,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4099,7 +4147,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -4165,7 +4213,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4175,10 +4223,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -4200,19 +4253,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -4371,7 +4422,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4382,7 +4433,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -4456,7 +4507,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4466,10 +4517,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4491,19 +4547,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -4626,7 +4680,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4637,7 +4691,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -4699,7 +4753,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4709,10 +4763,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -4734,19 +4793,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -4848,7 +4905,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4859,7 +4916,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -4914,7 +4971,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4924,10 +4981,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -4949,19 +5011,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -5081,7 +5141,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5092,7 +5152,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -5153,7 +5213,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5163,10 +5223,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -5188,19 +5253,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -5308,7 +5371,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5319,7 +5382,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -5376,7 +5439,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5386,10 +5449,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -5410,6 +5478,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -5432,19 +5501,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5570,7 +5637,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5581,7 +5648,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -5644,7 +5711,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5654,10 +5721,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5679,19 +5751,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5793,7 +5863,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5804,7 +5874,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -5859,7 +5929,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5869,10 +5939,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5894,19 +5969,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -6008,7 +6081,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6019,7 +6092,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -6074,7 +6147,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6084,10 +6157,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -6108,19 +6186,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -6255,7 +6331,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6266,7 +6342,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -6332,7 +6408,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6342,10 +6418,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -6363,19 +6444,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -6510,7 +6589,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6521,7 +6600,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -6587,7 +6666,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6597,10 +6676,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -6619,19 +6703,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -6766,7 +6848,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6777,7 +6859,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -6843,7 +6925,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6853,10 +6935,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6878,19 +6965,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -7049,7 +7134,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7060,7 +7145,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -7134,7 +7219,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7144,10 +7229,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -7169,19 +7259,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -7304,7 +7392,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7315,7 +7403,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -7377,7 +7465,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7387,10 +7475,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -7412,19 +7505,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -7526,7 +7617,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7537,7 +7628,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -7592,7 +7683,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7602,10 +7693,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -7627,19 +7723,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -7759,7 +7853,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7770,7 +7864,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -7831,7 +7925,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7841,10 +7935,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7866,19 +7965,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -7986,7 +8083,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7997,7 +8094,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -8054,7 +8151,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8064,10 +8161,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -8088,6 +8190,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -8110,19 +8213,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -8248,7 +8349,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8259,7 +8360,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -8322,7 +8423,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8332,10 +8433,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -8357,19 +8463,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -8471,7 +8575,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8482,7 +8586,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -8537,7 +8641,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8547,10 +8651,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -8572,19 +8681,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -8686,7 +8793,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8697,7 +8804,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -8752,7 +8859,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8762,10 +8869,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -8786,19 +8898,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -8933,7 +9043,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8944,7 +9054,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -9010,7 +9120,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9020,10 +9130,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -9041,19 +9156,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -9188,7 +9301,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9199,7 +9312,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -9265,7 +9378,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9275,10 +9388,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -9297,19 +9415,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -9444,7 +9560,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9455,7 +9571,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -9521,7 +9637,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9531,10 +9647,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -9556,19 +9677,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -9727,7 +9846,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9738,7 +9857,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -9812,7 +9931,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9822,10 +9941,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -9847,19 +9971,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -9982,7 +10104,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9993,7 +10115,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -10055,7 +10177,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10065,10 +10187,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -10090,19 +10217,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -10204,7 +10329,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10215,7 +10340,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -10270,7 +10395,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10280,10 +10405,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -10305,19 +10435,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -10437,7 +10565,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10448,7 +10576,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -10509,7 +10637,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10519,10 +10647,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -10544,19 +10677,17 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -10664,7 +10795,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10675,7 +10806,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // For EXCEPT ALL join we add (lGroupLength - rGroupLength) number @@ -10732,7 +10863,7 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10742,10 +10873,15 @@ func (o *mergeJoinExceptAllOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go index 29a35664057a..0d8d1fde22d9 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_fullouter.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,43 +77,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -211,7 +212,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -222,7 +223,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -303,7 +304,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -313,7 +314,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -323,10 +324,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -348,43 +354,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -459,7 +465,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -470,7 +476,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -535,7 +541,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -545,7 +551,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -555,10 +561,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -580,43 +591,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -691,7 +702,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -702,7 +713,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -767,7 +778,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -777,7 +788,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -787,10 +798,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -811,43 +827,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -955,7 +971,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -966,7 +982,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1053,7 +1069,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1063,7 +1079,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1073,10 +1089,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -1094,43 +1115,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1238,7 +1259,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1249,7 +1270,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1336,7 +1357,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1346,7 +1367,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1356,10 +1377,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -1378,43 +1404,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1522,7 +1548,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1533,7 +1559,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1620,7 +1646,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1630,7 +1656,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1640,10 +1666,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1665,43 +1696,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1833,7 +1864,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1844,7 +1875,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1947,7 +1978,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1957,7 +1988,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1967,10 +1998,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1992,43 +2028,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2124,7 +2160,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2135,7 +2171,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2214,7 +2250,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2224,7 +2260,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2234,10 +2270,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -2259,43 +2300,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2370,7 +2411,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2381,7 +2422,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2446,7 +2487,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2456,7 +2497,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2466,10 +2507,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -2491,43 +2537,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2620,7 +2666,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2631,7 +2677,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2708,7 +2754,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2718,7 +2764,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2728,10 +2774,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -2753,43 +2804,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2870,7 +2921,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2881,7 +2932,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2950,7 +3001,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2960,7 +3011,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2970,10 +3021,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2994,6 +3050,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -3016,43 +3073,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3151,7 +3208,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3162,7 +3219,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3243,7 +3300,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3253,7 +3310,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3263,10 +3320,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -3288,43 +3350,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3399,7 +3461,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3410,7 +3472,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3475,7 +3537,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3485,7 +3547,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3495,10 +3557,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -3520,43 +3587,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3631,7 +3698,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3642,7 +3709,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3707,7 +3774,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3717,7 +3784,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3727,10 +3794,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -3751,43 +3823,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3895,7 +3967,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3906,7 +3978,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3993,7 +4065,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4003,7 +4075,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4013,10 +4085,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -4034,43 +4111,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4178,7 +4255,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4189,7 +4266,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4276,7 +4353,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4286,7 +4363,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4296,10 +4373,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -4318,43 +4400,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4462,7 +4544,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4473,7 +4555,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4560,7 +4642,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4570,7 +4652,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4580,10 +4662,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -4605,43 +4692,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4773,7 +4860,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4784,7 +4871,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4887,7 +4974,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4897,7 +4984,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4907,10 +4994,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4932,43 +5024,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5064,7 +5156,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5075,7 +5167,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5154,7 +5246,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5164,7 +5256,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5174,10 +5266,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -5199,43 +5296,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5310,7 +5407,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5321,7 +5418,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5386,7 +5483,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5396,7 +5493,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5406,10 +5503,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -5431,43 +5533,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5560,7 +5662,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5571,7 +5673,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5648,7 +5750,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5658,7 +5760,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5668,10 +5770,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -5693,43 +5800,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5810,7 +5917,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5821,7 +5928,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5890,7 +5997,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5900,7 +6007,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5910,10 +6017,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -5934,6 +6046,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -5956,43 +6069,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6091,7 +6204,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6102,7 +6215,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6183,7 +6296,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6193,7 +6306,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6203,10 +6316,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -6228,43 +6346,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6339,7 +6457,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6350,7 +6468,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6415,7 +6533,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6425,7 +6543,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6435,10 +6553,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -6460,43 +6583,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6571,7 +6694,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6582,7 +6705,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6647,7 +6770,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6657,7 +6780,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6667,10 +6790,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -6691,43 +6819,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6835,7 +6963,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6846,7 +6974,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6933,7 +7061,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6943,7 +7071,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6953,10 +7081,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -6974,43 +7107,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7118,7 +7251,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7129,7 +7262,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7216,7 +7349,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7226,7 +7359,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7236,10 +7369,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -7258,43 +7396,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7402,7 +7540,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7413,7 +7551,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7500,7 +7638,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7510,7 +7648,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7520,10 +7658,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -7545,43 +7688,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7713,7 +7856,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7724,7 +7867,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7827,7 +7970,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7837,7 +7980,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7847,10 +7990,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -7872,43 +8020,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8004,7 +8152,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8015,7 +8163,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8094,7 +8242,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8104,7 +8252,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8114,10 +8262,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -8139,43 +8292,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8250,7 +8403,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8261,7 +8414,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8326,7 +8479,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8336,7 +8489,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8346,10 +8499,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -8371,43 +8529,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8500,7 +8658,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8511,7 +8669,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8588,7 +8746,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8598,7 +8756,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8608,10 +8766,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -8633,43 +8796,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8750,7 +8913,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8761,7 +8924,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8830,7 +8993,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8840,7 +9003,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8850,10 +9013,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -8874,6 +9042,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -8896,43 +9065,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9031,7 +9200,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9042,7 +9211,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9123,7 +9292,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9133,7 +9302,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9143,10 +9312,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -9168,43 +9342,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9279,7 +9453,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9290,7 +9464,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9355,7 +9529,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9365,7 +9539,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9375,10 +9549,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -9400,43 +9579,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9511,7 +9690,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9522,7 +9701,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9587,7 +9766,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9597,7 +9776,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9607,10 +9786,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -9631,43 +9815,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9775,7 +9959,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9786,7 +9970,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9873,7 +10057,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9883,7 +10067,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9893,10 +10077,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -9914,43 +10103,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -10058,7 +10247,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10069,7 +10258,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -10156,7 +10345,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10166,7 +10355,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -10176,10 +10365,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -10198,43 +10392,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -10342,7 +10536,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10353,7 +10547,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -10440,7 +10634,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10450,7 +10644,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -10460,10 +10654,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -10485,43 +10684,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -10653,7 +10852,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10664,7 +10863,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -10767,7 +10966,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -10777,7 +10976,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -10787,10 +10986,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -10812,43 +11016,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -10944,7 +11148,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -10955,7 +11159,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -11034,7 +11238,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -11044,7 +11248,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -11054,10 +11258,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -11079,43 +11288,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -11190,7 +11399,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -11201,7 +11410,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -11266,7 +11475,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -11276,7 +11485,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -11286,10 +11495,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -11311,43 +11525,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -11440,7 +11654,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -11451,7 +11665,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -11528,7 +11742,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -11538,7 +11752,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -11548,10 +11762,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -11573,43 +11792,43 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -11690,7 +11909,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -11701,7 +11920,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -11770,7 +11989,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -11780,7 +11999,7 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -11790,10 +12009,15 @@ func (o *mergeJoinFullOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go index 0ff915b3120c..fdd9d665d452 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_inner.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,21 +77,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -189,7 +192,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -200,7 +203,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -220,10 +223,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -245,21 +253,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -334,7 +344,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -345,7 +355,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -365,10 +375,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -390,21 +405,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -479,7 +496,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -490,7 +507,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -510,10 +527,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -534,21 +556,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -656,7 +680,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -667,7 +691,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -687,10 +711,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -708,21 +737,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -830,7 +861,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -841,7 +872,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -861,10 +892,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -883,21 +919,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1005,7 +1043,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1016,7 +1054,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1036,10 +1074,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1061,21 +1104,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1207,7 +1252,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1218,7 +1263,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1238,10 +1283,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1263,21 +1313,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1373,7 +1425,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1384,7 +1436,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1404,10 +1456,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1429,21 +1486,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1518,7 +1577,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1529,7 +1588,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1549,10 +1608,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -1574,21 +1638,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1681,7 +1747,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1692,7 +1758,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1712,10 +1778,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -1737,21 +1808,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1832,7 +1905,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1843,7 +1916,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1863,10 +1936,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -1887,6 +1965,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -1909,21 +1988,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2022,7 +2103,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2033,7 +2114,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2053,10 +2134,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2078,21 +2164,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2167,7 +2255,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2178,7 +2266,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2198,10 +2286,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2223,21 +2316,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2312,7 +2407,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2323,7 +2418,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2343,10 +2438,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -2367,21 +2467,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2489,7 +2591,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2500,7 +2602,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2520,10 +2622,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -2541,21 +2648,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2663,7 +2772,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2674,7 +2783,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2694,10 +2803,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -2716,21 +2830,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2838,7 +2954,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2849,7 +2965,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2869,10 +2985,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -2894,21 +3015,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3040,7 +3163,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3051,7 +3174,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3071,10 +3194,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -3096,21 +3224,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3206,7 +3336,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3217,7 +3347,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3237,10 +3367,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -3262,21 +3397,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3351,7 +3488,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3362,7 +3499,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3382,10 +3519,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -3407,21 +3549,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3514,7 +3658,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3525,7 +3669,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3545,10 +3689,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -3570,21 +3719,23 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3665,7 +3816,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3676,7 +3827,7 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3696,10 +3847,15 @@ func (o *mergeJoinInnerOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -3720,6 +3876,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -3742,21 +3899,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3855,7 +4014,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3866,7 +4025,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3886,10 +4045,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -3911,21 +4075,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4000,7 +4166,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4011,7 +4177,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4031,10 +4197,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -4056,21 +4227,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4145,7 +4318,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4156,7 +4329,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4176,10 +4349,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -4200,21 +4378,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4322,7 +4502,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4333,7 +4513,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4353,10 +4533,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -4374,21 +4559,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4496,7 +4683,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4507,7 +4694,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4527,10 +4714,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -4549,21 +4741,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4671,7 +4865,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4682,7 +4876,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4702,10 +4896,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -4727,21 +4926,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4873,7 +5074,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4884,7 +5085,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4904,10 +5105,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4929,21 +5135,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5039,7 +5247,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5050,7 +5258,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5070,10 +5278,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -5095,21 +5308,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5184,7 +5399,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5195,7 +5410,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5215,10 +5430,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -5240,21 +5460,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5347,7 +5569,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5358,7 +5580,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5378,10 +5600,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -5403,21 +5630,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5498,7 +5727,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5509,7 +5738,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5529,10 +5758,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -5553,6 +5787,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -5575,21 +5810,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5688,7 +5925,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5699,7 +5936,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5719,10 +5956,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5744,21 +5986,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5833,7 +6077,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5844,7 +6088,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5864,10 +6108,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5889,21 +6138,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5978,7 +6229,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5989,7 +6240,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6009,10 +6260,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -6033,21 +6289,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6155,7 +6413,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6166,7 +6424,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6186,10 +6444,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -6207,21 +6470,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6329,7 +6594,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6340,7 +6605,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6360,10 +6625,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -6382,21 +6652,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6504,7 +6776,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6515,7 +6787,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6535,10 +6807,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6560,21 +6837,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6706,7 +6985,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6717,7 +6996,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6737,10 +7016,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6762,21 +7046,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6872,7 +7158,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6883,7 +7169,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6903,10 +7189,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6928,21 +7219,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7017,7 +7310,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7028,7 +7321,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7048,10 +7341,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -7073,21 +7371,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7180,7 +7480,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7191,7 +7491,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7211,10 +7511,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7236,21 +7541,23 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7331,7 +7638,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7342,7 +7649,7 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7362,10 +7669,15 @@ func (o *mergeJoinInnerOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go index e785de22a4c0..55c13e7a4139 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_intersectall.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,9 +77,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -204,7 +204,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -215,7 +215,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -239,10 +239,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -264,9 +269,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -368,7 +372,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -379,7 +383,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -403,10 +407,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -428,9 +437,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -532,7 +540,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -543,7 +551,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -567,10 +575,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -591,9 +604,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -728,7 +740,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -739,7 +751,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -763,10 +775,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -784,9 +801,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -921,7 +937,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -932,7 +948,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -956,10 +972,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -978,9 +999,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1115,7 +1135,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1126,7 +1146,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1150,10 +1170,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1175,9 +1200,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1336,7 +1360,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1347,7 +1371,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1371,10 +1395,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1396,9 +1425,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1521,7 +1549,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1532,7 +1560,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1556,10 +1584,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1581,9 +1614,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1685,7 +1717,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1696,7 +1728,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1720,10 +1752,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -1745,9 +1782,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -1867,7 +1903,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1878,7 +1914,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1902,10 +1938,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -1927,9 +1968,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -2037,7 +2077,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2048,7 +2088,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2072,10 +2112,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2096,6 +2141,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -2118,9 +2164,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -2246,7 +2291,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2257,7 +2302,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2281,10 +2326,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2306,9 +2356,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -2410,7 +2459,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2421,7 +2470,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2445,10 +2494,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2470,9 +2524,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -2574,7 +2627,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2585,7 +2638,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2609,10 +2662,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -2633,9 +2691,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -2770,7 +2827,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2781,7 +2838,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2805,10 +2862,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -2826,9 +2888,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -2963,7 +3024,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2974,7 +3035,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2998,10 +3059,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -3020,9 +3086,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3157,7 +3222,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3168,7 +3233,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3192,10 +3257,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -3217,9 +3287,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3378,7 +3447,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3389,7 +3458,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3413,10 +3482,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -3438,9 +3512,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3563,7 +3636,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3574,7 +3647,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3598,10 +3671,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -3623,9 +3701,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3727,7 +3804,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3738,7 +3815,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3762,10 +3839,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -3787,9 +3869,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -3909,7 +3990,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3920,7 +4001,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3944,10 +4025,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -3969,9 +4055,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) @@ -4079,7 +4164,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4090,7 +4175,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4114,10 +4199,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -4138,6 +4228,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -4160,9 +4251,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -4288,7 +4378,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4299,7 +4389,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4323,10 +4413,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -4348,9 +4443,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -4452,7 +4546,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4463,7 +4557,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4487,10 +4581,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -4512,9 +4611,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -4616,7 +4714,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4627,7 +4725,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4651,10 +4749,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -4675,9 +4778,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -4812,7 +4914,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4823,7 +4925,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4847,10 +4949,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -4868,9 +4975,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5005,7 +5111,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5016,7 +5122,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5040,10 +5146,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -5062,9 +5173,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5199,7 +5309,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5210,7 +5320,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5234,10 +5344,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -5259,9 +5374,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5420,7 +5534,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5431,7 +5545,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5455,10 +5569,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -5480,9 +5599,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5605,7 +5723,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5616,7 +5734,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5640,10 +5758,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -5665,9 +5788,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5769,7 +5891,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5780,7 +5902,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5804,10 +5926,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -5829,9 +5956,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -5951,7 +6077,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5962,7 +6088,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5986,10 +6112,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -6011,9 +6142,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) @@ -6121,7 +6251,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6132,7 +6262,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6156,10 +6286,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -6180,6 +6315,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -6202,9 +6338,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -6330,7 +6465,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6341,7 +6476,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6365,10 +6500,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -6390,9 +6530,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -6494,7 +6633,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6505,7 +6644,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6529,10 +6668,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -6554,9 +6698,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -6658,7 +6801,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6669,7 +6812,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6693,10 +6836,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -6717,9 +6865,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -6854,7 +7001,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6865,7 +7012,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6889,10 +7036,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -6910,9 +7062,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -7047,7 +7198,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7058,7 +7209,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7082,10 +7233,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -7104,9 +7260,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -7241,7 +7396,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7252,7 +7407,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7276,10 +7431,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -7301,9 +7461,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -7462,7 +7621,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7473,7 +7632,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7497,10 +7656,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -7522,9 +7686,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -7647,7 +7810,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7658,7 +7821,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7682,10 +7845,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -7707,9 +7875,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -7811,7 +7978,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7822,7 +7989,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7846,10 +8013,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -7871,9 +8043,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -7993,7 +8164,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8004,7 +8175,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -8028,10 +8199,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -8053,9 +8229,8 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) @@ -8163,7 +8338,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8174,7 +8349,7 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -8198,10 +8373,15 @@ func (o *mergeJoinIntersectAllOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go index ddec5991258b..dbc70f4fa95c 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_leftanti.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,32 +77,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -200,7 +202,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -211,7 +213,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -261,7 +263,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -271,10 +273,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -296,32 +303,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -396,7 +404,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -407,7 +415,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -449,7 +457,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -459,10 +467,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -484,32 +497,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -584,7 +598,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -595,7 +609,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -637,7 +651,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -647,10 +661,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -671,32 +690,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -804,7 +824,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -815,7 +835,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -868,7 +888,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -878,10 +898,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -899,32 +924,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1032,7 +1058,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1043,7 +1069,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -1096,7 +1122,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1106,10 +1132,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -1128,32 +1159,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1261,7 +1293,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1272,7 +1304,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -1325,7 +1357,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1335,10 +1367,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1360,32 +1397,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1517,7 +1555,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1528,7 +1566,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -1589,7 +1627,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1599,10 +1637,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1624,32 +1667,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1745,7 +1789,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1756,7 +1800,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -1805,7 +1849,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1815,10 +1859,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1840,32 +1889,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1940,7 +1990,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1951,7 +2001,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -1993,7 +2043,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2003,10 +2053,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -2028,32 +2083,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2146,7 +2202,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2157,7 +2213,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -2205,7 +2261,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2215,10 +2271,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -2240,32 +2301,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2346,7 +2408,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2357,7 +2419,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -2401,7 +2463,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2411,10 +2473,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2435,6 +2502,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -2457,32 +2525,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2581,7 +2650,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2592,7 +2661,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -2642,7 +2711,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2652,10 +2721,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2677,32 +2751,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2777,7 +2852,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2788,7 +2863,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -2830,7 +2905,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2840,10 +2915,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2865,32 +2945,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2965,7 +3046,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2976,7 +3057,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -3018,7 +3099,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3028,10 +3109,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -3052,32 +3138,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3185,7 +3272,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3196,7 +3283,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -3249,7 +3336,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3259,10 +3346,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -3280,32 +3372,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3413,7 +3506,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3424,7 +3517,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -3477,7 +3570,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3487,10 +3580,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -3509,32 +3607,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3642,7 +3741,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3653,7 +3752,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -3706,7 +3805,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3716,10 +3815,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -3741,32 +3845,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3898,7 +4003,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3909,7 +4014,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -3970,7 +4075,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3980,10 +4085,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4005,32 +4115,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4126,7 +4237,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4137,7 +4248,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -4186,7 +4297,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4196,10 +4307,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -4221,32 +4337,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4321,7 +4438,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4332,7 +4449,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -4374,7 +4491,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4384,10 +4501,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -4409,32 +4531,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4527,7 +4650,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4538,7 +4661,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -4586,7 +4709,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4596,10 +4719,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -4621,32 +4749,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4727,7 +4856,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4738,7 +4867,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -4782,7 +4911,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4792,10 +4921,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -4816,6 +4950,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -4838,32 +4973,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4962,7 +5098,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4973,7 +5109,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -5023,7 +5159,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5033,10 +5169,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5058,32 +5199,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5158,7 +5300,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5169,7 +5311,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -5211,7 +5353,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5221,10 +5363,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5246,32 +5393,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5346,7 +5494,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5357,7 +5505,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -5399,7 +5547,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5409,10 +5557,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -5433,32 +5586,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5566,7 +5720,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5577,7 +5731,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -5630,7 +5784,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5640,10 +5794,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -5661,32 +5820,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5794,7 +5954,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5805,7 +5965,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -5858,7 +6018,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5868,10 +6028,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -5890,32 +6055,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6023,7 +6189,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6034,7 +6200,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -6087,7 +6253,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6097,10 +6263,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6122,32 +6293,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6279,7 +6451,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6290,7 +6462,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -6351,7 +6523,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6361,10 +6533,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6386,32 +6563,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6507,7 +6685,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6518,7 +6696,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -6567,7 +6745,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6577,10 +6755,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6602,32 +6785,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6702,7 +6886,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6713,7 +6897,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -6755,7 +6939,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6765,10 +6949,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -6790,32 +6979,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6908,7 +7098,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6919,7 +7109,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -6967,7 +7157,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6977,10 +7167,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7002,32 +7197,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7108,7 +7304,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7119,7 +7315,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -7163,7 +7359,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7173,10 +7369,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -7197,6 +7398,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -7219,32 +7421,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7343,7 +7546,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7354,7 +7557,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -7404,7 +7607,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7414,10 +7617,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -7439,32 +7647,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7539,7 +7748,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7550,7 +7759,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -7592,7 +7801,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7602,10 +7811,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -7627,32 +7841,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7727,7 +7942,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7738,7 +7953,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -7780,7 +7995,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7790,10 +8005,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -7814,32 +8034,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7947,7 +8168,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7958,7 +8179,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -8011,7 +8232,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8021,10 +8242,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -8042,32 +8268,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8175,7 +8402,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8186,7 +8413,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -8239,7 +8466,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8249,10 +8476,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -8271,32 +8503,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8404,7 +8637,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8415,7 +8648,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -8468,7 +8701,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8478,10 +8711,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -8503,32 +8741,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8660,7 +8899,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8671,7 +8910,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -8732,7 +8971,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8742,10 +8981,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -8767,32 +9011,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8888,7 +9133,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8899,7 +9144,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -8948,7 +9193,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8958,10 +9203,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -8983,32 +9233,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9083,7 +9334,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9094,7 +9345,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -9136,7 +9387,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9146,10 +9397,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -9171,32 +9427,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9289,7 +9546,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9300,7 +9557,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -9348,7 +9605,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9358,10 +9615,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -9383,32 +9645,33 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9489,7 +9752,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9500,7 +9763,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With LEFT ANTI join, we are only interested in unmatched tuples @@ -9544,7 +9807,7 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9554,10 +9817,15 @@ func (o *mergeJoinLeftAntiOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go index 823ca30d0842..f8d6f065d6bd 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_leftouter.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,32 +77,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -200,7 +202,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -211,7 +213,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -262,7 +264,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -272,10 +274,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -297,32 +304,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -397,7 +405,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -408,7 +416,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -451,7 +459,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -461,10 +469,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -486,32 +499,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -586,7 +600,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -597,7 +611,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -640,7 +654,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -650,10 +664,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -674,32 +693,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -807,7 +827,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -818,7 +838,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -872,7 +892,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -882,10 +902,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -903,32 +928,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1036,7 +1062,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1047,7 +1073,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1101,7 +1127,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1111,10 +1137,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -1133,32 +1164,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1266,7 +1298,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1277,7 +1309,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1331,7 +1363,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1341,10 +1373,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1366,32 +1403,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1523,7 +1561,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1534,7 +1572,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1596,7 +1634,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1606,10 +1644,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1631,32 +1674,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1752,7 +1796,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1763,7 +1807,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1813,7 +1857,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -1823,10 +1867,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1848,32 +1897,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1948,7 +1998,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1959,7 +2009,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2002,7 +2052,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2012,10 +2062,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -2037,32 +2092,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2155,7 +2211,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2166,7 +2222,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2215,7 +2271,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2225,10 +2281,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -2250,32 +2311,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2356,7 +2418,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2367,7 +2429,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2412,7 +2474,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2422,10 +2484,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2446,6 +2513,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -2468,32 +2536,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2592,7 +2661,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2603,7 +2672,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2654,7 +2723,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2664,10 +2733,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2689,32 +2763,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2789,7 +2864,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2800,7 +2875,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2843,7 +2918,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -2853,10 +2928,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2878,32 +2958,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2978,7 +3059,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2989,7 +3070,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3032,7 +3113,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3042,10 +3123,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -3066,32 +3152,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3199,7 +3286,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3210,7 +3297,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3264,7 +3351,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3274,10 +3361,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -3295,32 +3387,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3428,7 +3521,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3439,7 +3532,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3493,7 +3586,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3503,10 +3596,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -3525,32 +3623,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3658,7 +3757,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3669,7 +3768,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3723,7 +3822,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3733,10 +3832,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -3758,32 +3862,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3915,7 +4020,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3926,7 +4031,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3988,7 +4093,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -3998,10 +4103,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4023,32 +4133,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4144,7 +4255,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4155,7 +4266,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4205,7 +4316,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4215,10 +4326,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -4240,32 +4356,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4340,7 +4457,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4351,7 +4468,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4394,7 +4511,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4404,10 +4521,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -4429,32 +4551,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4547,7 +4670,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4558,7 +4681,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4607,7 +4730,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4617,10 +4740,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -4642,32 +4770,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4748,7 +4877,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4759,7 +4888,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4804,7 +4933,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -4814,10 +4943,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSeltrueRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -4838,6 +4972,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -4860,32 +4995,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4984,7 +5120,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4995,7 +5131,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5046,7 +5182,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5056,10 +5192,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5081,32 +5222,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5181,7 +5323,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5192,7 +5334,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5235,7 +5377,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5245,10 +5387,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5270,32 +5417,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5370,7 +5518,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5381,7 +5529,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5424,7 +5572,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5434,10 +5582,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -5458,32 +5611,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5591,7 +5745,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5602,7 +5756,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5656,7 +5810,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5666,10 +5820,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -5687,32 +5846,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5820,7 +5980,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5831,7 +5991,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5885,7 +6045,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -5895,10 +6055,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -5917,32 +6082,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6050,7 +6216,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6061,7 +6227,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6115,7 +6281,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6125,10 +6291,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6150,32 +6321,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6307,7 +6479,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6318,7 +6490,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6380,7 +6552,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6390,10 +6562,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6415,32 +6592,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6536,7 +6714,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6547,7 +6725,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6597,7 +6775,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6607,10 +6785,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6632,32 +6815,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6732,7 +6916,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6743,7 +6927,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6786,7 +6970,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -6796,10 +6980,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -6821,32 +7010,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6939,7 +7129,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6950,7 +7140,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6999,7 +7189,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7009,10 +7199,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7034,32 +7229,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7140,7 +7336,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7151,7 +7347,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7196,7 +7392,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7206,10 +7402,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSeltrue() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -7230,6 +7431,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -7252,32 +7454,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7376,7 +7579,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7387,7 +7590,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7438,7 +7641,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7448,10 +7651,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -7473,32 +7681,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7573,7 +7782,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7584,7 +7793,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7627,7 +7836,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7637,10 +7846,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -7662,32 +7876,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7762,7 +7977,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7773,7 +7988,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7816,7 +8031,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -7826,10 +8041,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -7850,32 +8070,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7983,7 +8204,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7994,7 +8215,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8048,7 +8269,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8058,10 +8279,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -8079,32 +8305,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8212,7 +8439,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8223,7 +8450,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8277,7 +8504,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8287,10 +8514,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -8309,32 +8541,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8442,7 +8675,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8453,7 +8686,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8507,7 +8740,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8517,10 +8750,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -8542,32 +8780,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8699,7 +8938,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8710,7 +8949,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8772,7 +9011,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8782,10 +9021,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -8807,32 +9051,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8928,7 +9173,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8939,7 +9184,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8989,7 +9234,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -8999,10 +9244,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -9024,32 +9274,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9124,7 +9375,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9135,7 +9386,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9178,7 +9429,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9188,10 +9439,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -9213,32 +9469,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9331,7 +9588,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9342,7 +9599,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9391,7 +9648,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9401,10 +9658,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -9426,32 +9688,33 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9532,7 +9795,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9543,7 +9806,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9588,7 +9851,7 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -9598,10 +9861,15 @@ func (o *mergeJoinLeftOuterOp) probeBodyLSelfalseRSelfalse() { curLIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go index 7a1f6af3fa0e..ddab25e50f6c 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_leftsemi.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,21 +77,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -189,7 +192,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -200,7 +203,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -219,10 +222,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -244,21 +252,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -333,7 +343,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -344,7 +354,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -363,10 +373,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -388,21 +403,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -477,7 +494,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -488,7 +505,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -507,10 +524,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -531,21 +553,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -653,7 +677,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -664,7 +688,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -683,10 +707,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -704,21 +733,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -826,7 +857,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -837,7 +868,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -856,10 +887,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -878,21 +914,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1000,7 +1038,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1011,7 +1049,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1030,10 +1068,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1055,21 +1098,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1201,7 +1246,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1212,7 +1257,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1231,10 +1276,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1256,21 +1306,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1366,7 +1418,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1377,7 +1429,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1396,10 +1448,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1421,21 +1478,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1510,7 +1569,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1521,7 +1580,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1540,10 +1599,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -1565,21 +1629,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1672,7 +1738,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1683,7 +1749,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1702,10 +1768,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -1727,21 +1798,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1822,7 +1895,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1833,7 +1906,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -1852,10 +1925,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -1876,6 +1954,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -1898,21 +1977,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2011,7 +2092,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2022,7 +2103,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2041,10 +2122,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2066,21 +2152,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2155,7 +2243,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2166,7 +2254,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2185,10 +2273,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2210,21 +2303,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2299,7 +2394,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2310,7 +2405,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2329,10 +2424,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -2353,21 +2453,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2475,7 +2577,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2486,7 +2588,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2505,10 +2607,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -2526,21 +2633,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2648,7 +2757,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2659,7 +2768,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2678,10 +2787,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -2700,21 +2814,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2822,7 +2938,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2833,7 +2949,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -2852,10 +2968,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -2877,21 +2998,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3023,7 +3146,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3034,7 +3157,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3053,10 +3176,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -3078,21 +3206,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3188,7 +3318,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3199,7 +3329,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3218,10 +3348,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -3243,21 +3378,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3332,7 +3469,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3343,7 +3480,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3362,10 +3499,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -3387,21 +3529,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3494,7 +3638,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3505,7 +3649,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3524,10 +3668,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -3549,21 +3698,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3644,7 +3795,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3655,7 +3806,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3674,10 +3825,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -3698,6 +3854,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -3720,21 +3877,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3833,7 +3992,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3844,7 +4003,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -3863,10 +4022,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -3888,21 +4052,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3977,7 +4143,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3988,7 +4154,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4007,10 +4173,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -4032,21 +4203,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4121,7 +4294,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4132,7 +4305,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4151,10 +4324,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -4175,21 +4353,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4297,7 +4477,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4308,7 +4488,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4327,10 +4507,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -4348,21 +4533,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4470,7 +4657,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4481,7 +4668,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4500,10 +4687,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -4522,21 +4714,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4644,7 +4838,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4655,7 +4849,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4674,10 +4868,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -4699,21 +4898,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4845,7 +5046,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4856,7 +5057,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -4875,10 +5076,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4900,21 +5106,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5010,7 +5218,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5021,7 +5229,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5040,10 +5248,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -5065,21 +5278,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5154,7 +5369,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5165,7 +5380,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5184,10 +5399,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -5209,21 +5429,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5316,7 +5538,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5327,7 +5549,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5346,10 +5568,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -5371,21 +5598,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5466,7 +5695,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5477,7 +5706,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5496,10 +5725,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -5520,6 +5754,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -5542,21 +5777,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5655,7 +5892,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5666,7 +5903,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5685,10 +5922,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5710,21 +5952,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5799,7 +6043,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5810,7 +6054,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5829,10 +6073,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5854,21 +6103,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5943,7 +6194,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5954,7 +6205,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -5973,10 +6224,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -5997,21 +6253,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6119,7 +6377,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6130,7 +6388,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6149,10 +6407,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -6170,21 +6433,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6292,7 +6557,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6303,7 +6568,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6322,10 +6587,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -6344,21 +6614,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6466,7 +6738,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6477,7 +6749,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6496,10 +6768,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6521,21 +6798,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6667,7 +6946,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6678,7 +6957,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6697,10 +6976,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6722,21 +7006,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6832,7 +7118,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6843,7 +7129,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -6862,10 +7148,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6887,21 +7178,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6976,7 +7269,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6987,7 +7280,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7006,10 +7299,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -7031,21 +7329,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7138,7 +7438,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7149,7 +7449,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7168,10 +7468,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7193,21 +7498,23 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7288,7 +7595,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7299,7 +7606,7 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { leftSemiGroupLength := lGroupLength @@ -7318,10 +7625,15 @@ func (o *mergeJoinLeftSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go index 224822d898b9..d782f4429bf2 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_rightanti.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,32 +77,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -200,7 +202,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -211,7 +213,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -260,7 +262,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -270,10 +272,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -295,32 +302,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -395,7 +403,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -406,7 +414,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -447,7 +455,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -457,10 +465,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -482,32 +495,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -582,7 +596,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -593,7 +607,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -634,7 +648,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -644,10 +658,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -668,32 +687,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -801,7 +821,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -812,7 +832,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -864,7 +884,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -874,10 +894,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -895,32 +920,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1028,7 +1054,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1039,7 +1065,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -1091,7 +1117,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1101,10 +1127,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -1123,32 +1154,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1256,7 +1288,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1267,7 +1299,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -1319,7 +1351,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1329,10 +1361,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1354,32 +1391,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1511,7 +1549,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1522,7 +1560,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -1582,7 +1620,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1592,10 +1630,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1617,32 +1660,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1738,7 +1782,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1749,7 +1793,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -1797,7 +1841,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1807,10 +1851,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1832,32 +1881,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1932,7 +1982,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1943,7 +1993,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -1984,7 +2034,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1994,10 +2044,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -2019,32 +2074,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2137,7 +2193,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2148,7 +2204,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -2195,7 +2251,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2205,10 +2261,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -2230,32 +2291,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2336,7 +2398,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2347,7 +2409,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -2390,7 +2452,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2400,10 +2462,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2424,6 +2491,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -2446,32 +2514,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2570,7 +2639,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2581,7 +2650,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -2630,7 +2699,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2640,10 +2709,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2665,32 +2739,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2765,7 +2840,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2776,7 +2851,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -2817,7 +2892,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2827,10 +2902,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2852,32 +2932,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2952,7 +3033,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2963,7 +3044,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -3004,7 +3085,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3014,10 +3095,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -3038,32 +3124,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3171,7 +3258,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3182,7 +3269,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -3234,7 +3321,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3244,10 +3331,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -3265,32 +3357,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3398,7 +3491,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3409,7 +3502,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -3461,7 +3554,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3471,10 +3564,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -3493,32 +3591,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3626,7 +3725,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3637,7 +3736,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -3689,7 +3788,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3699,10 +3798,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -3724,32 +3828,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3881,7 +3986,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3892,7 +3997,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -3952,7 +4057,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3962,10 +4067,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -3987,32 +4097,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4108,7 +4219,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4119,7 +4230,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -4167,7 +4278,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4177,10 +4288,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -4202,32 +4318,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4302,7 +4419,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4313,7 +4430,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -4354,7 +4471,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4364,10 +4481,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -4389,32 +4511,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4507,7 +4630,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4518,7 +4641,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -4565,7 +4688,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4575,10 +4698,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -4600,32 +4728,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4706,7 +4835,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4717,7 +4846,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -4760,7 +4889,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4770,10 +4899,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -4794,6 +4928,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -4816,32 +4951,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4940,7 +5076,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4951,7 +5087,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -5000,7 +5136,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5010,10 +5146,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5035,32 +5176,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5135,7 +5277,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5146,7 +5288,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -5187,7 +5329,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5197,10 +5339,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5222,32 +5369,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5322,7 +5470,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5333,7 +5481,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -5374,7 +5522,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5384,10 +5532,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -5408,32 +5561,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5541,7 +5695,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5552,7 +5706,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -5604,7 +5758,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5614,10 +5768,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -5635,32 +5794,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5768,7 +5928,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5779,7 +5939,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -5831,7 +5991,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5841,10 +6001,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -5863,32 +6028,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5996,7 +6162,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6007,7 +6173,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -6059,7 +6225,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6069,10 +6235,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6094,32 +6265,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6251,7 +6423,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6262,7 +6434,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -6322,7 +6494,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6332,10 +6504,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6357,32 +6534,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6478,7 +6656,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6489,7 +6667,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -6537,7 +6715,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6547,10 +6725,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6572,32 +6755,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6672,7 +6856,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6683,7 +6867,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -6724,7 +6908,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6734,10 +6918,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -6759,32 +6948,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6877,7 +7067,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6888,7 +7078,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -6935,7 +7125,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6945,10 +7135,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -6970,32 +7165,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7076,7 +7272,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7087,7 +7283,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -7130,7 +7326,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7140,10 +7336,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -7164,6 +7365,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -7186,32 +7388,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7310,7 +7513,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7321,7 +7524,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -7370,7 +7573,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7380,10 +7583,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -7405,32 +7613,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7505,7 +7714,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7516,7 +7725,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -7557,7 +7766,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7567,10 +7776,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -7592,32 +7806,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7692,7 +7907,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7703,7 +7918,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -7744,7 +7959,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7754,10 +7969,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -7778,32 +7998,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7911,7 +8132,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7922,7 +8143,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -7974,7 +8195,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7984,10 +8205,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -8005,32 +8231,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8138,7 +8365,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8149,7 +8376,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -8201,7 +8428,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8211,10 +8438,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -8233,32 +8465,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8366,7 +8599,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8377,7 +8610,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -8429,7 +8662,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8439,10 +8672,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -8464,32 +8702,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8621,7 +8860,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8632,7 +8871,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -8692,7 +8931,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8702,10 +8941,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -8727,32 +8971,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8848,7 +9093,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8859,7 +9104,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -8907,7 +9152,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8917,10 +9162,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -8942,32 +9192,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9042,7 +9293,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9053,7 +9304,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -9094,7 +9345,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9104,10 +9355,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -9129,32 +9385,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9247,7 +9504,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9258,7 +9515,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -9305,7 +9562,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9315,10 +9572,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -9340,32 +9602,33 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9446,7 +9709,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9457,7 +9720,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // With RIGHT ANTI join, we are only interested in unmatched tuples @@ -9500,7 +9763,7 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9510,10 +9773,15 @@ func (o *mergeJoinRightAntiOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go index b5661129f242..bd6ae5db6595 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_rightouter.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,32 +77,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -200,7 +202,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -211,7 +213,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -261,7 +263,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -271,10 +273,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -296,32 +303,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -396,7 +404,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -407,7 +415,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -449,7 +457,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -459,10 +467,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -484,32 +497,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -584,7 +598,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -595,7 +609,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -637,7 +651,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -647,10 +661,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -671,32 +690,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -804,7 +824,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -815,7 +835,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -868,7 +888,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -878,10 +898,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -899,32 +924,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1032,7 +1058,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1043,7 +1069,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1096,7 +1122,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1106,10 +1132,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -1128,32 +1159,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1261,7 +1293,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1272,7 +1304,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1325,7 +1357,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1335,10 +1367,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1360,32 +1397,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1517,7 +1555,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1528,7 +1566,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1589,7 +1627,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1599,10 +1637,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1624,32 +1667,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1745,7 +1789,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1756,7 +1800,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1805,7 +1849,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -1815,10 +1859,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1840,32 +1889,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1940,7 +1990,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1951,7 +2001,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -1993,7 +2043,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2003,10 +2053,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -2028,32 +2083,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2146,7 +2202,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2157,7 +2213,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2205,7 +2261,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2215,10 +2271,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -2240,32 +2301,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2346,7 +2408,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2357,7 +2419,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2401,7 +2463,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2411,10 +2473,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -2435,6 +2502,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -2457,32 +2525,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2581,7 +2650,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2592,7 +2661,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2642,7 +2711,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2652,10 +2721,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2677,32 +2751,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2777,7 +2852,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2788,7 +2863,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -2830,7 +2905,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -2840,10 +2915,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2865,32 +2945,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2965,7 +3046,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2976,7 +3057,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3018,7 +3099,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3028,10 +3109,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -3052,32 +3138,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3185,7 +3272,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3196,7 +3283,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3249,7 +3336,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3259,10 +3346,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -3280,32 +3372,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3413,7 +3506,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3424,7 +3517,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3477,7 +3570,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3487,10 +3580,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -3509,32 +3607,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3642,7 +3741,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3653,7 +3752,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3706,7 +3805,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3716,10 +3815,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -3741,32 +3845,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3898,7 +4003,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3909,7 +4014,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -3970,7 +4075,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -3980,10 +4085,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4005,32 +4115,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4126,7 +4237,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4137,7 +4248,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4186,7 +4297,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4196,10 +4307,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -4221,32 +4337,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4321,7 +4438,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4332,7 +4449,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4374,7 +4491,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4384,10 +4501,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -4409,32 +4531,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4527,7 +4650,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4538,7 +4661,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4586,7 +4709,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4596,10 +4719,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -4621,32 +4749,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4727,7 +4856,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4738,7 +4867,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -4782,7 +4911,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -4792,10 +4921,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSeltrueRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -4816,6 +4950,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -4838,32 +4973,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4962,7 +5098,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4973,7 +5109,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5023,7 +5159,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5033,10 +5169,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5058,32 +5199,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5158,7 +5300,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5169,7 +5311,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5211,7 +5353,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5221,10 +5363,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5246,32 +5393,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5346,7 +5494,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5357,7 +5505,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5399,7 +5547,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5409,10 +5557,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -5433,32 +5586,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5566,7 +5720,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5577,7 +5731,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5630,7 +5784,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5640,10 +5794,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -5661,32 +5820,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5794,7 +5954,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5805,7 +5965,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -5858,7 +6018,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -5868,10 +6028,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -5890,32 +6055,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6023,7 +6189,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6034,7 +6200,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6087,7 +6253,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6097,10 +6263,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6122,32 +6293,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6279,7 +6451,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6290,7 +6462,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6351,7 +6523,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6361,10 +6533,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6386,32 +6563,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6507,7 +6685,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6518,7 +6696,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6567,7 +6745,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6577,10 +6755,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6602,32 +6785,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6702,7 +6886,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6713,7 +6897,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6755,7 +6939,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6765,10 +6949,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -6790,32 +6979,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6908,7 +7098,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6919,7 +7109,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -6967,7 +7157,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -6977,10 +7167,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7002,32 +7197,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7108,7 +7304,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7119,7 +7315,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7163,7 +7359,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7173,10 +7369,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSeltrue() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -7197,6 +7398,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -7219,32 +7421,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7343,7 +7546,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7354,7 +7557,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7404,7 +7607,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7414,10 +7617,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -7439,32 +7647,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7539,7 +7748,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7550,7 +7759,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7592,7 +7801,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7602,10 +7811,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -7627,32 +7841,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7727,7 +7942,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7738,7 +7953,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -7780,7 +7995,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -7790,10 +8005,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -7814,32 +8034,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7947,7 +8168,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7958,7 +8179,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8011,7 +8232,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8021,10 +8242,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -8042,32 +8268,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8175,7 +8402,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8186,7 +8413,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8239,7 +8466,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8249,10 +8476,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -8271,32 +8503,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8404,7 +8637,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8415,7 +8648,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8468,7 +8701,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8478,10 +8711,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -8503,32 +8741,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8660,7 +8899,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8671,7 +8910,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8732,7 +8971,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8742,10 +8981,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -8767,32 +9011,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -8888,7 +9133,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -8899,7 +9144,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -8948,7 +9193,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -8958,10 +9203,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -8983,32 +9233,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9083,7 +9334,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9094,7 +9345,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9136,7 +9387,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9146,10 +9397,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -9171,32 +9427,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9289,7 +9546,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9300,7 +9557,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9348,7 +9605,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9358,10 +9615,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -9383,32 +9645,33 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -9489,7 +9752,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -9500,7 +9763,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // Neither group ends with the batch, so add the group to the @@ -9544,7 +9807,7 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { } } } - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -9554,10 +9817,15 @@ func (o *mergeJoinRightOuterOp) probeBodyLSelfalseRSelfalse() { curRIdx++ } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go b/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go index 6eca76b2c3cc..d2cd18ca9289 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_rightsemi.eg.go @@ -54,6 +54,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -76,21 +77,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -189,7 +192,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -200,7 +203,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -218,10 +221,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -243,21 +251,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -332,7 +342,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -343,7 +353,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -361,10 +371,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -386,21 +401,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -475,7 +492,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -486,7 +503,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -504,10 +521,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -528,21 +550,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -650,7 +674,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -661,7 +685,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -679,10 +703,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -700,21 +729,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -822,7 +853,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -833,7 +864,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -851,10 +882,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -873,21 +909,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -995,7 +1033,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1006,7 +1044,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -1024,10 +1062,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -1049,21 +1092,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1195,7 +1240,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1206,7 +1251,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -1224,10 +1269,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -1249,21 +1299,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1359,7 +1411,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1370,7 +1422,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -1388,10 +1440,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -1413,21 +1470,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1502,7 +1561,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1513,7 +1572,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -1531,10 +1590,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -1556,21 +1620,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1663,7 +1729,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1674,7 +1740,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -1692,10 +1758,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -1717,21 +1788,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -1812,7 +1885,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -1823,7 +1896,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -1841,10 +1914,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -1865,6 +1943,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -1887,21 +1966,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2000,7 +2081,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2011,7 +2092,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -2029,10 +2110,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -2054,21 +2140,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2143,7 +2231,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2154,7 +2242,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -2172,10 +2260,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -2197,21 +2290,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2286,7 +2381,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2297,7 +2392,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -2315,10 +2410,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -2339,21 +2439,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2461,7 +2563,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2472,7 +2574,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -2490,10 +2592,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -2511,21 +2618,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2633,7 +2742,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2644,7 +2753,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -2662,10 +2771,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -2684,21 +2798,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -2806,7 +2922,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -2817,7 +2933,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -2835,10 +2951,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -2860,21 +2981,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3006,7 +3129,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3017,7 +3140,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3035,10 +3158,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -3060,21 +3188,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3170,7 +3300,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3181,7 +3311,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3199,10 +3329,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -3224,21 +3359,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3313,7 +3450,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3324,7 +3461,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3342,10 +3479,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -3367,21 +3509,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3474,7 +3618,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3485,7 +3629,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3503,10 +3647,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -3528,21 +3677,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(lSel[curLIdx]) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3623,7 +3774,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3634,7 +3785,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3652,10 +3803,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSeltrueRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -3676,6 +3832,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -3698,21 +3855,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3811,7 +3970,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3822,7 +3981,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3840,10 +3999,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -3865,21 +4029,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -3954,7 +4120,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -3965,7 +4131,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -3983,10 +4149,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -4008,21 +4179,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4097,7 +4270,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4108,7 +4281,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -4126,10 +4299,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -4150,21 +4328,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4272,7 +4452,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4283,7 +4463,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -4301,10 +4481,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -4322,21 +4507,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4444,7 +4631,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4455,7 +4642,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -4473,10 +4660,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -4495,21 +4687,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4617,7 +4811,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4628,7 +4822,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -4646,10 +4840,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -4671,21 +4870,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4817,7 +5018,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4828,7 +5029,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -4846,10 +5047,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -4871,21 +5077,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -4981,7 +5189,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -4992,7 +5200,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5010,10 +5218,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -5035,21 +5248,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5124,7 +5339,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5135,7 +5350,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5153,10 +5368,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -5178,21 +5398,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5285,7 +5507,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5296,7 +5518,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5314,10 +5536,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -5339,21 +5566,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(rSel[curRIdx]) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5434,7 +5663,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5445,7 +5674,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5463,10 +5692,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSeltrue() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: @@ -5487,6 +5721,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 lNulls := lVec.Nulls() rNulls := rVec.Nulls() switch lVec.CanonicalTypeFamily() { @@ -5509,21 +5744,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5622,7 +5859,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5633,7 +5870,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5651,10 +5888,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.BytesFamily: @@ -5676,21 +5918,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5765,7 +6009,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5776,7 +6020,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5794,10 +6038,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.DecimalFamily: @@ -5819,21 +6068,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -5908,7 +6159,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -5919,7 +6170,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -5937,10 +6188,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntFamily: @@ -5961,21 +6217,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6083,7 +6341,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6094,7 +6352,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -6112,10 +6370,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case 32: lKeys := lVec.Int32() @@ -6133,21 +6396,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6255,7 +6520,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6266,7 +6531,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -6284,10 +6549,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } case -1: default: @@ -6306,21 +6576,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6428,7 +6700,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6439,7 +6711,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -6457,10 +6729,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.FloatFamily: @@ -6482,21 +6759,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6628,7 +6907,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6639,7 +6918,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -6657,10 +6936,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.TimestampTZFamily: @@ -6682,21 +6966,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6792,7 +7078,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6803,7 +7089,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -6821,10 +7107,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.IntervalFamily: @@ -6846,21 +7137,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -6935,7 +7228,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -6946,7 +7239,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -6964,10 +7257,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case types.JsonFamily: @@ -6989,21 +7287,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7096,7 +7396,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7107,7 +7407,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -7125,10 +7425,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } case typeconv.DatumVecCanonicalTypeFamily: @@ -7150,21 +7455,23 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(curLIdx) rNull := rNulls.NullAt(curRIdx) - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } @@ -7245,7 +7552,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -7256,7 +7563,7 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { o.groups.addRightSemiGroup(beginRIdx, rGroupLength) @@ -7274,10 +7581,15 @@ func (o *mergeJoinRightSemiOp) probeBodyLSelfalseRSelfalse() { } } } - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } } default: diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go b/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go index 620994a13745..ff29ce6837f9 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_tmpl.go @@ -129,11 +129,10 @@ func _PROBE_SWITCH(_JOIN_TYPE joinTypeInfo, _SEL_PERMUTATION selPermutation) { / curRIdx := rGroup.rowStartIdx curLEndIdx := lGroup.rowEndIdx curREndIdx := rGroup.rowEndIdx - areGroupsProcessed := false _LEFT_UNMATCHED_GROUP_SWITCH(_JOIN_TYPE) _RIGHT_UNMATCHED_GROUP_SWITCH(_JOIN_TYPE) // Expand or filter each group based on the current equality column. - for curLIdx < curLEndIdx && curRIdx < curREndIdx && !areGroupsProcessed { + for curLIdx < curLEndIdx && curRIdx < curREndIdx { cmp = 0 lNull := lNulls.NullAt(_L_SEL_IND) rNull := rNulls.NullAt(_R_SEL_IND) @@ -163,16 +162,19 @@ func _PROBE_SWITCH(_JOIN_TYPE joinTypeInfo, _SEL_PERMUTATION selPermutation) { / // so if either value is NULL, the tuples are not // matches. // */}} - // TODO(yuzefovich): we can advance both sides if both are - // NULL. + curLIdxInc := 0 if lNull { _NULL_FROM_LEFT_SWITCH(_JOIN_TYPE) - curLIdx++ - continue + curLIdxInc = 1 } + curRIdxInc := 0 if rNull { _NULL_FROM_RIGHT_SWITCH(_JOIN_TYPE) - curRIdx++ + curRIdxInc = 1 + } + if lNull || rNull { + curLIdx += curLIdxInc + curRIdx += curRIdxInc continue } // {{end}} @@ -268,7 +270,7 @@ func _PROBE_SWITCH(_JOIN_TYPE joinTypeInfo, _SEL_PERMUTATION selPermutation) { / } // Last equality column and either group is incomplete. - if eqColIdx == len(o.left.eqCols)-1 && (!lComplete || !rComplete) { + if lastEqCol && (!lComplete || !rComplete) { // Store the state about the buffered group. o.startLeftBufferedGroup(lSel, beginLIdx, lGroupLength) o.bufferedGroup.leftGroupStartIdx = beginLIdx @@ -279,7 +281,7 @@ func _PROBE_SWITCH(_JOIN_TYPE joinTypeInfo, _SEL_PERMUTATION selPermutation) { / return } - if eqColIdx < len(o.left.eqCols)-1 { + if !lastEqCol { o.groups.addGroupsToNextCol(beginLIdx, lGroupLength, beginRIdx, rGroupLength) } else { // {{if _JOIN_TYPE.IsLeftSemi}} @@ -331,10 +333,15 @@ func _PROBE_SWITCH(_JOIN_TYPE joinTypeInfo, _SEL_PERMUTATION selPermutation) { / } } _PROCESS_NOT_LAST_GROUP_IN_COLUMN_SWITCH(_JOIN_TYPE) - // Both o.proberState.lIdx and o.proberState.rIdx should point to the - // last elements processed in their respective batches. - o.proberState.lIdx = curLIdx - o.proberState.rIdx = curRIdx + // Both o.proberState.lIdx and o.proberState.rIdx should point + // to the last tuples that have been fully processed in their + // respective batches. This is the case when we've just finished + // the last equality column or the current column is such that + // all tuples were filtered out. + if lastEqCol || !o.groups.hasGroupForNextCol() { + o.proberState.lIdx = curLIdx + o.proberState.rIdx = curRIdx + } } // {{end}} } @@ -361,14 +368,13 @@ func _LEFT_UNMATCHED_GROUP_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} // {{end}} // {{if or $.JoinType.IsLeftOuter $.JoinType.IsLeftAnti}} if lGroup.unmatched { - if curLIdx+1 != curLEndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the left unmatched group is not 1", curLEndIdx-curLIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addLeftUnmatchedGroup(curLIdx, curRIdx) - curLIdx++ - areGroupsProcessed = true + if lastEqCol && curLIdx >= o.proberState.lIdx { + o.proberState.lIdx = curLIdx + 1 + } + continue } // {{end}} // {{if or $.JoinType.IsRightOuter $.JoinType.IsRightAnti}} @@ -403,14 +409,13 @@ func _RIGHT_UNMATCHED_GROUP_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} // {{end}} // {{if or $.JoinType.IsRightOuter $.JoinType.IsRightAnti}} if rGroup.unmatched { - if curRIdx+1 != curREndIdx { - colexecerror.InternalError(errors.AssertionFailedf("unexpectedly length %d of the right unmatched group is not 1", curREndIdx-curRIdx)) - } // The row already does not have a match, so we don't need to do any // additional processing. o.groups.addRightUnmatchedGroup(curLIdx, curRIdx) - curRIdx++ - areGroupsProcessed = true + if lastEqCol && curRIdx >= o.proberState.rIdx { + o.proberState.rIdx = curRIdx + 1 + } + continue } // {{end}} // {{end}} @@ -605,7 +610,7 @@ func _PROCESS_NOT_LAST_GROUP_IN_COLUMN_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} // */}} // {{end}} // {{if or $.JoinType.IsLeftOuter $.JoinType.IsLeftAnti}} - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the left group will not get a match, so each one of @@ -617,7 +622,7 @@ func _PROCESS_NOT_LAST_GROUP_IN_COLUMN_SWITCH(_JOIN_TYPE joinTypeInfo) { // */}} } // {{end}} // {{if or $.JoinType.IsRightOuter $.JoinType.IsRightAnti}} - if !o.groups.isLastGroupInCol() && !areGroupsProcessed { + if !o.groups.isLastGroupInCol() { // The current group is not the last one within the column, so it cannot be // extended into the next batch, and we need to process it right now. Any // unprocessed row in the right group will not get a match, so each one of @@ -644,6 +649,7 @@ func (o *mergeJoin_JOIN_TYPE_STRINGOp) probeBodyLSel_IS_L_SELRSel_IS_R_SEL() { lVec := o.proberState.lBatch.ColVec(int(leftColIdx)) rVec := o.proberState.rBatch.ColVec(int(rightColIdx)) colType := o.left.sourceTypes[leftColIdx] + lastEqCol := eqColIdx == len(o.left.eqCols)-1 _PROBE_SWITCH(_JOIN_TYPE, _SEL_ARG) // Look at the groups associated with the next equality column by moving // the circular buffer pointer up. diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner_util.go b/pkg/sql/colexec/colexecjoin/mergejoiner_util.go index 74df84023034..10dae4754e7f 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner_util.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner_util.go @@ -148,6 +148,12 @@ func (b *circularGroupsBuffer) isLastGroupInCol() bool { return b.startIdx == b.nextColStartIdx } +// hasGroupForNextCol returns true if at least one group has already been added +// for the next column. +func (b *circularGroupsBuffer) hasGroupForNextCol() bool { + return b.nextColStartIdx != b.endIdx +} + // ensureCapacityForNewGroup makes sure that groups slices have enough space to // add another group to the buffer, reallocating the slices if necessary. func (b *circularGroupsBuffer) ensureCapacityForNewGroup() { diff --git a/pkg/sql/colexec/mergejoiner_test.go b/pkg/sql/colexec/mergejoiner_test.go index 81f7c1c3c940..40780fa33c93 100644 --- a/pkg/sql/colexec/mergejoiner_test.go +++ b/pkg/sql/colexec/mergejoiner_test.go @@ -1639,6 +1639,19 @@ func getMJTestCases() []*joinTestCase { rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC}, expected: colexectestutils.Tuples{{0, 4, nil, nil}, {nil, nil, 0, 1}, {nil, 0, nil, 0}, {3, 0, nil, 0}, {nil, nil, 4, nil}}, }, + { + description: "RIGHT OUTER join with a single match in the middle", + joinType: descpb.RightOuterJoin, + leftTypes: []*types.T{types.Int, types.Int}, + rightTypes: []*types.T{types.Int, types.Int}, + leftTuples: colexectestutils.Tuples{{nil, 4}, {nil, 4}, {2, nil}, {2, nil}, {2, 4}, {3, 0}}, + rightTuples: colexectestutils.Tuples{{nil, 1}, {1, nil}, {1, 0}, {2, 4}, {3, nil}, {3, 2}}, + leftOutCols: []uint32{0, 1}, + rightOutCols: []uint32{0, 1}, + leftEqCols: []uint32{0, 1}, + rightEqCols: []uint32{0, 1}, + expected: colexectestutils.Tuples{{nil, nil, nil, 1}, {nil, nil, 1, nil}, {nil, nil, 1, 0}, {2, 4, 2, 4}, {nil, nil, 3, nil}, {nil, nil, 3, 2}}, + }, } return withMirrors(mjTestCases) } From 74a0e8e52d5aad1de1ed07e8a4290c08326679b9 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Mon, 16 Aug 2021 20:53:44 -0700 Subject: [PATCH 103/205] colexecjoin: avoid buffering tuples from the right in merge joiner Depending on the join type, we don't need to fully buffer the tuples from the right input in order to produce the output. Namely, for set-operation joins we only need to know the number of right tuples whereas for LEFT SEMI and RIGHT ANTI we know exactly the behavior of the builder for the buffered group. Release note: None --- pkg/sql/colexec/colexecjoin/mergejoiner.go | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner.go b/pkg/sql/colexec/colexecjoin/mergejoiner.go index b8593dcd39b6..0abb41d31ac4 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner.go @@ -665,14 +665,6 @@ func (o *mergeJoinBase) appendToRightBufferedGroup(sel []int, groupStartIdx int, sourceTypes := o.right.sourceTypes numBufferedTuples := o.bufferedGroup.helper.numRightTuples o.bufferedGroup.helper.numRightTuples += groupLength - // TODO(yuzefovich): for LEFT/RIGHT ANTI joins we only need to store the - // first tuple (in order to find the boundaries of the groups) since all - // of the buffered tuples do have a match and, thus, don't contribute to - // the output. - // TODO(yuzefovich): for INTERSECT/EXCEPT ALL joins we can buffer only - // tuples from the left side and count the number of tuples on the right. - // TODO(yuzefovich): for LEFT/RIGHT SEMI joins we only need to buffer tuples - // from one side (left/right respectively). if numBufferedTuples == 0 && groupStartIdx+groupLength == o.proberState.rLength { // Set the right first tuple only if this is the first call to this // method for the current right buffered group and if the group doesn't @@ -692,6 +684,21 @@ func (o *mergeJoinBase) appendToRightBufferedGroup(sel []int, groupStartIdx int, }) } + // TODO(yuzefovich): check whether it's worth templating this method out as + // well as having join-type-specific crossJoinerBase. + switch o.joinType { + case descpb.LeftSemiJoin, descpb.RightAntiJoin: + // For LEFT SEMI and RIGHT ANTI joins we only need to store the first + // tuple (in order to find the boundaries of the groups) since all of + // the buffered tuples don't/do have a match and, thus, do/don't + // contribute to the output. + return + case descpb.IntersectAllJoin, descpb.ExceptAllJoin: + // For INTERSECT/EXCEPT ALL joins we only need the number of tuples on + // the right side (which we have already updated above). + return + } + // We don't impose any memory limits on the scratch batch because we rely on // the inputs to the merge joiner to produce reasonably sized batches. const maxBatchMemSize = math.MaxInt64 From 4999ac5c2edee2f94a91dbe338c87aa190be3cc4 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Mon, 16 Aug 2021 21:12:53 -0700 Subject: [PATCH 104/205] colexecjoin: remove a copy when buffering the right group Previously, before enqueueing the tuples from the right buffered group into the spiling queue we would perform a deep-copy. This is an overkill because the spilling queue itself performs the deep copy. This commit refactors the enqueueing code to modify the right batch directly to include only the tuples from the group. Release note: None --- pkg/sql/colexec/colexecjoin/mergejoiner.go | 64 +++++++++++++--------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/pkg/sql/colexec/colexecjoin/mergejoiner.go b/pkg/sql/colexec/colexecjoin/mergejoiner.go index 0abb41d31ac4..07f8af436e49 100644 --- a/pkg/sql/colexec/colexecjoin/mergejoiner.go +++ b/pkg/sql/colexec/colexecjoin/mergejoiner.go @@ -12,7 +12,6 @@ package colexecjoin import ( "context" - "math" "unsafe" "github.com/cockroachdb/cockroach/pkg/col/coldata" @@ -248,9 +247,8 @@ type mjBufferedGroupState struct { // rightFirstTuple is the first tuple of the right buffered group. It is set // only in case the right buffered group spans more than one input batch. rightFirstTuple []coldata.Vec - // rightScratchBatch is a scratch space for copying the tuples out of the - // right input batches before enqueueing them into the spilling queue. - rightScratchBatch coldata.Batch + // scratchSel is a scratch selection vector initialized only when needed. + scratchSel []int // helper is the building facility for the cross join of the buffered group. helper *crossJoinerBase @@ -699,29 +697,45 @@ func (o *mergeJoinBase) appendToRightBufferedGroup(sel []int, groupStartIdx int, return } - // We don't impose any memory limits on the scratch batch because we rely on - // the inputs to the merge joiner to produce reasonably sized batches. - const maxBatchMemSize = math.MaxInt64 - o.bufferedGroup.rightScratchBatch, _ = o.unlimitedAllocator.ResetMaybeReallocate( - sourceTypes, o.bufferedGroup.rightScratchBatch, groupLength, maxBatchMemSize, - ) - // TODO(yuzefovich): SpillingQueue.Enqueue deep-copies the batch too. Think - // through whether the copy here can be avoided altogether. - o.unlimitedAllocator.PerformOperation(o.bufferedGroup.rightScratchBatch.ColVecs(), func() { - for colIdx := range sourceTypes { - o.bufferedGroup.rightScratchBatch.ColVec(colIdx).Copy( - coldata.SliceArgs{ - Src: o.proberState.rBatch.ColVec(colIdx), - Sel: sel, - DestIdx: 0, - SrcStartIdx: groupStartIdx, - SrcEndIdx: groupStartIdx + groupLength, - }, + // Update the selection on the probing batch to only include tuples from the + // buffered group. + rBatch, rLength := o.proberState.rBatch, o.proberState.rLength + rSel := rBatch.Selection() + rBatchHasSel := rSel != nil + // No need to modify the batch if the whole batch is part of the buffered + // group. + needToModify := groupStartIdx != 0 || groupLength != rLength + if needToModify { + if rBatchHasSel { + // Since rBatch already has a selection vector which we'll be + // modifying, we need to copy the original. + o.bufferedGroup.scratchSel = colexecutils.EnsureSelectionVectorLength(o.bufferedGroup.scratchSel, rLength) + copy(o.bufferedGroup.scratchSel, rSel) + // Now we need to shift elements in range + // [groupStartIdx; groupStartIdx+groupLength) to the beginning of + // the selection vector and then update the length of the batch + // accordingly. + copy(rSel[:groupLength], rSel[groupStartIdx:groupStartIdx+groupLength]) + rBatch.SetLength(groupLength) + } else { + // Since rBatch doesn't have a selection vector, we will set the + // selection vector to include tuples in range + // [groupStartIdx; groupStartIdx+groupLength). + colexecutils.UpdateBatchState( + rBatch, groupLength, true, /* usesSel */ + colexecutils.DefaultSelectionVector[groupStartIdx:groupStartIdx+groupLength], ) } - o.bufferedGroup.rightScratchBatch.SetLength(groupLength) - }) - bufferedTuples.Enqueue(o.Ctx, o.bufferedGroup.rightScratchBatch) + } + + bufferedTuples.Enqueue(o.Ctx, rBatch) + + // If we had to modify the batch, then restore the original state now. + if needToModify { + colexecutils.UpdateBatchState( + rBatch, rLength, rBatchHasSel, o.bufferedGroup.scratchSel, + ) + } } // sourceFinished returns true if either of input sources has no more rows. From 3966d84f76e2631c1ef6f7385c460e6b7b321499 Mon Sep 17 00:00:00 2001 From: Rail Aliiev Date: Thu, 28 Oct 2021 21:43:12 -0400 Subject: [PATCH 105/205] roachprod: install AWS CLI v2 for GC images Previously, after regenerating the GC docker images, roachprod stopped listing AWS as an available provider, because Debian ships with AWS CLI v1, but roachprod doesn't support it. This patch installs AWS CLI v2. Release note: None --- pkg/cmd/roachprod/docker/build.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/roachprod/docker/build.sh b/pkg/cmd/roachprod/docker/build.sh index 8984929c2605..d76c8d4a14c5 100755 --- a/pkg/cmd/roachprod/docker/build.sh +++ b/pkg/cmd/roachprod/docker/build.sh @@ -26,5 +26,16 @@ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.gpg] https://packa # Install packages and clean up apt-get update -y -apt-get install google-cloud-sdk awscli azure-cli -y +apt-get install -y google-cloud-sdk azure-cli unzip rm -rf /var/lib/apt/lists/* + +# Debian ships with awscli version 1.x, which is unsupported by roachprod. +# Install aws-cli using the official instructions from +# https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.0.30.zip" -o "awscliv2.zip" +sha256sum -c - < Date: Thu, 28 Oct 2021 19:37:24 -0700 Subject: [PATCH 106/205] pgwire: add benchmarks for writing from columnar representation Release note: None --- pkg/col/coldatatestutils/BUILD.bazel | 1 + pkg/col/coldatatestutils/random_testutils.go | 5 + pkg/sql/pgwire/BUILD.bazel | 1 + pkg/sql/pgwire/types_test.go | 179 +++++++++++++++++++ 4 files changed, 186 insertions(+) diff --git a/pkg/col/coldatatestutils/BUILD.bazel b/pkg/col/coldatatestutils/BUILD.bazel index 0fe8ed2771f7..a9b5cfa2a797 100644 --- a/pkg/col/coldatatestutils/BUILD.bazel +++ b/pkg/col/coldatatestutils/BUILD.bazel @@ -19,6 +19,7 @@ go_library( "//pkg/util/duration", "//pkg/util/json", "//pkg/util/timeutil", + "//pkg/util/uuid", "@com_github_cockroachdb_errors//:errors", ], ) diff --git a/pkg/col/coldatatestutils/random_testutils.go b/pkg/col/coldatatestutils/random_testutils.go index 49517de9d6f5..ae7b0562b546 100644 --- a/pkg/col/coldatatestutils/random_testutils.go +++ b/pkg/col/coldatatestutils/random_testutils.go @@ -25,6 +25,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/duration" "github.com/cockroachdb/cockroach/pkg/util/json" "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/cockroach/pkg/util/uuid" "github.com/cockroachdb/errors" ) @@ -91,11 +92,15 @@ func RandomVec(args RandomVecArgs) { } case types.BytesFamily: bytes := args.Vec.Bytes() + isUUID := args.Vec.Type().Family() == types.UuidFamily for i := 0; i < args.N; i++ { bytesLen := args.BytesFixedLength if bytesLen <= 0 { bytesLen = args.Rand.Intn(maxVarLen) } + if isUUID { + bytesLen = uuid.Size + } randBytes := make([]byte, bytesLen) // Read always returns len(bytes[i]) and nil. _, _ = rand.Read(randBytes) diff --git a/pkg/sql/pgwire/BUILD.bazel b/pkg/sql/pgwire/BUILD.bazel index 38e80198ade1..d6058cf3b9de 100644 --- a/pkg/sql/pgwire/BUILD.bazel +++ b/pkg/sql/pgwire/BUILD.bazel @@ -85,6 +85,7 @@ go_test( "//pkg/cloud/impl:cloudimpl", "//pkg/col/coldata", "//pkg/col/coldataext", + "//pkg/col/coldatatestutils", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", diff --git a/pkg/sql/pgwire/types_test.go b/pkg/sql/pgwire/types_test.go index a36696f2ed9e..cc650be1afdc 100644 --- a/pkg/sql/pgwire/types_test.go +++ b/pkg/sql/pgwire/types_test.go @@ -19,6 +19,9 @@ import ( "testing" "time" + "github.com/cockroachdb/cockroach/pkg/col/coldata" + "github.com/cockroachdb/cockroach/pkg/col/coldataext" + "github.com/cockroachdb/cockroach/pkg/col/coldatatestutils" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase" "github.com/cockroachdb/cockroach/pkg/sql/randgen" @@ -29,6 +32,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/metric" + "github.com/cockroachdb/cockroach/pkg/util/randutil" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/cockroach/pkg/util/uuid" ) @@ -295,18 +299,83 @@ func benchmarkWriteType(b *testing.B, d tree.Datum, format pgwirebase.FormatCode } } +func benchmarkWriteColumnar(b *testing.B, batch coldata.Batch, format pgwirebase.FormatCode) { + ctx := context.Background() + + buf := newWriteBuffer(nil /* bytecount */) + buf.bytecount = metric.NewCounter(metric.Metadata{Name: ""}) + + writeMethod := func(ctx context.Context, batch coldata.Batch, loc *time.Location) { + defaultConv, _ := makeTestingConvCfg() + for rowIdx := 0; rowIdx < batch.Length(); rowIdx++ { + buf.writeTextColumnarElement(ctx, batch.ColVec(0), rowIdx, defaultConv, loc) + } + } + if format == pgwirebase.FormatBinary { + writeMethod = func(ctx context.Context, batch coldata.Batch, loc *time.Location) { + for rowIdx := 0; rowIdx < batch.Length(); rowIdx++ { + buf.writeBinaryColumnarElement(ctx, batch.ColVec(0), rowIdx, loc) + } + } + } + + // Warm up the buffer. + writeMethod(ctx, batch, nil) + buf.wrapped.Reset() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Starting and stopping the timer in each loop iteration causes this + // to take much longer. See http://stackoverflow.com/a/37624250/3435257. + // buf.wrapped.Reset() should be fast enough to be negligible. + writeMethod(ctx, batch, nil) + if buf.err != nil { + b.Fatal(buf.err) + } + buf.wrapped.Reset() + } +} + +// getBatch returns a batch with a single vector of the provided type, +// coldata.BatchSize() in length. +func getBatch(t *types.T) coldata.Batch { + evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) + batch := coldata.NewMemBatch([]*types.T{t}, coldataext.NewExtendedColumnFactory(&evalCtx)) + rng, _ := randutil.NewTestRand() + coldatatestutils.RandomVec(coldatatestutils.RandomVecArgs{ + Rand: rng, + Vec: batch.ColVec(0), + N: coldata.BatchSize(), + BytesFixedLength: 8, + }) + batch.SetLength(coldata.BatchSize()) + return batch +} + func benchmarkWriteBool(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, tree.DBoolTrue, format) } +func benchmarkWriteColumnarBool(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Bool), format) +} + func benchmarkWriteInt(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, tree.NewDInt(1234), format) } +func benchmarkWriteColumnarInt(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Int), format) +} + func benchmarkWriteFloat(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, tree.NewDFloat(12.34), format) } +func benchmarkWriteColumnarFloat(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Float), format) +} + func benchmarkWriteDecimal(b *testing.B, format pgwirebase.FormatCode) { dec := new(tree.DDecimal) s := "-1728718718271827121233.1212121212" @@ -316,19 +385,35 @@ func benchmarkWriteDecimal(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, dec, pgwirebase.FormatText) } +func benchmarkWriteColumnarDecimal(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Decimal), format) +} + func benchmarkWriteBytes(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, tree.NewDBytes("testing"), format) } +func benchmarkWriteColumnarBytes(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Bytes), format) +} + func benchmarkWriteUUID(b *testing.B, format pgwirebase.FormatCode) { u := uuid.MakeV4() benchmarkWriteType(b, tree.NewDUuid(tree.DUuid{UUID: u}), format) } +func benchmarkWriteColumnarUUID(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Uuid), format) +} + func benchmarkWriteString(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, tree.NewDString("testing"), format) } +func benchmarkWriteColumnarString(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.String), format) +} + func benchmarkWriteDate(b *testing.B, format pgwirebase.FormatCode) { d, _, err := tree.ParseDDate(nil, "2010-09-28") if err != nil { @@ -337,6 +422,10 @@ func benchmarkWriteDate(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, d, format) } +func benchmarkWriteColumnarDate(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Date), format) +} + func benchmarkWriteTimestamp(b *testing.B, format pgwirebase.FormatCode) { ts, _, err := tree.ParseDTimestamp(nil, "2010-09-28 12:00:00.1", time.Microsecond) if err != nil { @@ -345,6 +434,10 @@ func benchmarkWriteTimestamp(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, ts, format) } +func benchmarkWriteColumnarTimestamp(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Timestamp), format) +} + func benchmarkWriteTimestampTZ(b *testing.B, format pgwirebase.FormatCode) { tstz, _, err := tree.ParseDTimestampTZ(nil, "2010-09-28 12:00:00.1", time.Microsecond) if err != nil { @@ -353,6 +446,10 @@ func benchmarkWriteTimestampTZ(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, tstz, format) } +func benchmarkWriteColumnarTimestampTZ(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.TimestampTZ), format) +} + func benchmarkWriteInterval(b *testing.B, format pgwirebase.FormatCode) { i, err := tree.ParseDInterval(duration.IntervalStyle_POSTGRES, "PT12H2M") if err != nil { @@ -361,6 +458,10 @@ func benchmarkWriteInterval(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, i, format) } +func benchmarkWriteColumnarInterval(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.Interval), format) +} + func benchmarkWriteTuple(b *testing.B, format pgwirebase.FormatCode) { i := tree.NewDInt(1234) f := tree.NewDFloat(12.34) @@ -370,6 +471,11 @@ func benchmarkWriteTuple(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, t, format) } +func benchmarkWriteColumnarTuple(b *testing.B, format pgwirebase.FormatCode) { + typ := types.MakeTuple([]*types.T{types.Int, types.Float, types.String}) + benchmarkWriteColumnar(b, getBatch(typ), format) +} + func benchmarkWriteArray(b *testing.B, format pgwirebase.FormatCode) { a := tree.NewDArray(types.Int) for i := 0; i < 3; i++ { @@ -380,12 +486,22 @@ func benchmarkWriteArray(b *testing.B, format pgwirebase.FormatCode) { benchmarkWriteType(b, a, format) } +func benchmarkWriteColumnarArray(b *testing.B, format pgwirebase.FormatCode) { + benchmarkWriteColumnar(b, getBatch(types.MakeArray(types.Int)), format) +} + func BenchmarkWriteTextBool(b *testing.B) { benchmarkWriteBool(b, pgwirebase.FormatText) } func BenchmarkWriteBinaryBool(b *testing.B) { benchmarkWriteBool(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarBool(b *testing.B) { + benchmarkWriteColumnarBool(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarBool(b *testing.B) { + benchmarkWriteColumnarBool(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextInt(b *testing.B) { benchmarkWriteInt(b, pgwirebase.FormatText) @@ -393,6 +509,12 @@ func BenchmarkWriteTextInt(b *testing.B) { func BenchmarkWriteBinaryInt(b *testing.B) { benchmarkWriteInt(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarInt(b *testing.B) { + benchmarkWriteColumnarInt(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarInt(b *testing.B) { + benchmarkWriteColumnarInt(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextFloat(b *testing.B) { benchmarkWriteFloat(b, pgwirebase.FormatText) @@ -400,6 +522,12 @@ func BenchmarkWriteTextFloat(b *testing.B) { func BenchmarkWriteBinaryFloat(b *testing.B) { benchmarkWriteFloat(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarFloat(b *testing.B) { + benchmarkWriteColumnarFloat(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarFloat(b *testing.B) { + benchmarkWriteColumnarFloat(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextDecimal(b *testing.B) { benchmarkWriteDecimal(b, pgwirebase.FormatText) @@ -407,6 +535,12 @@ func BenchmarkWriteTextDecimal(b *testing.B) { func BenchmarkWriteBinaryDecimal(b *testing.B) { benchmarkWriteDecimal(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarDecimal(b *testing.B) { + benchmarkWriteColumnarDecimal(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarDecimal(b *testing.B) { + benchmarkWriteColumnarDecimal(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextBytes(b *testing.B) { benchmarkWriteBytes(b, pgwirebase.FormatText) @@ -414,6 +548,12 @@ func BenchmarkWriteTextBytes(b *testing.B) { func BenchmarkWriteBinaryBytes(b *testing.B) { benchmarkWriteBytes(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarBytes(b *testing.B) { + benchmarkWriteColumnarBytes(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarBytes(b *testing.B) { + benchmarkWriteColumnarBytes(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextUUID(b *testing.B) { benchmarkWriteUUID(b, pgwirebase.FormatText) @@ -421,6 +561,12 @@ func BenchmarkWriteTextUUID(b *testing.B) { func BenchmarkWriteBinaryUUID(b *testing.B) { benchmarkWriteUUID(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarUUID(b *testing.B) { + benchmarkWriteColumnarUUID(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarUUID(b *testing.B) { + benchmarkWriteColumnarUUID(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextString(b *testing.B) { benchmarkWriteString(b, pgwirebase.FormatText) @@ -428,6 +574,12 @@ func BenchmarkWriteTextString(b *testing.B) { func BenchmarkWriteBinaryString(b *testing.B) { benchmarkWriteString(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarString(b *testing.B) { + benchmarkWriteColumnarString(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarString(b *testing.B) { + benchmarkWriteColumnarString(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextDate(b *testing.B) { benchmarkWriteDate(b, pgwirebase.FormatText) @@ -435,6 +587,12 @@ func BenchmarkWriteTextDate(b *testing.B) { func BenchmarkWriteBinaryDate(b *testing.B) { benchmarkWriteDate(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarDate(b *testing.B) { + benchmarkWriteColumnarDate(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarDate(b *testing.B) { + benchmarkWriteColumnarDate(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextTimestamp(b *testing.B) { benchmarkWriteTimestamp(b, pgwirebase.FormatText) @@ -442,6 +600,12 @@ func BenchmarkWriteTextTimestamp(b *testing.B) { func BenchmarkWriteBinaryTimestamp(b *testing.B) { benchmarkWriteTimestamp(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarTimestamp(b *testing.B) { + benchmarkWriteColumnarTimestamp(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarTimestamp(b *testing.B) { + benchmarkWriteColumnarTimestamp(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextTimestampTZ(b *testing.B) { benchmarkWriteTimestampTZ(b, pgwirebase.FormatText) @@ -449,18 +613,33 @@ func BenchmarkWriteTextTimestampTZ(b *testing.B) { func BenchmarkWriteBinaryTimestampTZ(b *testing.B) { benchmarkWriteTimestampTZ(b, pgwirebase.FormatBinary) } +func BenchmarkWriteTextColumnarTimestampTZ(b *testing.B) { + benchmarkWriteColumnarTimestampTZ(b, pgwirebase.FormatText) +} +func BenchmarkWriteBinaryColumnarTimestampTZ(b *testing.B) { + benchmarkWriteColumnarTimestampTZ(b, pgwirebase.FormatBinary) +} func BenchmarkWriteTextInterval(b *testing.B) { benchmarkWriteInterval(b, pgwirebase.FormatText) } +func BenchmarkWriteTextColumnarInterval(b *testing.B) { + benchmarkWriteColumnarInterval(b, pgwirebase.FormatText) +} func BenchmarkWriteTextTuple(b *testing.B) { benchmarkWriteTuple(b, pgwirebase.FormatText) } +func BenchmarkWriteTextColumnarTuple(b *testing.B) { + benchmarkWriteColumnarTuple(b, pgwirebase.FormatText) +} func BenchmarkWriteTextArray(b *testing.B) { benchmarkWriteArray(b, pgwirebase.FormatText) } +func BenchmarkWriteTextColumnarArray(b *testing.B) { + benchmarkWriteColumnarArray(b, pgwirebase.FormatText) +} func BenchmarkDecodeBinaryDecimal(b *testing.B) { wbuf := newWriteBuffer(nil /* bytecount */) From a25136cfc71f8d4f6ba654731b6d7caf9e05b4b2 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Mon, 25 Oct 2021 14:31:30 -0700 Subject: [PATCH 107/205] pgwire: operate on the well-typed columns This commit applies the same optimization as in the previous commit but to the pgwire-writing logic. Release note: None --- pkg/sql/pgwire/conn.go | 53 ++++++++++--------- pkg/sql/pgwire/encoding_test.go | 14 ++--- pkg/sql/pgwire/types.go | 92 ++++++++++++++++++--------------- pkg/sql/pgwire/types_test.go | 9 +++- 4 files changed, 95 insertions(+), 73 deletions(-) diff --git a/pkg/sql/pgwire/conn.go b/pkg/sql/pgwire/conn.go index c4600b690417..433fd0e7f0d7 100644 --- a/pkg/sql/pgwire/conn.go +++ b/pkg/sql/pgwire/conn.go @@ -97,6 +97,9 @@ type conn struct { readBuf pgwirebase.ReadBuffer msgBuilder writeBuffer + // vecsScratch is a scratch space used by bufferBatch. + vecsScratch coldata.TypedVecs + sv *settings.Values // alwaysLogAuthActivity is used force-enables logging of authn events. @@ -1290,31 +1293,35 @@ func (c *conn) bufferBatch( ) { sel := batch.Selection() n := batch.Length() - vecs := batch.ColVecs() - width := int16(len(vecs)) - for i := 0; i < n; i++ { - rowIdx := i - if sel != nil { - rowIdx = sel[rowIdx] - } - c.msgBuilder.initMsg(pgwirebase.ServerMsgDataRow) - c.msgBuilder.putInt16(width) - for colIdx, col := range vecs { - fmtCode := pgwirebase.FormatText - if formatCodes != nil { - fmtCode = formatCodes[colIdx] + if n > 0 { + c.vecsScratch.SetBatch(batch) + // Make sure that c doesn't hold on to the memory of the batch. + defer c.vecsScratch.Reset() + width := int16(len(c.vecsScratch.Vecs)) + for i := 0; i < n; i++ { + rowIdx := i + if sel != nil { + rowIdx = sel[rowIdx] } - switch fmtCode { - case pgwirebase.FormatText: - c.msgBuilder.writeTextColumnarElement(ctx, col, rowIdx, conv, sessionLoc) - case pgwirebase.FormatBinary: - c.msgBuilder.writeBinaryColumnarElement(ctx, col, rowIdx, sessionLoc) - default: - c.msgBuilder.setError(errors.Errorf("unsupported format code %s", fmtCode)) + c.msgBuilder.initMsg(pgwirebase.ServerMsgDataRow) + c.msgBuilder.putInt16(width) + for vecIdx := 0; vecIdx < len(c.vecsScratch.Vecs); vecIdx++ { + fmtCode := pgwirebase.FormatText + if formatCodes != nil { + fmtCode = formatCodes[vecIdx] + } + switch fmtCode { + case pgwirebase.FormatText: + c.msgBuilder.writeTextColumnarElement(ctx, &c.vecsScratch, vecIdx, rowIdx, conv, sessionLoc) + case pgwirebase.FormatBinary: + c.msgBuilder.writeBinaryColumnarElement(ctx, &c.vecsScratch, vecIdx, rowIdx, sessionLoc) + default: + c.msgBuilder.setError(errors.Errorf("unsupported format code %s", fmtCode)) + } + } + if err := c.msgBuilder.finishMsg(&c.writerState.buf); err != nil { + panic(fmt.Sprintf("unexpected err from buffer: %s", err)) } - } - if err := c.msgBuilder.finishMsg(&c.writerState.buf); err != nil { - panic(fmt.Sprintf("unexpected err from buffer: %s", err)) } } } diff --git a/pkg/sql/pgwire/encoding_test.go b/pkg/sql/pgwire/encoding_test.go index af3ef6c1970f..ef23ad656810 100644 --- a/pkg/sql/pgwire/encoding_test.go +++ b/pkg/sql/pgwire/encoding_test.go @@ -165,17 +165,19 @@ func TestEncodings(t *testing.T) { writeBinaryDatum := func(d tree.Datum, t *types.T) { buf.writeBinaryDatum(ctx, d, time.UTC, t) } - convertToVec := func(d tree.Datum, t *types.T) coldata.Vec { - vec := coldata.NewMemColumn(t, 1 /* length */, coldataext.NewExtendedColumnFactory(&evalCtx)) + convertToVec := func(d tree.Datum, t *types.T) *coldata.TypedVecs { + batch := coldata.NewMemBatchWithCapacity([]*types.T{t}, 1 /* capacity */, coldataext.NewExtendedColumnFactory(&evalCtx)) converter := colconv.GetDatumToPhysicalFn(t) - coldata.SetValueAt(vec, converter(d), 0 /* rowIdx */) - return vec + coldata.SetValueAt(batch.ColVec(0), converter(d), 0 /* rowIdx */) + var vecs coldata.TypedVecs + vecs.SetBatch(batch) + return &vecs } writeTextColumnarElement := func(d tree.Datum, t *types.T) { - buf.writeTextColumnarElement(ctx, convertToVec(d, t), 0 /* idx */, conv, loc) + buf.writeTextColumnarElement(ctx, convertToVec(d, t), 0 /* vecIdx */, 0 /* rowIdx */, conv, loc) } writeBinaryColumnarElement := func(d tree.Datum, t *types.T) { - buf.writeBinaryColumnarElement(ctx, convertToVec(d, t), 0 /* idx */, loc) + buf.writeBinaryColumnarElement(ctx, convertToVec(d, t), 0 /* vecIdx */, 0 /* rowIdx */, loc) } t.Run("encode", func(t *testing.T) { for _, test := range tests { diff --git a/pkg/sql/pgwire/types.go b/pkg/sql/pgwire/types.go index f80f585f4334..f9963b7cc106 100644 --- a/pkg/sql/pgwire/types.go +++ b/pkg/sql/pgwire/types.go @@ -262,85 +262,90 @@ func writeTextDatumNotNull( } // getInt64 returns an int64 from vectors of Int family. -func getInt64(vec coldata.Vec, idx int) int64 { - switch vec.Type().Width() { +func getInt64(vecs *coldata.TypedVecs, vecIdx, rowIdx int, typ *types.T) int64 { + colIdx := vecs.ColsMap[vecIdx] + switch typ.Width() { case 16: - return int64(vec.Int16().Get(idx)) + return int64(vecs.Int16Cols[colIdx].Get(rowIdx)) case 32: - return int64(vec.Int32().Get(idx)) + return int64(vecs.Int32Cols[colIdx].Get(rowIdx)) default: - return vec.Int64().Get(idx) + return vecs.Int64Cols[colIdx].Get(rowIdx) } } // writeTextColumnarElement is the same as writeTextDatum where the datum is -// represented in a columnar element (at position idx in vec). +// represented in a columnar element (at position rowIdx in the vector at +// position vecIdx in vecs). func (b *writeBuffer) writeTextColumnarElement( ctx context.Context, - vec coldata.Vec, - idx int, + vecs *coldata.TypedVecs, + vecIdx int, + rowIdx int, conv sessiondatapb.DataConversionConfig, sessionLoc *time.Location, ) { oldDCC := b.textFormatter.SetDataConversionConfig(conv) defer b.textFormatter.SetDataConversionConfig(oldDCC) + typ := vecs.Vecs[vecIdx].Type() if log.V(2) { - log.Infof(ctx, "pgwire writing TEXT columnar element of type: %s", vec.Type()) + log.Infof(ctx, "pgwire writing TEXT columnar element of type: %s", typ) } - if vec.MaybeHasNulls() && vec.Nulls().NullAt(idx) { + if vecs.Nulls[vecIdx].MaybeHasNulls() && vecs.Nulls[vecIdx].NullAt(rowIdx) { // NULL is encoded as -1; all other values have a length prefix. b.putInt32(-1) return } - switch vec.Type().Family() { + colIdx := vecs.ColsMap[vecIdx] + switch typ.Family() { case types.BoolFamily: - writeTextBool(b, vec.Bool().Get(idx)) + writeTextBool(b, vecs.BoolCols[colIdx].Get(rowIdx)) case types.IntFamily: - writeTextInt64(b, getInt64(vec, idx)) + writeTextInt64(b, getInt64(vecs, vecIdx, rowIdx, typ)) case types.FloatFamily: - writeTextFloat64(b, vec.Float64().Get(idx), conv) + writeTextFloat64(b, vecs.Float64Cols[colIdx].Get(rowIdx), conv) case types.DecimalFamily: - d := vec.Decimal().Get(idx) + d := vecs.DecimalCols[colIdx].Get(rowIdx) // The logic here is the simplification of tree.DDecimal.Format given // that we use tree.FmtSimple. b.writeLengthPrefixedString(d.String()) case types.BytesFamily: - writeTextBytes(b, string(vec.Bytes().Get(idx)), conv) + writeTextBytes(b, string(vecs.BytesCols[colIdx].Get(rowIdx)), conv) case types.UuidFamily: - id, err := uuid.FromBytes(vec.Bytes().Get(idx)) + id, err := uuid.FromBytes(vecs.BytesCols[colIdx].Get(rowIdx)) if err != nil { panic(errors.Wrap(err, "unexpectedly couldn't retrieve UUID object")) } writeTextUUID(b, id) case types.StringFamily: - writeTextString(b, string(vec.Bytes().Get(idx)), vec.Type()) + writeTextString(b, string(vecs.BytesCols[colIdx].Get(rowIdx)), typ) case types.DateFamily: - tree.FormatDate(pgdate.MakeCompatibleDateFromDisk(vec.Int64().Get(idx)), b.textFormatter) + tree.FormatDate(pgdate.MakeCompatibleDateFromDisk(vecs.Int64Cols[colIdx].Get(rowIdx)), b.textFormatter) b.writeFromFmtCtx(b.textFormatter) case types.TimestampFamily: - writeTextTimestamp(b, vec.Timestamp().Get(idx)) + writeTextTimestamp(b, vecs.TimestampCols[colIdx].Get(rowIdx)) case types.TimestampTZFamily: - writeTextTimestampTZ(b, vec.Timestamp().Get(idx), sessionLoc) + writeTextTimestampTZ(b, vecs.TimestampCols[colIdx].Get(rowIdx), sessionLoc) case types.IntervalFamily: - tree.FormatDuration(vec.Interval().Get(idx), b.textFormatter) + tree.FormatDuration(vecs.IntervalCols[colIdx].Get(rowIdx), b.textFormatter) b.writeFromFmtCtx(b.textFormatter) case types.JsonFamily: - b.writeLengthPrefixedString(vec.JSON().Get(idx).String()) + b.writeLengthPrefixedString(vecs.JSONCols[colIdx].Get(rowIdx).String()) default: // All other types are represented via the datum-backed vector. - writeTextDatumNotNull(b, vec.Datum().Get(idx).(tree.Datum), conv, sessionLoc, vec.Type()) + writeTextDatumNotNull(b, vecs.DatumCols[colIdx].Get(rowIdx).(tree.Datum), conv, sessionLoc, typ) } } @@ -744,59 +749,62 @@ func writeBinaryDatumNotNull( } // writeBinaryColumnarElement is the same as writeBinaryDatum where the datum is -// represented in a columnar element (at position idx in vec). +// represented in a columnar element (at position rowIdx in the vector at +// position vecIdx in vecs). func (b *writeBuffer) writeBinaryColumnarElement( - ctx context.Context, vec coldata.Vec, idx int, sessionLoc *time.Location, + ctx context.Context, vecs *coldata.TypedVecs, vecIdx int, rowIdx int, sessionLoc *time.Location, ) { + typ := vecs.Vecs[vecIdx].Type() if log.V(2) { - log.Infof(ctx, "pgwire writing BINARY columnar element of type: %s", vec.Type()) + log.Infof(ctx, "pgwire writing BINARY columnar element of type: %s", typ) } - if vec.MaybeHasNulls() && vec.Nulls().NullAt(idx) { + if vecs.Nulls[vecIdx].MaybeHasNulls() && vecs.Nulls[vecIdx].NullAt(rowIdx) { // NULL is encoded as -1; all other values have a length prefix. b.putInt32(-1) return } - switch vec.Type().Family() { + colIdx := vecs.ColsMap[vecIdx] + switch typ.Family() { case types.BoolFamily: - writeBinaryBool(b, vec.Bool().Get(idx)) + writeBinaryBool(b, vecs.BoolCols[colIdx].Get(rowIdx)) case types.IntFamily: - writeBinaryInt(b, getInt64(vec, idx), vec.Type()) + writeBinaryInt(b, getInt64(vecs, vecIdx, rowIdx, typ), typ) case types.FloatFamily: - writeBinaryFloat(b, vec.Float64().Get(idx), vec.Type()) + writeBinaryFloat(b, vecs.Float64Cols[colIdx].Get(rowIdx), typ) case types.DecimalFamily: - v := vec.Decimal().Get(idx) + v := vecs.DecimalCols[colIdx].Get(rowIdx) writeBinaryDecimal(b, &v) case types.BytesFamily: - writeBinaryBytes(b, vec.Bytes().Get(idx)) + writeBinaryBytes(b, vecs.BytesCols[colIdx].Get(rowIdx)) case types.UuidFamily: - writeBinaryBytes(b, vec.Bytes().Get(idx)) + writeBinaryBytes(b, vecs.BytesCols[colIdx].Get(rowIdx)) case types.StringFamily: - writeBinaryString(b, string(vec.Bytes().Get(idx)), vec.Type()) + writeBinaryString(b, string(vecs.BytesCols[colIdx].Get(rowIdx)), typ) case types.TimestampFamily: - writeBinaryTimestamp(b, vec.Timestamp().Get(idx)) + writeBinaryTimestamp(b, vecs.TimestampCols[colIdx].Get(rowIdx)) case types.TimestampTZFamily: - writeBinaryTimestampTZ(b, vec.Timestamp().Get(idx), sessionLoc) + writeBinaryTimestampTZ(b, vecs.TimestampCols[colIdx].Get(rowIdx), sessionLoc) case types.DateFamily: - writeBinaryDate(b, pgdate.MakeCompatibleDateFromDisk(vec.Int64().Get(idx))) + writeBinaryDate(b, pgdate.MakeCompatibleDateFromDisk(vecs.Int64Cols[colIdx].Get(rowIdx))) case types.IntervalFamily: - writeBinaryInterval(b, vec.Interval().Get(idx)) + writeBinaryInterval(b, vecs.IntervalCols[colIdx].Get(rowIdx)) case types.JsonFamily: - writeBinaryJSON(b, vec.JSON().Get(idx)) + writeBinaryJSON(b, vecs.JSONCols[colIdx].Get(rowIdx)) default: // All other types are represented via the datum-backed vector. - writeBinaryDatumNotNull(ctx, b, vec.Datum().Get(idx).(tree.Datum), sessionLoc, vec.Type()) + writeBinaryDatumNotNull(ctx, b, vecs.DatumCols[colIdx].Get(rowIdx).(tree.Datum), sessionLoc, typ) } } diff --git a/pkg/sql/pgwire/types_test.go b/pkg/sql/pgwire/types_test.go index cc650be1afdc..a67046627a80 100644 --- a/pkg/sql/pgwire/types_test.go +++ b/pkg/sql/pgwire/types_test.go @@ -304,17 +304,22 @@ func benchmarkWriteColumnar(b *testing.B, batch coldata.Batch, format pgwirebase buf := newWriteBuffer(nil /* bytecount */) buf.bytecount = metric.NewCounter(metric.Metadata{Name: ""}) + var vecs coldata.TypedVecs writeMethod := func(ctx context.Context, batch coldata.Batch, loc *time.Location) { defaultConv, _ := makeTestingConvCfg() + vecs.SetBatch(batch) + defer vecs.Reset() for rowIdx := 0; rowIdx < batch.Length(); rowIdx++ { - buf.writeTextColumnarElement(ctx, batch.ColVec(0), rowIdx, defaultConv, loc) + buf.writeTextColumnarElement(ctx, &vecs, 0 /* vecIdx */, rowIdx, defaultConv, loc) } } if format == pgwirebase.FormatBinary { writeMethod = func(ctx context.Context, batch coldata.Batch, loc *time.Location) { + vecs.SetBatch(batch) + defer vecs.Reset() for rowIdx := 0; rowIdx < batch.Length(); rowIdx++ { - buf.writeBinaryColumnarElement(ctx, batch.ColVec(0), rowIdx, loc) + buf.writeBinaryColumnarElement(ctx, &vecs, 0 /* vecIdx */, rowIdx, loc) } } } From 81d8abe530c8b82b51358aa55e4774422cd7964c Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Thu, 28 Oct 2021 13:40:37 -0400 Subject: [PATCH 108/205] sql: add compat support for LC_* session vars Release note (sql change): The session variables LC_COLLATE, LC_CTYPE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC, and LC_TIME were added for compatibility with PostgreSQL. They only support the C.UTF-8 locale. --- docs/generated/sql/bnf/stmt_block.bnf | 2 + .../testdata/logic_test/information_schema | 6 ++ .../logictest/testdata/logic_test/pg_catalog | 18 ++++++ pkg/sql/logictest/testdata/logic_test/set | 60 +++++++++++++++++++ .../logictest/testdata/logic_test/show_source | 6 ++ pkg/sql/parser/sql.y | 8 ++- pkg/sql/unsupported_vars.go | 8 +-- pkg/sql/vars.go | 31 ++++++++++ 8 files changed, 132 insertions(+), 7 deletions(-) diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index d6065ff85b72..3f5edfba22f9 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -1561,6 +1561,8 @@ session_var ::= | 'NAMES' | 'ROLE' | 'SESSION_USER' + | 'LC_COLLATE' + | 'LC_CTYPE' | 'TIME' 'ZONE' var_name ::= diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index 9b34ba712481..6dc765c47a9e 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -4656,6 +4656,12 @@ intervalstyle postgres intervalstyle_enabled off is_superuser on large_full_scan_rows 1000 +lc_collate C.UTF-8 +lc_ctype C.UTF-8 +lc_messages C.UTF-8 +lc_monetary C.UTF-8 +lc_numeric C.UTF-8 +lc_time C.UTF-8 locality region=test,dc=dc1 locality_optimized_partitioned_index_scan on lock_timeout 0 diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index b1ba416e62de..b2598cf6839c 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -4038,6 +4038,12 @@ intervalstyle postgres NULL intervalstyle_enabled off NULL NULL NULL string is_superuser on NULL NULL NULL string large_full_scan_rows 1000 NULL NULL NULL string +lc_collate C.UTF-8 NULL NULL NULL string +lc_ctype C.UTF-8 NULL NULL NULL string +lc_messages C.UTF-8 NULL NULL NULL string +lc_monetary C.UTF-8 NULL NULL NULL string +lc_numeric C.UTF-8 NULL NULL NULL string +lc_time C.UTF-8 NULL NULL NULL string locality region=test,dc=dc1 NULL NULL NULL string locality_optimized_partitioned_index_scan on NULL NULL NULL string lock_timeout 0 NULL NULL NULL string @@ -4136,6 +4142,12 @@ intervalstyle postgres NULL intervalstyle_enabled off NULL user NULL off off is_superuser on NULL user NULL on on large_full_scan_rows 1000 NULL user NULL 1000 1000 +lc_collate C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_ctype C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_messages C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_monetary C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_numeric C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_time C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 locality region=test,dc=dc1 NULL user NULL region=test,dc=dc1 region=test,dc=dc1 locality_optimized_partitioned_index_scan on NULL user NULL on on lock_timeout 0 NULL user NULL 0s 0s @@ -4230,6 +4242,12 @@ intervalstyle NULL NULL NULL intervalstyle_enabled NULL NULL NULL NULL NULL is_superuser NULL NULL NULL NULL NULL large_full_scan_rows NULL NULL NULL NULL NULL +lc_collate NULL NULL NULL NULL NULL +lc_ctype NULL NULL NULL NULL NULL +lc_messages NULL NULL NULL NULL NULL +lc_monetary NULL NULL NULL NULL NULL +lc_numeric NULL NULL NULL NULL NULL +lc_time NULL NULL NULL NULL NULL locality NULL NULL NULL NULL NULL locality_optimized_partitioned_index_scan NULL NULL NULL NULL NULL lock_timeout NULL NULL NULL NULL NULL diff --git a/pkg/sql/logictest/testdata/logic_test/set b/pkg/sql/logictest/testdata/logic_test/set index 70717b69af07..5aee32edcca9 100644 --- a/pkg/sql/logictest/testdata/logic_test/set +++ b/pkg/sql/logictest/testdata/logic_test/set @@ -566,3 +566,63 @@ SET disable_plan_gists = 'true' statement ok SET disable_plan_gists = 'false' + +query T +SHOW LC_COLLATE +---- +C.UTF-8 + +query T +SHOW LC_CTYPE +---- +C.UTF-8 + +query T +SHOW LC_MESSAGES +---- +C.UTF-8 + +query T +SHOW LC_MONETARY +---- +C.UTF-8 + +query T +SHOW LC_NUMERIC +---- +C.UTF-8 + +query T +SHOW LC_TIME +---- +C.UTF-8 + +statement error parameter "lc_collate" cannot be changed +SET LC_COLLATE = 'C.UTF-8' + +statement error parameter "lc_ctype" cannot be changed +SET LC_CTYPE = 'C.UTF-8' + +statement ok +SET LC_MESSAGES = 'C.UTF-8' + +statement ok +SET LC_MONETARY = 'C.UTF-8' + +statement ok +SET LC_NUMERIC = 'C.UTF-8' + +statement ok +SET LC_TIME = 'C.UTF-8' + +statement error invalid value for parameter "lc_messages": "en_US.UTF-8" +SET LC_MESSAGES = 'en_US.UTF-8' + +statement error invalid value for parameter "lc_monetary": "en_US.UTF-8" +SET LC_MONETARY = 'en_US.UTF-8' + +statement error invalid value for parameter "lc_numeric": "en_US.UTF-8" +SET LC_NUMERIC = 'en_US.UTF-8' + +statement error invalid value for parameter "lc_time": "en_US.UTF-8" +SET LC_TIME = 'en_US.UTF-8' diff --git a/pkg/sql/logictest/testdata/logic_test/show_source b/pkg/sql/logictest/testdata/logic_test/show_source index 9a88a187bb0f..3af3a22abe54 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_source +++ b/pkg/sql/logictest/testdata/logic_test/show_source @@ -72,6 +72,12 @@ intervalstyle postgres intervalstyle_enabled off is_superuser on large_full_scan_rows 1000 +lc_collate C.UTF-8 +lc_ctype C.UTF-8 +lc_messages C.UTF-8 +lc_monetary C.UTF-8 +lc_numeric C.UTF-8 +lc_time C.UTF-8 locality region=test,dc=dc1 locality_optimized_partitioned_index_scan on lock_timeout 0 diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index f6873872e4ed..e2b14391ab3a 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -4978,9 +4978,9 @@ show_session_stmt: session_var: IDENT -// Although ALL, SESSION_USER and DATABASE are identifiers for the -// purpose of SHOW, they lex as separate token types, so they need -// separate rules. +// Although ALL, SESSION_USER, DATABASE, LC_COLLATE, and LC_CTYPE are +// identifiers for the purpose of SHOW, they lex as separate token types, so +// they need separate rules. | ALL | DATABASE // SET NAMES is standard SQL for SET client_encoding. @@ -4988,6 +4988,8 @@ session_var: | NAMES { $$ = "client_encoding" } | ROLE | SESSION_USER +| LC_COLLATE +| LC_CTYPE // TIME ZONE is special: it is two tokens, but is really the identifier "TIME ZONE". | TIME ZONE { $$ = "timezone" } | TIME error // SHOW HELP: SHOW SESSION diff --git a/pkg/sql/unsupported_vars.go b/pkg/sql/unsupported_vars.go index 3cfaa054d09e..51879d5e6ba2 100644 --- a/pkg/sql/unsupported_vars.go +++ b/pkg/sql/unsupported_vars.go @@ -118,10 +118,10 @@ var UnsupportedVars = func(ss ...string) map[string]struct{} { // "idle_in_transaction_session_timeout", "ignore_checksum_failure", "join_collapse_limit", - "lc_messages", - "lc_monetary", - "lc_numeric", - "lc_time", + // "lc_messages", + // "lc_monetary", + // "lc_numeric", + // "lc_time", "lo_compat_privileges", "local_preload_libraries", // "lock_timeout", diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index 7d797b45e52a..b3cc166c66b0 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -49,6 +49,11 @@ const ( PgServerVersion = "13.0.0" // PgServerVersionNum is the latest version of postgres that we claim to support in the numeric format of "server_version_num". PgServerVersionNum = "130000" + // PgCompatLocale is the locale string we advertise in `LC_*` session + // variables. C.UTF-8 is the only locale that is allowed in CREATE DATABASE + // at the time of writing. + // See https://www.postgresql.org/docs/14/locale.html + PgCompatLocale = "C.UTF-8" ) type getStringValFn = func( @@ -1123,6 +1128,32 @@ var varGen = map[string]sessionVar{ // See https://www.postgresql.org/docs/10/static/runtime-config-preset.html#GUC-SERVER-VERSION-NUM `server_version_num`: makeReadOnlyVar(PgServerVersionNum), + // This is read-only in Postgres also. + // See https://www.postgresql.org/docs/14/sql-show.html and + // https://www.postgresql.org/docs/14/locale.html + `lc_collate`: makeReadOnlyVar(PgCompatLocale), + + // This is read-only in Postgres also. + // See https://www.postgresql.org/docs/14/sql-show.html and + // https://www.postgresql.org/docs/14/locale.html + `lc_ctype`: makeReadOnlyVar(PgCompatLocale), + + // See https://www.postgresql.org/docs/14/sql-show.html and + // https://www.postgresql.org/docs/14/locale.html + `lc_messages`: makeCompatStringVar("lc_messages", PgCompatLocale), + + // See https://www.postgresql.org/docs/14/sql-show.html and + // https://www.postgresql.org/docs/14/locale.html + `lc_monetary`: makeCompatStringVar("lc_monetary", PgCompatLocale), + + // See https://www.postgresql.org/docs/14/sql-show.html and + // https://www.postgresql.org/docs/14/locale.html + `lc_numeric`: makeCompatStringVar("lc_numeric", PgCompatLocale), + + // See https://www.postgresql.org/docs/14/sql-show.html and + // https://www.postgresql.org/docs/14/locale.html + `lc_time`: makeCompatStringVar("lc_time", PgCompatLocale), + // See https://www.postgresql.org/docs/9.4/runtime-config-connection.html `ssl_renegotiation_limit`: { Hidden: true, From b37603815a36a60059afc244ec431ab1398ce9cd Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Sun, 24 Oct 2021 02:29:23 -0400 Subject: [PATCH 109/205] sql/sem/tree: support parsing of tuples from string literals Release note (sql change): String literals can now be parsed as tuples, either in a cast expression, or in other contexts like function arguments. --- pkg/cli/clisqlexec/format_value.go | 2 +- pkg/sql/logictest/testdata/logic_test/record | 22 ++ pkg/sql/randgen/type.go | 11 +- pkg/sql/sem/tree/BUILD.bazel | 4 + pkg/sql/sem/tree/constant.go | 2 + pkg/sql/sem/tree/parse_array_test.go | 42 +++ pkg/sql/sem/tree/parse_string.go | 4 +- pkg/sql/sem/tree/parse_tuple.go | 243 +++++++++++++ pkg/sql/sem/tree/parse_tuple_test.go | 344 +++++++++++++++++++ pkg/sql/types/types.go | 6 + 10 files changed, 677 insertions(+), 3 deletions(-) create mode 100644 pkg/sql/sem/tree/parse_tuple.go create mode 100644 pkg/sql/sem/tree/parse_tuple_test.go diff --git a/pkg/cli/clisqlexec/format_value.go b/pkg/cli/clisqlexec/format_value.go index 28607670872f..783103a55224 100644 --- a/pkg/cli/clisqlexec/format_value.go +++ b/pkg/cli/clisqlexec/format_value.go @@ -151,7 +151,7 @@ func formatArray( intArray := []int64{} backingArray = &intArray parsingArray = (*pq.Int64Array)(&intArray) - case "TEXT", "VARCHAR", "NAME", "CHAR", "BPCHAR": + case "TEXT", "VARCHAR", "NAME", "CHAR", "BPCHAR", "RECORD": stringArray := []string{} backingArray = &stringArray parsingArray = (*pq.StringArray)(&stringArray) diff --git a/pkg/sql/logictest/testdata/logic_test/record b/pkg/sql/logictest/testdata/logic_test/record index eebae774f655..802727bba6b1 100644 --- a/pkg/sql/logictest/testdata/logic_test/record +++ b/pkg/sql/logictest/testdata/logic_test/record @@ -148,3 +148,25 @@ CREATE VIEW v AS SELECT (1,'a')::b statement error cannot modify table record type CREATE VIEW v AS SELECT ((1,'a')::b).a + +# Test parsing of record types from string literals. + +query T +SELECT COALESCE(ARRAY[ROW(1, 2)], '{}') +---- +{"(1,2)"} + +query T +SELECT COALESCE(NULL, '{}'::record[]); +---- +{} + +query T +SELECT '{"(1, 3)", "(1, 2)"}'::a[] +---- +{"(1,\" 3\")","(1,\" 2\")"} + +query T +SELECT COALESCE(NULL::a[], '{"(1, 3)", "(1, 2)"}'); +---- +{"(1,\" 3\")","(1,\" 2\")"} diff --git a/pkg/sql/randgen/type.go b/pkg/sql/randgen/type.go index e9eac9a94da7..dc68f35b1289 100644 --- a/pkg/sql/randgen/type.go +++ b/pkg/sql/randgen/type.go @@ -111,12 +111,21 @@ func RandTypeFromSlice(rng *rand.Rand, typs []*types.T) *types.T { } return types.MakeArray(inner) } + if typ.ArrayContents().Family() == types.TupleFamily { + // Generate tuples between 0 and 4 datums in length + len := rng.Intn(5) + contents := make([]*types.T, len) + for i := range contents { + contents[i] = RandTypeFromSlice(rng, typs) + } + return types.MakeArray(types.MakeTuple(contents)) + } case types.TupleFamily: // Generate tuples between 0 and 4 datums in length len := rng.Intn(5) contents := make([]*types.T, len) for i := range contents { - contents[i] = RandType(rng) + contents[i] = RandTypeFromSlice(rng, typs) } return types.MakeTuple(contents) } diff --git a/pkg/sql/sem/tree/BUILD.bazel b/pkg/sql/sem/tree/BUILD.bazel index f330b068c4f2..4ca70455e454 100644 --- a/pkg/sql/sem/tree/BUILD.bazel +++ b/pkg/sql/sem/tree/BUILD.bazel @@ -67,6 +67,7 @@ go_library( "overload.go", "parse_array.go", "parse_string.go", # keep + "parse_tuple.go", "persistence.go", "pgwire_encode.go", "placeholders.go", @@ -203,6 +204,7 @@ go_test( "operators_test.go", "overload_test.go", "parse_array_test.go", + "parse_tuple_test.go", "placeholders_test.go", "pretty_test.go", "role_spec_test.go", @@ -242,6 +244,7 @@ go_test( "//pkg/testutils/sqlutils", "//pkg/util/duration", "//pkg/util/hlc", + "//pkg/util/json", "//pkg/util/leaktest", "//pkg/util/log", "//pkg/util/pretty", @@ -250,6 +253,7 @@ go_test( "//pkg/util/timeofday", "//pkg/util/timetz", "//pkg/util/timeutil", + "//pkg/util/timeutil/pgdate", "@com_github_cockroachdb_apd_v2//:apd", "@com_github_cockroachdb_datadriven//:datadriven", "@com_github_lib_pq//oid", diff --git a/pkg/sql/sem/tree/constant.go b/pkg/sql/sem/tree/constant.go index 724961b27c22..87cf2d64a6b8 100644 --- a/pkg/sql/sem/tree/constant.go +++ b/pkg/sql/sem/tree/constant.go @@ -495,6 +495,8 @@ var ( types.AnyEnumArray, types.INetArray, types.VarBitArray, + types.AnyTuple, + types.AnyTupleArray, } // StrValAvailBytes is the set of types convertible to byte array. StrValAvailBytes = []*types.T{types.Bytes, types.Uuid, types.String, types.AnyEnum} diff --git a/pkg/sql/sem/tree/parse_array_test.go b/pkg/sql/sem/tree/parse_array_test.go index eb4fe227a0db..00f2d7f7c3f4 100644 --- a/pkg/sql/sem/tree/parse_array_test.go +++ b/pkg/sql/sem/tree/parse_array_test.go @@ -21,6 +21,9 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/log" ) +var tupleOfTwoInts = types.MakeTuple([]*types.T{types.Int, types.Int}) +var tupleOfStringAndInt = types.MakeTuple([]*types.T{types.String, types.Int}) + func TestParseArray(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -80,6 +83,39 @@ lo}`, types.String, Datums{NewDString(`hel`), NewDString(`lo`)}}, // occur. {string([]byte{'{', 'a', 200, '}'}), types.String, Datums{NewDString("a\xc8")}}, {string([]byte{'{', 'a', 200, 'a', '}'}), types.String, Datums{NewDString("a\xc8a")}}, + + // Arrays of tuples can also be parsed from string literals. + { + `{"(3,4)"}`, + tupleOfTwoInts, + Datums{NewDTuple(tupleOfTwoInts, NewDInt(3), NewDInt(4))}, + }, + { + `{"(3,4)", null}`, + tupleOfTwoInts, + Datums{NewDTuple(tupleOfTwoInts, NewDInt(3), NewDInt(4)), DNull}, + }, + { + `{"(a12',4)"}`, + tupleOfStringAndInt, + Datums{NewDTuple(tupleOfStringAndInt, NewDString("a12'"), NewDInt(4))}, + }, + { + `{"(cat,4)", "(null,0)"}`, + tupleOfStringAndInt, + Datums{ + NewDTuple(tupleOfStringAndInt, NewDString("cat"), NewDInt(4)), + NewDTuple(tupleOfStringAndInt, NewDString("null"), NewDInt(0)), + }, + }, + { + `{"(1,2)", "(3,)"}`, + tupleOfTwoInts, + Datums{ + NewDTuple(tupleOfTwoInts, NewDInt(1), NewDInt(2)), + NewDTuple(tupleOfTwoInts, NewDInt(3), DNull), + }, + }, } for _, td := range testData { t.Run(td.str, func(t *testing.T) { @@ -182,6 +218,12 @@ func TestParseArrayError(t *testing.T) { {string([]byte{200}), types.String, `could not parse "\xc8" as type string[]: array must be enclosed in { and }`}, {string([]byte{'{', 'a', 200}), types.String, `could not parse "{a\xc8" as type string[]: malformed array`}, + + {`{"(1,2)", "(3,4,5)"}`, tupleOfTwoInts, `could not parse "{\"(1,2)\", \"(3,4,5)\"}" as type tuple{int, int}[]: could not parse "(3,4,5)" as type tuple{int, int}: malformed record literal`}, + {`{"(1,2)", "(3,4,)"}`, tupleOfTwoInts, `could not parse "{\"(1,2)\", \"(3,4,)\"}" as type tuple{int, int}[]: could not parse "(3,4,)" as type tuple{int, int}: malformed record literal`}, + {`{"(1,2)", "(3)"}`, tupleOfTwoInts, `could not parse "{\"(1,2)\", \"(3)\"}" as type tuple{int, int}[]: could not parse "(3)" as type tuple{int, int}: malformed record literal`}, + {`{"(1,2)", "(3,4"}`, tupleOfTwoInts, `could not parse "{\"(1,2)\", \"(3,4\"}" as type tuple{int, int}[]: could not parse "(3,4" as type tuple{int, int}: malformed record literal`}, + {`{"(1,2)", "(3,,4)"}`, tupleOfTwoInts, `could not parse "{\"(1,2)\", \"(3,,4)\"}" as type tuple{int, int}[]: could not parse "(3,,4)" as type tuple{int, int}: malformed record literal`}, } for _, td := range testData { t.Run(td.str, func(t *testing.T) { diff --git a/pkg/sql/sem/tree/parse_string.go b/pkg/sql/sem/tree/parse_string.go index e0de051a2c8f..4f6c04d1dc1b 100644 --- a/pkg/sql/sem/tree/parse_string.go +++ b/pkg/sql/sem/tree/parse_string.go @@ -36,7 +36,7 @@ func ParseAndRequireString( } d = formatBitArrayToType(r, t) case types.BoolFamily: - d, err = ParseDBool(s) + d, err = ParseDBool(strings.TrimSpace(s)) case types.BytesFamily: d, err = ParseDByte(s) case types.DateFamily: @@ -89,6 +89,8 @@ func ParseAndRequireString( d, err = ParseDUuidFromString(s) case types.EnumFamily: d, err = MakeDEnumFromLogicalRepresentation(t, s) + case types.TupleFamily: + d, dependsOnContext, err = ParseDTupleFromString(ctx, s, t) default: return nil, false, errors.AssertionFailedf("unknown type %s (%T)", t, t) } diff --git a/pkg/sql/sem/tree/parse_tuple.go b/pkg/sql/sem/tree/parse_tuple.go new file mode 100644 index 000000000000..2fe1dcaca78c --- /dev/null +++ b/pkg/sql/sem/tree/parse_tuple.go @@ -0,0 +1,243 @@ +// Copyright 2016 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package tree + +import ( + "bytes" + "unicode" + "unicode/utf8" + + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/errors" +) + +var enclosingRecordError = pgerror.Newf(pgcode.InvalidTextRepresentation, "record must be enclosed in ( and )") +var extraTextRecordError = pgerror.Newf(pgcode.InvalidTextRepresentation, "extra text after closing right paren") +var malformedRecordError = pgerror.Newf(pgcode.InvalidTextRepresentation, "malformed record literal") +var unsupportedRecordError = pgerror.Newf(pgcode.FeatureNotSupported, "cannot parse anonymous record type") + +var isTupleControlChar = func(ch byte) bool { + return ch == '(' || ch == ')' || ch == ',' +} + +var isTupleElementChar = func(r rune) bool { + return r != '(' && r != ')' && r != ',' +} + +// gobbleString advances the parser for the remainder of the current string +// until it sees a non-escaped termination character, as specified by +// isTerminatingChar, returning the resulting string, not including the +// termination character. +func (p *tupleParseState) gobbleString() (out string, err error) { + isTerminatingChar := func(inQuote bool, ch byte) bool { + if inQuote { + return isQuoteChar(ch) + } + return isTupleControlChar(ch) + } + var result bytes.Buffer + start := 0 + i := 0 + inQuote := false + for i < len(p.s) && (!isTerminatingChar(inQuote, p.s[i]) || inQuote) { + // In these strings, we just encode directly the character following a + // '\', even if it would normally be an escape sequence. + if i < len(p.s) && p.s[i] == '\\' { + result.WriteString(p.s[start:i]) + i++ + if i < len(p.s) { + result.WriteByte(p.s[i]) + i++ + } + start = i + } else if i < len(p.s) && p.s[i] == '"' { + result.WriteString(p.s[start:i]) + i++ + if inQuote && i < len(p.s) && p.s[i] == '"' { + // If we are inQuote and the following character is also a double quote, + // then the two characters are treated as an escape sequence for one + // double quote. + result.WriteByte(p.s[i]) + i++ + } else { + // Otherwise, to match Postgres, double quotes are allowed in the middle + // of an unquoted string, but they are just ignored, even though the + // quotes do need to be balanced! + inQuote = !inQuote + } + + start = i + } else { + i++ + } + } + if i >= len(p.s) { + return "", malformedRecordError + } + if inQuote { + return "", malformedRecordError + } + result.WriteString(p.s[start:i]) + p.s = p.s[i:] + return result.String(), nil +} + +type tupleParseState struct { + s string + tupleIdx int + ctx ParseTimeContext + dependsOnContext bool + result *DTuple + t *types.T +} + +func (p *tupleParseState) advance() { + _, l := utf8.DecodeRuneInString(p.s) + p.s = p.s[l:] +} + +func (p *tupleParseState) eatWhitespace() { + for unicode.IsSpace(p.peek()) { + p.advance() + } +} + +func (p *tupleParseState) peek() rune { + r, _ := utf8.DecodeRuneInString(p.s) + return r +} + +func (p *tupleParseState) eof() bool { + return len(p.s) == 0 +} + +func (p *tupleParseState) parseString() (string, error) { + out, err := p.gobbleString() + if err != nil { + return "", err + } + // Unlike arrays, we don't trim whitespace here. + return out, nil +} + +func (p *tupleParseState) parseElement() error { + if p.tupleIdx >= len(p.t.TupleContents()) { + return errors.WithDetail(malformedRecordError, "Too many columns.") + } + var next string + var err error + r := p.peek() + switch r { + case ')', ',': + // NULLs are represented by an unquoted empty string. + p.result.D[p.tupleIdx] = DNull + p.tupleIdx++ + return nil + default: + if !isTupleElementChar(r) { + return malformedRecordError + } + next, err = p.parseString() + if err != nil { + return err + } + } + + d, dependsOnContext, err := ParseAndRequireString( + p.t.TupleContents()[p.tupleIdx], + next, + p.ctx, + ) + if err != nil { + return err + } + if dependsOnContext { + p.dependsOnContext = true + } + p.result.D[p.tupleIdx] = d + p.tupleIdx++ + return nil +} + +// ParseDTupleFromString parses the string-form of constructing tuples, handling +// cases such as `'(1,2,3)'::record`. The input type t is the type of the +// tuple to parse. +// +// The dependsOnContext return value indicates if we had to consult the +// ParseTimeContext (either for the time or the local timezone). +func ParseDTupleFromString( + ctx ParseTimeContext, s string, t *types.T, +) (_ *DTuple, dependsOnContext bool, _ error) { + ret, dependsOnContext, err := doParseDTupleFromString(ctx, s, t) + if err != nil { + return ret, false, MakeParseError(s, t, err) + } + return ret, dependsOnContext, nil +} + +// doParseDTupleFromString does most of the work of ParseDTupleFromString, +// except the error it returns isn't prettified as a parsing error. +// +// The dependsOnContext return value indicates if we had to consult the +// ParseTimeContext (either for the time or the local timezone). +func doParseDTupleFromString( + ctx ParseTimeContext, s string, t *types.T, +) (_ *DTuple, dependsOnContext bool, _ error) { + if t.TupleContents() == nil { + return nil, false, errors.AssertionFailedf("not a tuple type %s (%T)", t, t) + } + if t == types.AnyTuple { + return nil, false, unsupportedRecordError + } + parser := tupleParseState{ + s: s, + ctx: ctx, + result: NewDTupleWithLen(t, len(t.TupleContents())), + t: t, + } + + parser.eatWhitespace() + if parser.peek() != '(' { + return nil, false, enclosingRecordError + } + parser.advance() + if parser.peek() != ')' || len(t.TupleContents()) > 0 { + if err := parser.parseElement(); err != nil { + return nil, false, err + } + parser.eatWhitespace() + for parser.peek() == ',' { + parser.advance() + if err := parser.parseElement(); err != nil { + return nil, false, err + } + } + } + parser.eatWhitespace() + if parser.eof() { + return nil, false, enclosingRecordError + } + if parser.peek() != ')' { + return nil, false, malformedRecordError + } + if parser.tupleIdx < len(parser.t.TupleContents()) { + return nil, false, errors.WithDetail(malformedRecordError, "Too few columns.") + } + parser.advance() + parser.eatWhitespace() + if !parser.eof() { + return nil, false, extraTextRecordError + } + + return parser.result, parser.dependsOnContext, nil +} diff --git a/pkg/sql/sem/tree/parse_tuple_test.go b/pkg/sql/sem/tree/parse_tuple_test.go new file mode 100644 index 000000000000..be4ecca3b820 --- /dev/null +++ b/pkg/sql/sem/tree/parse_tuple_test.go @@ -0,0 +1,344 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package tree_test + +import ( + "bytes" + "fmt" + "strings" + "testing" + "time" + + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql/randgen" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/types" + jsonb "github.com/cockroachdb/cockroach/pkg/util/json" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/randutil" + "github.com/cockroachdb/cockroach/pkg/util/timetz" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/cockroach/pkg/util/timeutil/pgdate" + "github.com/stretchr/testify/require" +) + +const randomTupleIterations = 1000 +const randomTupleMaxLength = 10 +const randomTupleStringMaxLength = 1000 + +var emptyTuple = types.MakeTuple([]*types.T{}) +var tupleOfStringAndInt = types.MakeTuple([]*types.T{types.String, types.Int}) +var tupleOfOneInt = types.MakeTuple([]*types.T{types.Int}) +var tupleOfTwoInts = types.MakeTuple([]*types.T{types.Int, types.Int}) +var tupleOfTwoStrings = types.MakeTuple([]*types.T{types.String, types.String}) +var tupleOfTuples = types.MakeTuple([]*types.T{tupleOfTwoInts, tupleOfTwoStrings}) +var tupleComplex = types.MakeTuple([]*types.T{types.String, types.TimeTZ, types.Jsonb}) + +func TestParseTuple(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + timeTZString := "15:43:50.707903-02:31:00" + timeTZ, _, err := timetz.ParseTimeTZ(timeutil.Now(), pgdate.DefaultDateStyle(), timeTZString, time.Microsecond) + require.NoError(t, err) + + escapedJsonString := `{"")N}."": [false], ""UA%t"": [""foobar""]}` + jsonVal, err := jsonb.ParseJSON(`{")N}.": [false], "UA%t": ["foobar"]}`) + require.NoError(t, err) + + testData := []struct { + str string + typ *types.T + expected *tree.DTuple + }{ + { + `(3,4)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.NewDString("3"), tree.NewDInt(4)), + }, + {`(1, 2)`, tupleOfTwoInts, tree.NewDTuple(tupleOfStringAndInt, tree.NewDInt(1), tree.NewDInt(2))}, + {`( 1,2)`, tupleOfTwoInts, tree.NewDTuple(tupleOfStringAndInt, tree.NewDInt(1), tree.NewDInt(2))}, + {`(1 ,2)`, tupleOfTwoInts, tree.NewDTuple(tupleOfStringAndInt, tree.NewDInt(1), tree.NewDInt(2))}, + {`(1,2 )`, tupleOfTwoInts, tree.NewDTuple(tupleOfStringAndInt, tree.NewDInt(1), tree.NewDInt(2))}, + {`(null,)`, tupleOfStringAndInt, tree.NewDTuple(tupleOfStringAndInt, tree.NewDString("null"), tree.DNull)}, + {`()`, emptyTuple, tree.NewDTuple(emptyTuple)}, + {`()`, tupleOfOneInt, tree.NewDTuple(tupleOfOneInt, tree.DNull)}, + { + `(3,)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.NewDString("3"), tree.DNull), + }, + { + `(,2)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.DNull, tree.NewDInt(2)), + }, + { + `(,)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.DNull, tree.DNull), + }, + { + `( cat , dog )`, + tupleOfTwoStrings, + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString(" cat "), tree.NewDString(" dog ")), + }, + { + `( cat , "dog " )`, + tupleOfTwoStrings, + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString(" cat "), tree.NewDString(" dog ")), + }, + { + `( cat , " "dog " " )`, + tupleOfTwoStrings, + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString(" cat "), tree.NewDString(" dog ")), + }, + { + `( " cat " , "d"o"g" )`, + tupleOfTwoStrings, + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString(" cat "), tree.NewDString(" dog ")), + }, + { + `( cat \" ," dog \" " )`, + tupleOfTwoStrings, + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString(" cat \" "), tree.NewDString(" dog \" ")), + }, + { + `( cat "" ," dog "" " )`, + tupleOfTwoStrings, + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString(" cat "), tree.NewDString(" dog \" ")), + }, + { + `( 3 ,)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.NewDString(" 3 "), tree.DNull), + }, + { + `("(1,2)",)`, + tupleOfTuples, + tree.NewDTuple( + tupleOfTuples, + tree.NewDTuple(tupleOfTwoInts, tree.NewDInt(1), tree.NewDInt(2)), + tree.DNull, + ), + }, + { + `("(1,2)","(3,4)")`, + tupleOfTuples, + tree.NewDTuple( + tupleOfTuples, + tree.NewDTuple(tupleOfTwoInts, tree.NewDInt(1), tree.NewDInt(2)), + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString("3"), tree.NewDString("4")), + ), + }, + { + `("(1,2) ","(3,4)")`, + tupleOfTuples, + tree.NewDTuple( + tupleOfTuples, + tree.NewDTuple(tupleOfTwoInts, tree.NewDInt(1), tree.NewDInt(2)), + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString("3"), tree.NewDString("4")), + ), + }, + { + `("(1,2)", "(3,4)")`, + tupleOfTuples, + tree.NewDTuple( + tupleOfTuples, + tree.NewDTuple(tupleOfTwoInts, tree.NewDInt(1), tree.NewDInt(2)), + tree.NewDTuple(tupleOfTwoStrings, tree.NewDString("3"), tree.NewDString("4")), + ), + }, + { + `("(1,2)", " (,) ")`, + tupleOfTuples, + tree.NewDTuple( + tupleOfTuples, + tree.NewDTuple(tupleOfTwoInts, tree.NewDInt(1), tree.NewDInt(2)), + tree.NewDTuple(tupleOfTwoStrings, tree.DNull, tree.DNull), + ), + }, + { + `("\n",4)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.NewDString("n"), tree.NewDInt(4)), + }, + { + `("\\n",4)`, + tupleOfStringAndInt, + tree.NewDTuple(tupleOfStringAndInt, tree.NewDString("\\n"), tree.NewDInt(4)), + }, + { + fmt.Sprintf(`(0s{mlRk#, %s, "%s")`, timeTZString, escapedJsonString), + tupleComplex, + tree.NewDTuple( + tupleComplex, + tree.NewDString("0s{mlRk#"), + tree.NewDTimeTZ(timeTZ), + tree.NewDJSON(jsonVal), + ), + }, + } + for _, td := range testData { + t.Run(td.str, func(t *testing.T) { + evalContext := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) + actual, _, err := tree.ParseDTupleFromString(evalContext, td.str, td.typ) + if err != nil { + t.Fatalf("tuple %s: got error %s, expected %s", td.str, err.Error(), td.expected) + } + if actual.Compare(evalContext, td.expected) != 0 { + t.Fatalf("tuple %s: got %s, expected %s", td.str, actual, td.expected) + } + }) + } +} + +func TestParseTupleRandomStrings(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + rng, _ := randutil.NewTestRand() + for i := 0; i < randomTupleIterations; i++ { + numElems := rng.Intn(randomTupleMaxLength) + tup := make([][]byte, numElems) + tupContents := []*types.T{} + for tupIdx := range tup { + len := rng.Intn(randomTupleStringMaxLength) + str := make([]byte, len) + for strIdx := 0; strIdx < len; strIdx++ { + str[strIdx] = byte(rng.Intn(256)) + } + tup[tupIdx] = str + tupContents = append(tupContents, types.String) + } + + var buf bytes.Buffer + buf.WriteByte('(') + for j, b := range tup { + if j > 0 { + buf.WriteByte(',') + } + buf.WriteByte('"') + // The input format for this doesn't support regular escaping, any + // character can be preceded by a backslash to encode it directly (this + // means that there's no printable way to encode non-printing characters, + // users must use `e` prefixed strings). + for _, c := range b { + if c == '"' || c == '\\' || rng.Intn(10) == 0 { + buf.WriteByte('\\') + } + buf.WriteByte(c) + } + buf.WriteByte('"') + } + buf.WriteByte(')') + + parsed, _, err := tree.ParseDTupleFromString( + tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()), buf.String(), types.MakeTuple(tupContents)) + if err != nil { + t.Fatalf(`got error: "%s" for elem "%s"`, err, buf.String()) + } + for tupIdx := range tup { + value := tree.MustBeDString(parsed.D[tupIdx]) + if string(value) != string(tup[tupIdx]) { + t.Fatalf(`iteration %d: tuple "%s", got %#v, expected %#v`, i, buf.String(), value, string(tup[tupIdx])) + } + } + } +} + +func TestParseTupleRandomDatums(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + rng, _ := randutil.NewTestRand() + for i := 0; i < randomTupleIterations; i++ { + numElems := 1 + rng.Intn(randomTupleMaxLength) + tupContents := []*types.T{} + for j := 0; j < numElems; j++ { + tupContents = append(tupContents, randgen.RandTypeFromSlice(rng, tree.StrValAvailAllParsable)) + } + // The inner tuple contents can be NULL, but the tuple itself cannot be, + // since we can't parse a null tuple from a string. + tup := randgen.RandDatum(rng, types.MakeTuple(tupContents), true /* nullOk */) + if tup == tree.DNull { + continue + } + tupString := tree.AsStringWithFlags(tup, tree.FmtPgwireText) + + evalCtx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) + parsed, _, err := tree.ParseDTupleFromString( + evalCtx, tupString, types.MakeTuple(tupContents)) + if err != nil { + t.Fatalf( + "got error: %s\n tuple: %s\n tupleString: %s\n types: %s", + err, + tree.MustBeDTuple(tup).D, + tupString, + tupContents, + ) + } + if tup.Compare(evalCtx, parsed) != 0 { + t.Fatalf(`iteration %d: tuple "%s", got %#v, expected %#v`, i, tupString, parsed, tup) + } + } +} + +func TestParseTupleError(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + testData := []struct { + str string + typ *types.T + expectedError string + }{ + {``, tupleOfStringAndInt, `record must be enclosed in ( and )`}, + {`1`, tupleOfStringAndInt, `record must be enclosed in ( and )`}, + {`1,2`, tupleOfTwoInts, `record must be enclosed in ( and )`}, + {`(1,2`, tupleOfTwoInts, `malformed record literal`}, + {`(1,2,`, tupleOfTwoInts, `malformed record literal`}, + {`(`, tupleOfTwoInts, `malformed record literal`}, + {`()()`, tupleOfTwoInts, `malformed record literal`}, + {`() ()`, tupleOfTwoInts, `malformed record literal`}, + {`((1,2), (,))`, tupleOfTuples, `malformed record literal`}, + {`(, (2,3))`, tupleOfTuples, `record must be enclosed in ( and )`}, + {`(1,hello)`, tupleOfTwoInts, `strconv.ParseInt: parsing "hello": invalid syntax`}, + {`(1,"2)`, tupleOfTwoInts, `malformed record literal`}, + {`(hello,he"lo)`, tupleOfTwoStrings, `malformed record literal`}, + { + `( cat , "dog )`, + tupleOfTwoStrings, + // Quotes to be balanced. + `malformed record literal`, + }, + {string([]byte{200}), tupleOfTwoInts, `could not parse "\xc8" as type tuple{int, int}: record must be enclosed in ( and )`}, + {string([]byte{'(', 'a', 200}), tupleOfTwoStrings, `malformed record literal`}, + + {`(3,4,5)`, tupleOfTwoInts, `malformed record literal`}, + {`(3,4,)`, tupleOfTwoInts, `malformed record literal`}, + {`(3)`, tupleOfTwoInts, `malformed record literal`}, + {`(3,4`, tupleOfTwoInts, `malformed record literal`}, + {`(3,null)`, tupleOfTwoInts, `strconv.ParseInt: parsing "null": invalid syntax`}, + {`(3,,4)`, tupleOfTwoInts, `malformed record literal`}, + } + + for _, td := range testData { + t.Run(td.str, func(t *testing.T) { + _, _, err := tree.ParseDTupleFromString( + tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()), td.str, td.typ) + if err == nil { + t.Fatalf("expected %#v to error with message %#v", td.str, td.expectedError) + } + if !strings.HasSuffix(err.Error(), td.expectedError) { + t.Fatalf("tuple %s: got error %s, expected suffix %s", td.str, err.Error(), td.expectedError) + } + }) + } +} diff --git a/pkg/sql/types/types.go b/pkg/sql/types/types.go index dc34bf92c9bd..501f15255861 100644 --- a/pkg/sql/types/types.go +++ b/pkg/sql/types/types.go @@ -508,6 +508,12 @@ var ( AnyTuple = &T{InternalType: InternalType{ Family: TupleFamily, TupleContents: []*T{Any}, Oid: oid.T_record, Locale: &emptyLocale}} + // AnyTupleArray is a special type used only during static analysis as a wildcard + // type that matches an array of tuples with any number of fields of any type (including + // tuple types). Execution-time values should never have this type. + AnyTupleArray = &T{InternalType: InternalType{ + Family: ArrayFamily, ArrayContents: AnyTuple, Oid: oid.T__record, Locale: &emptyLocale}} + // AnyCollatedString is a special type used only during static analysis as a // wildcard type that matches a collated string with any locale. Execution- // time values should never have this type. From a48d5ab2ea0fb68071098b9b5c265fb8ebc90cc2 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Mon, 25 Oct 2021 12:12:39 -0400 Subject: [PATCH 110/205] pgwire/cli: format records and enums correctly Release note (bug fix): Previously, when records and enums containing escape sequences were shown in the CLI, they would be incorrectly double-escaped. This is now fixed. --- pkg/cli/clisqlexec/format_value.go | 3 ++- pkg/cli/clisqlexec/format_value_test.go | 19 +++++++++++++++++++ pkg/sql/sem/tree/datum.go | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/cli/clisqlexec/format_value.go b/pkg/cli/clisqlexec/format_value.go index 783103a55224..6697d8b48ad8 100644 --- a/pkg/cli/clisqlexec/format_value.go +++ b/pkg/cli/clisqlexec/format_value.go @@ -45,7 +45,8 @@ func FormatVal( return formatArray(b, colType[1:], showPrintableUnicode, showNewLinesAndTabs) } - if colType == "NAME" { + // Names, records, and user-defined types should all be displayed as strings. + if colType == "NAME" || colType == "RECORD" || colType == "" { val = string(b) colType = "VARCHAR" } diff --git a/pkg/cli/clisqlexec/format_value_test.go b/pkg/cli/clisqlexec/format_value_test.go index 9b1046a924b1..5c93acef4561 100644 --- a/pkg/cli/clisqlexec/format_value_test.go +++ b/pkg/cli/clisqlexec/format_value_test.go @@ -32,6 +32,12 @@ func Example_sql_format() { // Sanity check for other array types. c.RunWithArgs([]string{"sql", "-e", "select array[true, false], array['01:01'::time], array['2021-03-20'::date]"}) c.RunWithArgs([]string{"sql", "-e", "select array[123::int2], array[123::int4], array[123::int8]"}) + // Check that tuples and user-defined types are escaped correctly. + c.RunWithArgs([]string{"sql", "-e", `create table tup (a text, b int8)`}) + c.RunWithArgs([]string{"sql", "-e", `select row('\n',1), row('\\n',2), '("\n",3)'::tup, '("\\n",4)'::tup`}) + c.RunWithArgs([]string{"sql", "-e", `select '{"(n,1)","(\n,2)","(\\n,3)"}'::tup[]`}) + c.RunWithArgs([]string{"sql", "-e", `create type e as enum('a', '\n')`}) + c.RunWithArgs([]string{"sql", "-e", `select '\n'::e, '{a, "\\n"}'::e[]`}) // Output: // sql -e create database t; create table t.times (bare timestamp, withtz timestamptz) @@ -79,4 +85,17 @@ func Example_sql_format() { // sql -e select array[123::int2], array[123::int4], array[123::int8] // array array array // {123} {123} {123} + // sql -e create table tup (a text, b int8) + // CREATE TABLE + // sql -e select row('\n',1), row('\\n',2), '("\n",3)'::tup, '("\\n",4)'::tup + // row row tup tup + // "(""\\n"",1)" "(""\\\\n"",2)" (n,3) "(""\\n"",4)" + // sql -e select '{"(n,1)","(\n,2)","(\\n,3)"}'::tup[] + // tup + // "{""(n,1)"",""(n,2)"",""(n,3)""}" + // sql -e create type e as enum('a', '\n') + // CREATE TYPE + // sql -e select '\n'::e, '{a, "\\n"}'::e[] + // e e + // \n "{a,""\\n""}" } diff --git a/pkg/sql/sem/tree/datum.go b/pkg/sql/sem/tree/datum.go index 474836473136..5c82d6cc0d72 100644 --- a/pkg/sql/sem/tree/datum.go +++ b/pkg/sql/sem/tree/datum.go @@ -4459,6 +4459,8 @@ func (d *DEnum) Format(ctx *FmtCtx) { ctx.WithFlags(ctx.flags|fmtFormatByteLiterals, func() { s.Format(ctx) }) + } else if ctx.HasFlags(FmtPgwireText) { + ctx.WriteString(d.LogicalRep) } else { s := DString(d.LogicalRep) s.Format(ctx) From 031fda79c04c053083c8b191972cf9cbaa84f1e9 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Wed, 27 Oct 2021 16:30:54 +0000 Subject: [PATCH 111/205] kvserver: batch intents in `MVCCIterator.CheckForKeyCollisions` `MVCCIterator.CheckForKeyCollisions()` is used by `AddSSTable` to check for key collisions when `DisallowShadowing` is set. If it encounters any intents, it returns `WriteIntentError` to resolve these before retrying. However, this returned an error for each individual intent, which has quadratic performance. This patch changes it to instead collect and return a batch of intents, for more efficient intent resolution. The batch size is controlled by the existing setting `storage.mvcc.max_intents_per_error`, which defaults to 5000. Release note (performance improvement): Improved `IMPORT INTO` performance in cases where it encounters large numbers of unresolved write intents. --- pkg/kv/kvserver/batcheval/cmd_add_sstable.go | 8 +- .../batcheval/cmd_add_sstable_test.go | 39 ++++++++-- pkg/kv/kvserver/spanset/batch.go | 4 +- pkg/storage/engine.go | 10 ++- pkg/storage/engine_test.go | 78 +++++++++++++++++++ pkg/storage/intent_interleaving_iter.go | 4 +- pkg/storage/mvcc.go | 29 +++---- pkg/storage/pebble_iterator.go | 4 +- 8 files changed, 143 insertions(+), 33 deletions(-) diff --git a/pkg/kv/kvserver/batcheval/cmd_add_sstable.go b/pkg/kv/kvserver/batcheval/cmd_add_sstable.go index d4af1e6ea930..78f7c8ff6edd 100644 --- a/pkg/kv/kvserver/batcheval/cmd_add_sstable.go +++ b/pkg/kv/kvserver/batcheval/cmd_add_sstable.go @@ -121,7 +121,10 @@ func EvalAddSSTable( var skippedKVStats enginepb.MVCCStats var err error if args.DisallowShadowing { - if skippedKVStats, err = checkForKeyCollisions(ctx, readWriter, mvccStartKey, mvccEndKey, args.Data); err != nil { + maxIntents := storage.MaxIntentsPerWriteIntentError.Get(&cArgs.EvalCtx.ClusterSettings().SV) + skippedKVStats, err = checkForKeyCollisions( + ctx, readWriter, mvccStartKey, mvccEndKey, args.Data, maxIntents) + if err != nil { return result.Result{}, errors.Wrap(err, "checking for key collisions") } } @@ -294,6 +297,7 @@ func checkForKeyCollisions( mvccStartKey storage.MVCCKey, mvccEndKey storage.MVCCKey, data []byte, + maxIntents int64, ) (enginepb.MVCCStats, error) { // Create iterator over the existing data. existingDataIter := reader.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{UpperBound: mvccEndKey.Key}) @@ -306,5 +310,5 @@ func checkForKeyCollisions( return enginepb.MVCCStats{}, nil } - return existingDataIter.CheckForKeyCollisions(data, mvccStartKey.Key, mvccEndKey.Key) + return existingDataIter.CheckForKeyCollisions(data, mvccStartKey.Key, mvccEndKey.Key, maxIntents) } diff --git a/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go b/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go index e9b622f100b7..fd694eaeb4d4 100644 --- a/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go +++ b/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go @@ -26,6 +26,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/server" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/storage/enginepb" "github.com/cockroachdb/cockroach/pkg/testutils" @@ -498,6 +499,9 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { e := engineImpl.create() defer e.Close() + st := cluster.MakeTestingClusterSettings() + evalCtx := (&batcheval.MockEvalCtx{ClusterSettings: st}).EvalContext() + for _, kv := range mvccKVsFromStrs([]strKv{ {"a", 2, "aa"}, {"b", 1, "bb"}, @@ -553,6 +557,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) stats := getStats(roachpb.Key("a"), roachpb.Key("b"), sstBytes) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -580,6 +585,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -609,6 +615,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -636,6 +643,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) stats := getStats(roachpb.Key("c"), roachpb.Key("i"), sstBytes) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -668,6 +676,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -696,6 +705,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -725,6 +735,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -742,16 +753,16 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { } } - // Test key collision when ingesting a key which has a write intent in the + // Test key collision when ingesting keys which have write intents in the // existing data. { sstKVs := mvccKVsFromStrs([]strKv{ {"f", 2, "ff"}, - {"q", 4, "qq"}, - {"t", 3, "ttt"}, // has a write intent in the existing data. + {"q", 4, "qq"}, // has a write intent in the existing data + {"t", 3, "ttt"}, // has a write intent in the existing data }) - // Add in a write intent. + // Add in two write intents. ts := hlc.Timestamp{WallTime: 7} txn := roachpb.MakeTransaction( "test", @@ -760,18 +771,24 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { ts, base.DefaultMaxClockOffset.Nanoseconds(), ) + if err := storage.MVCCPut( + ctx, e, nil, []byte("q"), ts, + roachpb.MakeValueFromBytes([]byte("q")), + &txn, + ); err != nil { + t.Fatalf("%+v", err) + } if err := storage.MVCCPut( ctx, e, nil, []byte("t"), ts, roachpb.MakeValueFromBytes([]byte("tt")), &txn, ); err != nil { - if !errors.HasType(err, (*roachpb.WriteIntentError)(nil)) { - t.Fatalf("%+v", err) - } + t.Fatalf("%+v", err) } sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -784,7 +801,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { } _, err := batcheval.EvalAddSSTable(ctx, e, cArgs, nil) - if !testutils.IsError(err, "conflicting intents on \"t") { + if !testutils.IsError(err, "conflicting intents on \"q\", \"t\"") { t.Fatalf("%+v", err) } } @@ -810,6 +827,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -839,6 +857,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) stats := getStats(roachpb.Key("e"), roachpb.Key("zz"), sstBytes) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -868,6 +887,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -896,6 +916,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -924,6 +945,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { sstBytes := getSSTBytes(sstKVs) cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, @@ -963,6 +985,7 @@ func TestAddSSTableDisallowShadowing(t *testing.T) { commandStats := enginepb.MVCCStats{} cArgs := batcheval.CommandArgs{ + EvalCtx: evalCtx, Header: roachpb.Header{ Timestamp: hlc.Timestamp{WallTime: 7}, }, diff --git a/pkg/kv/kvserver/spanset/batch.go b/pkg/kv/kvserver/spanset/batch.go index 9d0e4923e774..58ed3bf61a09 100644 --- a/pkg/kv/kvserver/spanset/batch.go +++ b/pkg/kv/kvserver/spanset/batch.go @@ -215,9 +215,9 @@ func (i *MVCCIterator) FindSplitKey( // CheckForKeyCollisions is part of the storage.MVCCIterator interface. func (i *MVCCIterator) CheckForKeyCollisions( - sstData []byte, start, end roachpb.Key, + sstData []byte, start, end roachpb.Key, maxIntents int64, ) (enginepb.MVCCStats, error) { - return i.i.CheckForKeyCollisions(sstData, start, end) + return i.i.CheckForKeyCollisions(sstData, start, end, maxIntents) } // SetUpperBound is part of the storage.MVCCIterator interface. diff --git a/pkg/storage/engine.go b/pkg/storage/engine.go index 133716f2f8b4..a46c8e1a5d6c 100644 --- a/pkg/storage/engine.go +++ b/pkg/storage/engine.go @@ -177,9 +177,13 @@ type MVCCIterator interface { // must set the upper bound on the iterator before calling this method. FindSplitKey(start, end, minSplitKey roachpb.Key, targetSize int64) (MVCCKey, error) // CheckForKeyCollisions checks whether any keys collide between the iterator - // and the encoded SST data specified, within the provided key range. Returns - // stats on skipped KVs, or an error if a collision is found. - CheckForKeyCollisions(sstData []byte, start, end roachpb.Key) (enginepb.MVCCStats, error) + // and the encoded SST data specified, within the provided key range. + // maxIntents specifies the number of intents to collect and return in a + // WriteIntentError (0 disables batching, pass math.MaxInt64 to collect all). + // Returns stats on skipped KVs, or an error if a collision is found. + CheckForKeyCollisions( + sstData []byte, start, end roachpb.Key, maxIntents int64, + ) (enginepb.MVCCStats, error) // SetUpperBound installs a new upper bound for this iterator. The caller // can modify the parameter after this function returns. This must not be a // nil key. When Reader.ConsistentIterators is true, prefer creating a new diff --git a/pkg/storage/engine_test.go b/pkg/storage/engine_test.go index f5fb867534f4..d87dd9156c5c 100644 --- a/pkg/storage/engine_test.go +++ b/pkg/storage/engine_test.go @@ -1013,6 +1013,84 @@ func TestEngineDeleteIterRange(t *testing.T) { }) } +func TestMVCCIteratorCheckForKeyCollisionsMaxIntents(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + keys := []string{"aa", "bb", "cc", "dd"} + intents := []string{"a", "b", "c"} + + testcases := []struct { + maxIntents int64 + expectIntents []string + }{ + {maxIntents: -1, expectIntents: []string{"a"}}, + {maxIntents: 0, expectIntents: []string{"a"}}, + {maxIntents: 1, expectIntents: []string{"a"}}, + {maxIntents: 2, expectIntents: []string{"a", "b"}}, + {maxIntents: 3, expectIntents: []string{"a", "b", "c"}}, + {maxIntents: 4, expectIntents: []string{"a", "b", "c"}}, + } + + // Create SST with keys equal to intents at txn2TS. + sstFile := &MemFile{} + sstWriter := MakeBackupSSTWriter(sstFile) + defer sstWriter.Close() + for _, k := range intents { + key := MVCCKey{Key: roachpb.Key(k), Timestamp: txn2TS} + value := roachpb.Value{} + value.SetString("sst") + value.InitChecksum(key.Key) + require.NoError(t, sstWriter.Put(key, value.RawBytes)) + } + require.NoError(t, sstWriter.Finish()) + sstWriter.Close() + + for _, engineImpl := range mvccEngineImpls { + t.Run(engineImpl.name, func(t *testing.T) { + ctx := context.Background() + engine := engineImpl.create() + defer engine.Close() + + // Write some committed keys and intents at txn1TS. + batch := engine.NewBatch() + for _, key := range keys { + require.NoError(t, batch.PutMVCC( + MVCCKey{Key: roachpb.Key(key), Timestamp: txn1TS}, []byte("value"))) + } + for _, key := range intents { + require.NoError(t, MVCCPut( + ctx, batch, nil, roachpb.Key(key), txn1TS, roachpb.MakeValueFromString("intent"), txn1)) + } + require.NoError(t, batch.Commit(true)) + batch.Close() + require.NoError(t, engine.Flush()) + + for _, tc := range testcases { + t.Run(fmt.Sprintf("maxIntents=%d", tc.maxIntents), func(t *testing.T) { + // Provoke and check WriteIntentErrors. + iter := engine.NewMVCCIterator( + MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: roachpb.Key("z")}) + defer iter.Close() + iter.SeekGE(MVCCKey{Key: roachpb.Key("a")}) + + _, err := iter.CheckForKeyCollisions( + sstFile.Bytes(), roachpb.Key("a"), roachpb.Key("z"), tc.maxIntents) + require.Error(t, err) + writeIntentErr := &roachpb.WriteIntentError{} + require.ErrorAs(t, err, &writeIntentErr) + + actual := []string{} + for _, i := range writeIntentErr.Intents { + actual = append(actual, string(i.Key)) + } + require.Equal(t, tc.expectIntents, actual) + }) + } + }) + } +} + func TestSnapshot(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/storage/intent_interleaving_iter.go b/pkg/storage/intent_interleaving_iter.go index 871772e187fe..fd7d5f4098f6 100644 --- a/pkg/storage/intent_interleaving_iter.go +++ b/pkg/storage/intent_interleaving_iter.go @@ -942,9 +942,9 @@ func (i *intentInterleavingIter) FindSplitKey( } func (i *intentInterleavingIter) CheckForKeyCollisions( - sstData []byte, start, end roachpb.Key, + sstData []byte, start, end roachpb.Key, maxIntents int64, ) (enginepb.MVCCStats, error) { - return checkForKeyCollisionsGo(i, sstData, start, end) + return checkForKeyCollisionsGo(i, sstData, start, end, maxIntents) } func (i *intentInterleavingIter) SetUpperBound(key roachpb.Key) { diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 6e4183942df5..6287d867642f 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -4193,9 +4193,10 @@ func ComputeStatsForRange( // is not considered a collision and we continue iteration from the next key in // the existing data. func checkForKeyCollisionsGo( - existingIter MVCCIterator, sstData []byte, start, end roachpb.Key, + existingIter MVCCIterator, sstData []byte, start, end roachpb.Key, maxIntents int64, ) (enginepb.MVCCStats, error) { var skippedKVStats enginepb.MVCCStats + var intents []roachpb.Intent sstIter, err := NewMemSSTIterator(sstData, false) if err != nil { return enginepb.MVCCStats{}, err @@ -4229,20 +4230,17 @@ func checkForKeyCollisionsGo( if len(mvccMeta.RawBytes) > 0 { return enginepb.MVCCStats{}, errors.Errorf("inline values are unsupported when checking for key collisions") } else if mvccMeta.Txn != nil { - // Check for a write intent. - // - // TODO(adityamaru): Currently, we raise a WriteIntentError on - // encountering all intents. This is because, we do not expect to - // encounter many intents during IMPORT INTO as we lock the key space we - // are importing into. Older write intents could however be found in the - // target key space, which will require appropriate resolution logic. - writeIntentErr := roachpb.WriteIntentError{ - Intents: []roachpb.Intent{ - roachpb.MakeIntent(mvccMeta.Txn, existingIter.Key().Key), - }, + // Check for a write intent. We keep looking for additional intents to + // return a large batch for intent resolution. The caller will likely + // resolve the returned intents and retry the call, which would be + // quadratic, so this significantly reduces the overall number of scans. + intents = append(intents, roachpb.MakeIntent(mvccMeta.Txn, existingIter.Key().Key)) + if int64(len(intents)) >= maxIntents { + return enginepb.MVCCStats{}, &roachpb.WriteIntentError{Intents: intents} } - - return enginepb.MVCCStats{}, &writeIntentErr + existingIter.NextKey() + ok, extErr = existingIter.Valid() + continue } else { return enginepb.MVCCStats{}, errors.Errorf("intent without transaction") } @@ -4319,6 +4317,9 @@ func checkForKeyCollisionsGo( if sstErr != nil { return enginepb.MVCCStats{}, sstErr } + if len(intents) > 0 { + return enginepb.MVCCStats{}, &roachpb.WriteIntentError{Intents: intents} + } return skippedKVStats, nil } diff --git a/pkg/storage/pebble_iterator.go b/pkg/storage/pebble_iterator.go index 93d917ba787b..4d140155ed38 100644 --- a/pkg/storage/pebble_iterator.go +++ b/pkg/storage/pebble_iterator.go @@ -778,9 +778,9 @@ func (p *pebbleIterator) SupportsPrev() bool { // CheckForKeyCollisions indicates if the provided SST data collides with this // iterator in the specified range. func (p *pebbleIterator) CheckForKeyCollisions( - sstData []byte, start, end roachpb.Key, + sstData []byte, start, end roachpb.Key, maxIntents int64, ) (enginepb.MVCCStats, error) { - return checkForKeyCollisionsGo(p, sstData, start, end) + return checkForKeyCollisionsGo(p, sstData, start, end, maxIntents) } // GetRawIter is part of the EngineIterator interface. From 8b47233bfea3c1044aa21e24d25d6ca470d1de13 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Fri, 29 Oct 2021 09:51:01 +0100 Subject: [PATCH 112/205] util/admission: avoid write to tenantInfo after releasing it Previously, releaseTenantInfo(info) put the *tenantInfo back into the pool, but we then wrote to info.used directly after. This is a data race leading to multiple test failures. Since releaseTenantInfo resets the tenantInfo, we don't care about resetting the used field in that case anyway. Release note: None --- pkg/util/admission/work_queue.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/util/admission/work_queue.go b/pkg/util/admission/work_queue.go index 6a112ccf9b18..9a8ca8ef6f48 100644 --- a/pkg/util/admission/work_queue.go +++ b/pkg/util/admission/work_queue.go @@ -442,8 +442,7 @@ func (q *WorkQueue) gcTenantsAndResetTokens() { if info.used == 0 && len(info.waitingWorkHeap) == 0 { delete(q.mu.tenants, id) releaseTenantInfo(info) - } - if q.usesTokens { + } else if q.usesTokens { info.used = 0 // All the heap members will reset used=0, so no need to change heap // ordering. From bff446cc77d52281a5aa2eae6099d6c90fcd22b5 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Fri, 29 Oct 2021 11:42:52 +0100 Subject: [PATCH 113/205] sql: add test fixtures dependency to BUILD.bazel This fixes recent TestPlanGistBuilder failures when using bazel. Release note: None --- pkg/sql/opt/exec/explain/BUILD.bazel | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/sql/opt/exec/explain/BUILD.bazel b/pkg/sql/opt/exec/explain/BUILD.bazel index d04daf1c3240..8bb74feca4c0 100644 --- a/pkg/sql/opt/exec/explain/BUILD.bazel +++ b/pkg/sql/opt/exec/explain/BUILD.bazel @@ -46,7 +46,9 @@ go_test( "output_test.go", "plan_gist_test.go", ], - data = glob(["testdata/**"]), + data = glob(["testdata/**"]) + [ + "//pkg/sql/opt/testutils/opttester:testfixtures", + ], embed = [":explain"], deps = [ "//pkg/base", From af360f885d51c2a17fc16d842bf82b225ac9924a Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 29 Oct 2021 07:16:18 +0000 Subject: [PATCH 114/205] storage: add `ExportOptions.MaxIntents` `Engine.ExportMVCCToSst()` implementations would previously fetch the cluster setting `MaxIntentsPerWriteIntentError` internally from the engine's cluster settings, which can be opaque. This patch instead adds `ExportOptions.MaxIntents` and passes the cluster setting in expicitly, for clarity. This is consistent with what we already do for `ScanOptions.MaxIntents`. Release note: None --- pkg/kv/kvserver/batcheval/cmd_export.go | 6 ++++++ pkg/storage/engine.go | 6 ++++++ pkg/storage/mvcc_incremental_iterator_test.go | 1 + pkg/storage/pebble.go | 13 +++++-------- pkg/storage/pebble_test.go | 1 + 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/pkg/kv/kvserver/batcheval/cmd_export.go b/pkg/kv/kvserver/batcheval/cmd_export.go index 17d838fce449..c1be7b5af228 100644 --- a/pkg/kv/kvserver/batcheval/cmd_export.go +++ b/pkg/kv/kvserver/batcheval/cmd_export.go @@ -151,6 +151,11 @@ func evalExport( maxRunTime := exportRequestMaxIterationTime.Get(&cArgs.EvalCtx.ClusterSettings().SV) + var maxIntents uint64 + if m := storage.MaxIntentsPerWriteIntentError.Get(&cArgs.EvalCtx.ClusterSettings().SV); m > 0 { + maxIntents = uint64(m) + } + // Time-bound iterators only make sense to use if the start time is set. useTBI := args.EnableTimeBoundIteratorOptimization && !args.StartTime.IsEmpty() // Only use resume timestamp if splitting mid key is enabled. @@ -170,6 +175,7 @@ func evalExport( ExportAllRevisions: exportAllRevisions, TargetSize: targetSize, MaxSize: maxSize, + MaxIntents: maxIntents, StopMidKey: args.SplitMidKey, UseTBI: useTBI, ResourceLimiter: storage.NewResourceLimiter(storage.ResourceLimiterOptions{MaxRunTime: maxRunTime}, timeutil.DefaultTimeSource{}), diff --git a/pkg/storage/engine.go b/pkg/storage/engine.go index a46c8e1a5d6c..797d9803591d 100644 --- a/pkg/storage/engine.go +++ b/pkg/storage/engine.go @@ -386,6 +386,12 @@ type ExportOptions struct { // to an SST that exceeds maxSize, an error will be returned. This parameter // exists to prevent creating SSTs which are too large to be used. MaxSize uint64 + // MaxIntents specifies the number of intents to collect and return in a + // WriteIntentError. The caller will likely resolve the returned intents and + // retry the call, which would be quadratic, so this significantly reduces the + // overall number of scans. 0 disables batching and returns the first intent, + // pass math.MaxUint64 to collect all. + MaxIntents uint64 // If StopMidKey is false, once function reaches targetSize it would continue // adding all versions until it reaches next key or end of range. If true, it // would stop immediately when targetSize is reached and return the next versions diff --git a/pkg/storage/mvcc_incremental_iterator_test.go b/pkg/storage/mvcc_incremental_iterator_test.go index b1a397281a24..538c56c6ab7b 100644 --- a/pkg/storage/mvcc_incremental_iterator_test.go +++ b/pkg/storage/mvcc_incremental_iterator_test.go @@ -193,6 +193,7 @@ func assertExportedErrs( ExportAllRevisions: revisions, TargetSize: big, MaxSize: big, + MaxIntents: uint64(MaxIntentsPerWriteIntentError.Default()), StopMidKey: false, UseTBI: useTBI, }, sstFile) diff --git a/pkg/storage/pebble.go b/pkg/storage/pebble.go index a32b4857f0ca..c2962c0933dd 100644 --- a/pkg/storage/pebble.go +++ b/pkg/storage/pebble.go @@ -794,8 +794,7 @@ func (p *Pebble) ExportMVCCToSst( ) (roachpb.BulkOpSummary, roachpb.Key, hlc.Timestamp, error) { r := wrapReader(p) // Doing defer r.Free() does not inline. - maxIntentCount := MaxIntentsPerWriteIntentError.Get(&p.settings.SV) - summary, k, err := pebbleExportToSst(ctx, r, exportOptions, dest, maxIntentCount) + summary, k, err := pebbleExportToSst(ctx, r, exportOptions, dest) r.Free() return summary, k.Key, k.Timestamp, err } @@ -1600,8 +1599,7 @@ func (p *pebbleReadOnly) ExportMVCCToSst( ) (roachpb.BulkOpSummary, roachpb.Key, hlc.Timestamp, error) { r := wrapReader(p) // Doing defer r.Free() does not inline. - maxIntentCount := MaxIntentsPerWriteIntentError.Get(&p.parent.settings.SV) - summary, k, err := pebbleExportToSst(ctx, r, exportOptions, dest, maxIntentCount) + summary, k, err := pebbleExportToSst(ctx, r, exportOptions, dest) r.Free() return summary, k.Key, k.Timestamp, err } @@ -1867,8 +1865,7 @@ func (p *pebbleSnapshot) ExportMVCCToSst( ) (roachpb.BulkOpSummary, roachpb.Key, hlc.Timestamp, error) { r := wrapReader(p) // Doing defer r.Free() does not inline. - maxIntentCount := MaxIntentsPerWriteIntentError.Get(&p.settings.SV) - summary, k, err := pebbleExportToSst(ctx, r, exportOptions, dest, maxIntentCount) + summary, k, err := pebbleExportToSst(ctx, r, exportOptions, dest) r.Free() return summary, k.Key, k.Timestamp, err } @@ -1989,7 +1986,7 @@ func (e *ExceedMaxSizeError) Error() string { } func pebbleExportToSst( - ctx context.Context, reader Reader, options ExportOptions, dest io.Writer, maxIntentCount int64, + ctx context.Context, reader Reader, options ExportOptions, dest io.Writer, ) (roachpb.BulkOpSummary, MVCCKey, error) { var span *tracing.Span ctx, span = tracing.ChildSpan(ctx, "pebbleExportToSst") @@ -2115,7 +2112,7 @@ func pebbleExportToSst( // If we do it means this export can't complete and is aborted. We need to loop over remaining data // to collect all matching intents before returning them in an error to the caller. if iter.NumCollectedIntents() > 0 { - for int64(iter.NumCollectedIntents()) < maxIntentCount { + for uint64(iter.NumCollectedIntents()) < options.MaxIntents { iter.NextKey() // If we encounter other errors during intent collection, we return our original write intent failure. // We would find this new error again upon retry. diff --git a/pkg/storage/pebble_test.go b/pkg/storage/pebble_test.go index 335da5e8a775..6f0ac40c22bf 100644 --- a/pkg/storage/pebble_test.go +++ b/pkg/storage/pebble_test.go @@ -653,6 +653,7 @@ func TestSstExportFailureIntentBatching(t *testing.T) { ExportAllRevisions: true, TargetSize: 0, MaxSize: 0, + MaxIntents: uint64(MaxIntentsPerWriteIntentError.Default()), StopMidKey: false, UseTBI: true, }, destination) From b8b3e37a4e0fb9164a2187df01fb8d02777b0d04 Mon Sep 17 00:00:00 2001 From: Rebecca Taft Date: Thu, 28 Oct 2021 17:16:35 -0500 Subject: [PATCH 115/205] opt: fix regression in many-columns-and-indexes microbenchmark This commit fixes a regression that was due to unnecessary computation of interesting orderings. We now only compute interesting orderings when it may be beneficial for planning purposes. Fixes #72001 Release note (performance improvement): Fixed a performance regression in planning that could occur for simple queries on schemas with a large number of indexes. --- pkg/sql/logictest/testdata/logic_test/prepare | 3 +-- pkg/sql/opt/ordering/select.go | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/prepare b/pkg/sql/logictest/testdata/logic_test/prepare index babb336f95bf..394c5be447dd 100644 --- a/pkg/sql/logictest/testdata/logic_test/prepare +++ b/pkg/sql/logictest/testdata/logic_test/prepare @@ -1181,8 +1181,7 @@ select │ ├── cost: 1064.42 │ ├── key: (1) │ ├── fd: (1)-->(2) - │ ├── prune: (1,2) - │ └── interesting orderings: (+1) + │ └── prune: (1,2) └── filters └── (k:1 % 2) = 1 [outer=(1), immutable] diff --git a/pkg/sql/opt/ordering/select.go b/pkg/sql/opt/ordering/select.go index c7d2f9c49480..8da139675c85 100644 --- a/pkg/sql/opt/ordering/select.go +++ b/pkg/sql/opt/ordering/select.go @@ -27,6 +27,10 @@ func selectBuildChildReqOrdering( if childIdx != 0 { return props.OrderingChoice{} } + if required.Any() { + return *required + } + child := parent.(*memo.SelectExpr).Input // Use interesting orderings from the child to "guide" the required ordering From f85b8ff601de964e3ae04b5f414c80d13a0136fd Mon Sep 17 00:00:00 2001 From: Lindsey Jin Date: Fri, 22 Oct 2021 18:26:16 -0400 Subject: [PATCH 116/205] sql/server: add function to reset index usage stats Previously, there was no existing way to clear index usage statistics from the crdb_internal.index_usage_statistics table. This commit adds a function that enables developers to clear index usage metrics using RPC fanout to reach all nodes in a cluster. We have also added a new metadata field that tracks the last reset time. Currently, this functionality can be accessed via the SQL shell, and is not yet accessible from the frontend console. Release note (sql change): Add function crdb_internal.reset_index_usage_stats() to clear index usage stats. This can be invoked from the SQL shell. --- docs/generated/http/full.md | 41 + docs/generated/sql/functions.md | 2 + pkg/ccl/serverccl/tenant_status_test.go | 106 ++ pkg/server/index_usage_stats.go | 66 + pkg/server/serverpb/status.go | 1 + pkg/server/serverpb/status.pb.go | 1370 +++++++++++------ pkg/server/serverpb/status.pb.gw.go | 81 + pkg/server/serverpb/status.proto | 18 + pkg/server/tenant_status.go | 62 +- pkg/sql/conn_executor.go | 54 +- pkg/sql/distsql/server.go | 25 +- pkg/sql/execinfra/server_config.go | 4 + pkg/sql/idxusage/BUILD.bazel | 2 + .../idxusage/index_usage_stats_controller.go | 42 + pkg/sql/idxusage/local_idx_usage_stats.go | 18 + pkg/sql/planner.go | 32 +- pkg/sql/sem/builtins/builtins.go | 22 +- pkg/sql/sem/tree/eval.go | 9 + 18 files changed, 1398 insertions(+), 557 deletions(-) create mode 100644 pkg/sql/idxusage/index_usage_stats_controller.go diff --git a/docs/generated/http/full.md b/docs/generated/http/full.md index 9ec1c094b9f5..4ec522b9bacf 100644 --- a/docs/generated/http/full.md +++ b/docs/generated/http/full.md @@ -3956,6 +3956,47 @@ Response object returned by IndexUsageStatistics. | Field | Type | Label | Description | Support status | | ----- | ---- | ----- | ----------- | -------------- | | statistics | [cockroach.sql.CollectedIndexUsageStatistics](#cockroach.server.serverpb.IndexUsageStatisticsResponse-cockroach.sql.CollectedIndexUsageStatistics) | repeated | | [reserved](#support-status) | +| last_reset | [google.protobuf.Timestamp](#cockroach.server.serverpb.IndexUsageStatisticsResponse-google.protobuf.Timestamp) | | Timestamp of the last index usage stats reset. | [reserved](#support-status) | + + + + + + + +## ResetIndexUsageStats + +`POST /_status/resetindexusagestats` + + + +Support status: [reserved](#support-status) + +#### Request Parameters + + + + +Request object for issuing a index usage stats reset request. + + +| Field | Type | Label | Description | Support status | +| ----- | ---- | ----- | ----------- | -------------- | +| node_id | [string](#cockroach.server.serverpb.ResetIndexUsageStatsRequest-string) | | | [reserved](#support-status) | + + + + + + + +#### Response Parameters + + + + +Response object returned by ResetIndexUsageStatsRequest. + diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index c91aaddfe7be..3965fd4230da 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -2924,6 +2924,8 @@ SELECT * FROM crdb_internal.check_consistency(true, ‘\x02’, ‘\x04’)

+ - diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index da806b77f112..d0b7f9698b86 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -952,7 +952,6 @@ unreserved_keyword ::= | 'IMMEDIATE' | 'IMPORT' | 'INCLUDE' - | 'INCLUDE_DEPRECATED_INTERLEAVES' | 'INCLUDING' | 'INCREMENT' | 'INCREMENTAL' @@ -1863,7 +1862,6 @@ backup_options ::= | 'REVISION_HISTORY' | 'DETACHED' | 'KMS' '=' string_or_placeholder_opt_list - | 'INCLUDE_DEPRECATED_INTERLEAVES' c_expr ::= d_expr diff --git a/pkg/bench/bench_test.go b/pkg/bench/bench_test.go index 4b4c5c68911c..20f3a315b596 100644 --- a/pkg/bench/bench_test.go +++ b/pkg/bench/bench_test.go @@ -386,8 +386,6 @@ func BenchmarkSQL(b *testing.B) { runBenchmarkInsertDistinct, runBenchmarkInsertFK, runBenchmarkInsertSecondaryIndex, - runBenchmarkInterleavedSelect, - runBenchmarkInterleavedFK, runBenchmarkTrackChoices, runBenchmarkUpdate, runBenchmarkUpsert, @@ -751,88 +749,6 @@ func BenchmarkScanFilter(b *testing.B) { }) } -func runBenchmarkInterleavedSelect(b *testing.B, db *sqlutils.SQLRunner, count int) { - defer func() { - db.Exec(b, `DROP TABLE IF EXISTS bench.interleaved_select2`) - db.Exec(b, `DROP TABLE IF EXISTS bench.interleaved_select1`) - }() - - db.Exec(b, `CREATE TABLE bench.interleaved_select1 (a INT PRIMARY KEY, b INT)`) - db.Exec(b, `CREATE TABLE bench.interleaved_select2 (c INT PRIMARY KEY, d INT) INTERLEAVE IN PARENT interleaved_select1 (c)`) - - const interleaveFreq = 4 - - var buf1 bytes.Buffer - var buf2 bytes.Buffer - buf1.WriteString(`INSERT INTO bench.interleaved_select1 VALUES `) - buf2.WriteString(`INSERT INTO bench.interleaved_select2 VALUES `) - for i := 0; i < count; i++ { - if i > 0 { - buf1.WriteString(", ") - } - fmt.Fprintf(&buf1, "(%d, %d)", i, i) - if i%interleaveFreq == 0 { - if i > 0 { - buf2.WriteString(", ") - } - fmt.Fprintf(&buf2, "(%d, %d)", i, i) - } - } - db.Exec(b, buf1.String()) - db.Exec(b, buf2.String()) - - query := `SELECT * FROM bench.interleaved_select1 is1 INNER JOIN bench.interleaved_select2 is2 on is1.a = is2.c` - - b.ResetTimer() - for i := 0; i < b.N; i++ { - rows := db.Query(b, query) - n := 0 - for rows.Next() { - n++ - } - rows.Close() - if err := rows.Err(); err != nil { - b.Fatal(err) - } - expected := (count + interleaveFreq - 1) / interleaveFreq - if n != expected { - b.Fatalf("unexpected result count: %d (expected %d)", n, expected) - } - } - b.StopTimer() -} - -func runBenchmarkInterleavedFK(b *testing.B, db *sqlutils.SQLRunner, count int) { - defer func() { - db.Exec(b, `DROP TABLE IF EXISTS bench.parent CASCADE; DROP TABLE IF EXISTS bench.child CASCADE`) - }() - - db.Exec(b, ` -CREATE TABLE bench.parent (a INT PRIMARY KEY); -INSERT INTO bench.parent VALUES(0); -CREATE TABLE bench.child - (a INT REFERENCES bench.parent(a), - b INT, PRIMARY KEY(a, b)) -INTERLEAVE IN PARENT bench.parent (a) -`) - - b.ResetTimer() - var buf bytes.Buffer - val := 0 - for i := 0; i < b.N; i++ { - buf.Reset() - buf.WriteString(`INSERT INTO bench.child VALUES `) - for j := 0; j < count; j++ { - if j > 0 { - buf.WriteString(", ") - } - fmt.Fprintf(&buf, "(0, %d)", val) - val++ - } - db.Exec(b, buf.String()) - } -} - // runBenchmarkOrderBy benchmarks scanning a table and sorting the results. func runBenchmarkOrderBy( b *testing.B, db *sqlutils.SQLRunner, count int, limit int, distinct bool, diff --git a/pkg/ccl/backupccl/BUILD.bazel b/pkg/ccl/backupccl/BUILD.bazel index 6a1fd0ccc470..60c82cd2e0b2 100644 --- a/pkg/ccl/backupccl/BUILD.bazel +++ b/pkg/ccl/backupccl/BUILD.bazel @@ -40,7 +40,6 @@ go_library( "//pkg/ccl/storageccl", "//pkg/ccl/utilccl", "//pkg/cloud", - "//pkg/clusterversion", "//pkg/featureflag", "//pkg/gossip", "//pkg/jobs", diff --git a/pkg/ccl/backupccl/backup_planning.go b/pkg/ccl/backupccl/backup_planning.go index f3b4ea9a2523..c417a51064c1 100644 --- a/pkg/ccl/backupccl/backup_planning.go +++ b/pkg/ccl/backupccl/backup_planning.go @@ -55,15 +55,14 @@ import ( ) const ( - backupOptRevisionHistory = "revision_history" - backupOptIncludeInterleaves = "include_deprecated_interleaves" - backupOptEncPassphrase = "encryption_passphrase" - backupOptEncKMS = "kms" - backupOptWithPrivileges = "privileges" - backupOptAsJSON = "as_json" - backupOptWithDebugIDs = "debug_ids" - localityURLParam = "COCKROACH_LOCALITY" - defaultLocalityValue = "default" + backupOptRevisionHistory = "revision_history" + backupOptEncPassphrase = "encryption_passphrase" + backupOptEncKMS = "kms" + backupOptWithPrivileges = "privileges" + backupOptAsJSON = "as_json" + backupOptWithDebugIDs = "debug_ids" + localityURLParam = "COCKROACH_LOCALITY" + defaultLocalityValue = "default" ) type encryptionMode int @@ -777,16 +776,6 @@ func backupPlanHook( return errors.AssertionFailedf("unexpected descriptor coverage %v", backupStmt.Coverage()) } - if !backupStmt.Options.IncludeDeprecatedInterleaves { - for _, desc := range targetDescs { - if table, ok := desc.(catalog.TableDescriptor); ok { - if table.IsInterleaved() { - return errors.Errorf("interleaved tables are deprecated and backups containing interleaved tables will not be able to be RESTORE'd by future versions -- use option %q to backup interleaved tables anyway %q", backupOptIncludeInterleaves, table.TableDesc().Name) - } - } - } - } - // Check BACKUP privileges. err = checkPrivilegesForBackup(ctx, backupStmt, p, targetDescs, to) if err != nil { @@ -806,10 +795,6 @@ func backupPlanHook( } } - if err := ensureInterleavesIncluded(tables); err != nil { - return err - } - clusterID := p.ExecCfg().ClusterID() for i := range prevBackups { // IDs are how we identify tables, and those are only meaningful in the diff --git a/pkg/ccl/backupccl/backup_test.go b/pkg/ccl/backupccl/backup_test.go index a2fd953ea329..a7a47d8ba781 100644 --- a/pkg/ccl/backupccl/backup_test.go +++ b/pkg/ccl/backupccl/backup_test.go @@ -6369,48 +6369,6 @@ func TestProtectedTimestampSpanSelectionDuringBackup(t *testing.T) { actualResolvedSpans = nil }) - t.Run("interleaved-spans", func(t *testing.T) { - runner.Exec(t, "CREATE DATABASE test; USE test;") - runner.Exec(t, "CREATE TABLE grandparent (a INT PRIMARY KEY, v BYTES, INDEX gpindex (v))") - runner.Exec(t, "CREATE TABLE parent (a INT, b INT, v BYTES, "+ - "PRIMARY KEY(a, b)) INTERLEAVE IN PARENT grandparent(a)") - runner.Exec(t, "CREATE TABLE child (a INT, b INT, c INT, v BYTES, "+ - "PRIMARY KEY(a, b, c), INDEX childindex(c)) INTERLEAVE IN PARENT parent(a, b)") - - runner.Exec(t, fmt.Sprintf(`BACKUP DATABASE test INTO '%s' WITH include_deprecated_interleaves`, baseBackupURI+t.Name())) - // /Table/59/{1-2} encompasses the pk of grandparent, and the interleaved - // tables parent and child. - // /Table/59/2 - /Table/59/3 is for the gpindex - // /Table/61/{2-3} is for the childindex - grandparentID := getTableID(db, "test", "grandparent") - childID := getTableID(db, "test", "child") - require.Equal(t, []string{fmt.Sprintf("/Table/%d/{1-3}", grandparentID), - fmt.Sprintf("/Table/%d/{2-3}", childID)}, actualResolvedSpans) - runner.Exec(t, "DROP DATABASE test;") - actualResolvedSpans = nil - }) - - // This is a regression test for a bug that was fixed in - // https://github.com/cockroachdb/cockroach/pull/72270 where two (or more) - // public indexes followed by an interleaved index would result in index keys - // being missed during backup. - // Prior to the fix in https://github.com/cockroachdb/cockroach/pull/72270, - // the resolved spans would be `/Table/63/{1-3}` thereby missing the span for - // idx2. - // With the change we now backup `/Table/63/{1-4}` to include pkIndex, idx1, - // idx2 and idx3 (since it is interleaved it produces the span - // `/Table/63/{1-2}`). - t.Run("public-and-interleaved-indexes", func(t *testing.T) { - runner.Exec(t, "CREATE DATABASE test; USE test;") - runner.Exec(t, "CREATE TABLE foo (a INT PRIMARY KEY, b INT, v BYTES, INDEX idx1 (v), INDEX idx2(b))") - runner.Exec(t, "CREATE INDEX idx3 ON foo (a, b) INTERLEAVE IN PARENT foo (a)") - runner.Exec(t, fmt.Sprintf(`BACKUP DATABASE test INTO '%s' WITH include_deprecated_interleaves`, baseBackupURI+t.Name())) - tableID := getTableID(db, "test", "foo") - require.Equal(t, []string{fmt.Sprintf("/Table/%d/{1-4}", tableID)}, actualResolvedSpans) - runner.Exec(t, "DROP DATABASE test;") - actualResolvedSpans = nil - }) - t.Run("revs-span-merge", func(t *testing.T) { runner.Exec(t, "CREATE DATABASE test; USE test;") runner.Exec(t, "CREATE TABLE foo (k INT PRIMARY KEY, v BYTES, name STRING, "+ diff --git a/pkg/ccl/backupccl/backupresolver/targets.go b/pkg/ccl/backupccl/backupresolver/targets.go index cba46fb5bd51..accfcbb11fca 100644 --- a/pkg/ccl/backupccl/backupresolver/targets.go +++ b/pkg/ccl/backupccl/backupresolver/targets.go @@ -632,8 +632,11 @@ func ResolveTargetsToDescriptors( return nil, nil, err } - // Ensure interleaved tables appear after their parent. Since parents must be - // created before their children, simply sorting by ID accomplishes this. + // This sorting was originally required to support interleaves. + // Now that these have been removed, sorting is not strictly-speaking + // necessary but has been preserved to maintain the output of SHOW BACKUPS + // that certain tests rely on. sort.Slice(matched.Descs, func(i, j int) bool { return matched.Descs[i].GetID() < matched.Descs[j].GetID() }) + return matched.Descs, matched.ExpandedDB, nil } diff --git a/pkg/ccl/backupccl/create_scheduled_backup.go b/pkg/ccl/backupccl/create_scheduled_backup.go index 45097746d176..0cf8d8f0200c 100644 --- a/pkg/ccl/backupccl/create_scheduled_backup.go +++ b/pkg/ccl/backupccl/create_scheduled_backup.go @@ -336,9 +336,8 @@ func doCreateBackupSchedules( // Prepare backup statement (full). backupNode := &tree.Backup{ Options: tree.BackupOptions{ - CaptureRevisionHistory: eval.BackupOptions.CaptureRevisionHistory, - Detached: true, - IncludeDeprecatedInterleaves: eval.BackupOptions.IncludeDeprecatedInterleaves, + CaptureRevisionHistory: eval.BackupOptions.CaptureRevisionHistory, + Detached: true, }, Nested: true, AppendToLatest: false, diff --git a/pkg/ccl/backupccl/create_scheduled_backup_test.go b/pkg/ccl/backupccl/create_scheduled_backup_test.go index 06faf6193412..e6ec32df36b0 100644 --- a/pkg/ccl/backupccl/create_scheduled_backup_test.go +++ b/pkg/ccl/backupccl/create_scheduled_backup_test.go @@ -407,19 +407,6 @@ func TestSerializesScheduledBackupExecutionArgs(t *testing.T) { }, }, }, - { - name: "full-cluster-with-interleaved-table", - query: "CREATE SCHEDULE FOR BACKUP INTO 'nodelocal://0/backup?AWS_SECRET_ACCESS_KEY=neverappears' WITH INCLUDE_DEPRECATED_INTERLEAVES RECURRING '@hourly'", - user: freeUser, - expectedSchedules: []expectedSchedule{ - { - nameRe: "BACKUP .+", - backupStmt: "BACKUP INTO 'nodelocal://0/backup?AWS_SECRET_ACCESS_KEY=neverappears' WITH detached, include_deprecated_interleaves", - shownStmt: "BACKUP INTO 'nodelocal://0/backup?AWS_SECRET_ACCESS_KEY=redacted' WITH detached, include_deprecated_interleaves", - period: time.Hour, - }, - }, - }, { name: "full-cluster-always", query: "CREATE SCHEDULE FOR BACKUP INTO 'nodelocal://0/backup' WITH revision_history RECURRING '@hourly' FULL BACKUP ALWAYS", diff --git a/pkg/ccl/backupccl/key_rewriter.go b/pkg/ccl/backupccl/key_rewriter.go index 5f583051f41c..1be5256fb070 100644 --- a/pkg/ccl/backupccl/key_rewriter.go +++ b/pkg/ccl/backupccl/key_rewriter.go @@ -18,7 +18,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" - "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/cockroach/pkg/util/protoutil" "github.com/cockroachdb/errors" ) @@ -157,22 +156,8 @@ func MakeKeyRewriterPrefixIgnoringInterleaved(tableID descpb.ID, indexID descpb. } // RewriteKey modifies key (possibly in place), changing all table IDs to their -// new value, including any interleaved table children and prefix ends. This -// function works by inspecting the key for table and index IDs, then uses the -// corresponding table and index descriptors to determine if interleaved data is -// present and if it is, to find the next prefix of an interleaved child, then -// calls itself recursively until all interleaved children have been rekeyed. If -// it encounters a table ID for which it does not have a configured rewrite, it -// returns the prefix of the key that was rewritten key. The returned boolean -// is true if and only if all of the table IDs found in the key were rewritten. -// If isFromSpan is true, failures in value decoding are assumed to be due to -// valid span manipulations, like PrefixEnd or Next having altered the trailing -// byte(s) to corrupt the value encoding -- in such a case we will not be able -// to decode the value (to determine how much further to scan for table IDs) but -// we can assume that since these manipulations are only done to the trailing -// byte that we're likely at the end anyway and do not need to search for any -// further table IDs to replace. -func (kr *KeyRewriter) RewriteKey(key []byte, isFromSpan bool) ([]byte, bool, error) { +// new value. +func (kr *KeyRewriter) RewriteKey(key []byte) ([]byte, bool, error) { if kr.codec.ForSystemTenant() && bytes.HasPrefix(key, keys.TenantPrefix) { // If we're rewriting from the system tenant, we don't rewrite tenant keys // at all since we assume that we're restoring an entire tenant. @@ -184,7 +169,7 @@ func (kr *KeyRewriter) RewriteKey(key []byte, isFromSpan bool) ([]byte, bool, er return nil, false, err } - rekeyed, ok, err := kr.rewriteTableKey(noTenantPrefix, isFromSpan) + rekeyed, ok, err := kr.rewriteTableKey(noTenantPrefix) if err != nil { return nil, false, err } @@ -203,11 +188,11 @@ func (kr *KeyRewriter) RewriteKey(key []byte, isFromSpan bool) ([]byte, bool, er return rekeyed, ok, err } -// rewriteTableKey recursively (in the case of interleaved tables) rewrites the -// table IDs in the key. It assumes that any tenant ID has been stripped from -// the key so it operates with the system codec. It is the responsibility of the -// caller to either remap, or re-prepend any required tenant prefix. -func (kr *KeyRewriter) rewriteTableKey(key []byte, isFromSpan bool) ([]byte, bool, error) { +// rewriteTableKey rewrites the table IDs in the key. +// It assumes that any tenant ID has been stripped from the key so it operates +// with the system codec. It is the responsibility of the caller to either +// remap, or re-prepend any required tenant prefix. +func (kr *KeyRewriter) rewriteTableKey(key []byte) ([]byte, bool, error) { // Fetch the original table ID for descriptor lookup. Ignore errors because // they will be caught later on if tableID isn't in descs or kr doesn't // perform a rewrite. @@ -221,89 +206,5 @@ func (kr *KeyRewriter) rewriteTableKey(key []byte, isFromSpan bool) ([]byte, boo if desc == nil { return nil, false, errors.Errorf("missing descriptor for table %d", tableID) } - // Check if this key may have interleaved children. - k, _, indexID, err := keys.SystemSQLCodec.DecodeIndexPrefix(key) - if err != nil { - return nil, false, err - } - if len(k) == 0 { - // If there isn't any more data, we are at some split boundary. - return key, true, nil - } - idx, err := desc.FindIndexWithID(descpb.IndexID(indexID)) - if err != nil { - return nil, false, err - } - if idx.NumInterleavedBy() == 0 { - // Not interleaved. - return key, true, nil - } - // We do not support interleaved secondary indexes. - if !idx.Primary() { - return nil, false, errors.New("restoring interleaved secondary indexes not supported") - } - colIDs, _ := catalog.FullIndexColumnIDs(idx) - var skipCols int - for i := 0; i < idx.NumInterleaveAncestors(); i++ { - skipCols += int(idx.GetInterleaveAncestor(i).SharedPrefixLen) - } - for i := 0; i < len(colIDs)-skipCols; i++ { - n, err := encoding.PeekLength(k) - if err != nil { - // PeekLength, and key decoding in general, can fail when reading the last - // value from a key that is coming from a span. Keys in spans are often - // altered e.g. by calling Next() or PrefixEnd() to ensure a given span is - // inclusive or for other reasons, but the manipulations sometimes change - // the encoded bytes, meaning they can no longer successfully decode as - // back to the original values. This is OK when span boundaries mostly are - // only required to be even divisions of keyspace, but when we try to go - // back to interpreting them as keys, it can fall apart. Partitioning a - // table (and applying zone configs) eagerly creates splits at the defined - // partition boundaries, using PrefixEnd for their ends, resulting in such - // spans. - // - // Fortunately, the only common span manipulations are to the trailing - // byte of a key (e.g. incrementing or appending a null) so for our needs - // here, if we fail to decode because of one of those manipulations, we - // can assume that we are at the end of the key as far as fields where a - // table ID which needs to be replaced can appear and consider the rewrite - // of this key as being compelted successfully. - // - // Finally unlike key rewrites of actual row-data, span rewrites do not - // need to be perfect: spans are only rewritten for use in pre-splitting - // and work distribution, so even if it turned out that this assumption - // was incorrect, it could cause a performance degradation but does not - // pose a correctness risk. - if isFromSpan { - return key, true, nil - } - return nil, false, err - } - k = k[n:] - // Check if we ran out of key before getting to an interleave child? - if len(k) == 0 { - return key, true, nil - } - } - // We might have an interleaved key. - k, ok = encoding.DecodeIfInterleavedSentinel(k) - if !ok { - return key, true, nil - } - if len(k) == 0 { - // We have seen some span keys end in an interleaved sentinel. - // Check if we ran out of key before getting to an interleave child? - return key, true, nil - } - prefix := key[:len(key)-len(k)] - k, ok, err = kr.rewriteTableKey(k, isFromSpan) - if err != nil { - return nil, false, err - } - if !ok { - // The interleaved child was not rewritten, skip this row. - return prefix, false, nil - } - key = append(prefix, k...) return key, true, nil } diff --git a/pkg/ccl/backupccl/key_rewriter_test.go b/pkg/ccl/backupccl/key_rewriter_test.go index 0e01990e62a8..379deddf81fe 100644 --- a/pkg/ccl/backupccl/key_rewriter_test.go +++ b/pkg/ccl/backupccl/key_rewriter_test.go @@ -74,8 +74,6 @@ func TestKeyRewriter(t *testing.T) { }, } - const notSpan = false - kr, err := makeKeyRewriterFromRekeys(keys.SystemSQLCodec, rekeys) if err != nil { t.Fatal(err) @@ -84,7 +82,7 @@ func TestKeyRewriter(t *testing.T) { t.Run("normal", func(t *testing.T) { key := rowenc.MakeIndexKeyPrefix(keys.SystemSQLCodec, systemschema.NamespaceTable, desc.GetPrimaryIndexID()) - newKey, ok, err := kr.RewriteKey(key, notSpan) + newKey, ok, err := kr.RewriteKey(key) if err != nil { t.Fatal(err) } @@ -103,7 +101,7 @@ func TestKeyRewriter(t *testing.T) { t.Run("prefix end", func(t *testing.T) { key := roachpb.Key(rowenc.MakeIndexKeyPrefix(keys.SystemSQLCodec, systemschema.NamespaceTable, desc.GetPrimaryIndexID())).PrefixEnd() - newKey, ok, err := kr.RewriteKey(key, notSpan) + newKey, ok, err := kr.RewriteKey(key) if err != nil { t.Fatal(err) } @@ -132,7 +130,7 @@ func TestKeyRewriter(t *testing.T) { } key := rowenc.MakeIndexKeyPrefix(keys.SystemSQLCodec, systemschema.NamespaceTable, desc.GetPrimaryIndexID()) - newKey, ok, err := newKr.RewriteKey(key, notSpan) + newKey, ok, err := newKr.RewriteKey(key) if err != nil { t.Fatal(err) } @@ -159,7 +157,7 @@ func TestKeyRewriter(t *testing.T) { require.NoError(t, err) key := rowenc.MakeIndexKeyPrefix(srcCodec, systemschema.NamespaceTable, desc.GetPrimaryIndexID()) - newKey, ok, err := newKr.RewriteKey(key, notSpan) + newKey, ok, err := newKr.RewriteKey(key) require.NoError(t, err) if !ok { t.Fatalf("expected rewrite") diff --git a/pkg/ccl/backupccl/restore_data_processor.go b/pkg/ccl/backupccl/restore_data_processor.go index c87ca38ebaaf..e3f9b9d4ce7b 100644 --- a/pkg/ccl/backupccl/restore_data_processor.go +++ b/pkg/ccl/backupccl/restore_data_processor.go @@ -414,7 +414,7 @@ func (rd *restoreDataProcessor) processRestoreSpanEntry( value := roachpb.Value{RawBytes: valueScratch} iter.NextKey() - key.Key, ok, err = rd.kr.RewriteKey(key.Key, false /* isFromSpan */) + key.Key, ok, err = rd.kr.RewriteKey(key.Key) if err != nil { return summary, err } diff --git a/pkg/ccl/backupccl/restore_job.go b/pkg/ccl/backupccl/restore_job.go index 9dd00aa5db41..76c197f89407 100644 --- a/pkg/ccl/backupccl/restore_job.go +++ b/pkg/ccl/backupccl/restore_job.go @@ -490,7 +490,7 @@ func rewriteBackupSpanKey( return key, nil } - newKey, rewritten, err := kr.RewriteKey(append([]byte(nil), key...), true /* isFromSpan */) + newKey, rewritten, err := kr.RewriteKey(append([]byte(nil), key...)) if err != nil { return nil, errors.NewAssertionErrorWithWrappedErrf(err, "could not rewrite span start key: %s", key) diff --git a/pkg/ccl/backupccl/restore_planning.go b/pkg/ccl/backupccl/restore_planning.go index a71c99fdce9e..8148a9d671c5 100644 --- a/pkg/ccl/backupccl/restore_planning.go +++ b/pkg/ccl/backupccl/restore_planning.go @@ -22,7 +22,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/ccl/storageccl" "github.com/cockroachdb/cockroach/pkg/ccl/utilccl" "github.com/cockroachdb/cockroach/pkg/cloud" - "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/featureflag" "github.com/cockroachdb/cockroach/pkg/jobs" "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" @@ -1208,36 +1207,6 @@ func RewriteTableDescs( table.ViewQuery = viewQuery } - if err := catalog.ForEachNonDropIndex(table, func(indexI catalog.Index) error { - index := indexI.IndexDesc() - // Verify that for any interleaved index being restored, the interleave - // parent is also being restored. Otherwise, the interleave entries in the - // restored IndexDescriptors won't have anything to point to. - // TODO(dan): It seems like this restriction could be lifted by restoring - // stub TableDescriptors for the missing interleave parents. - for j, a := range index.Interleave.Ancestors { - ancestorRewrite, ok := descriptorRewrites[a.TableID] - if !ok { - return errors.Errorf( - "cannot restore table %q without interleave parent %d", table.Name, a.TableID, - ) - } - index.Interleave.Ancestors[j].TableID = ancestorRewrite.ID - } - for j, c := range index.InterleavedBy { - childRewrite, ok := descriptorRewrites[c.Table] - if !ok { - return errors.Errorf( - "cannot restore table %q without interleave child table %d", table.Name, c.Table, - ) - } - index.InterleavedBy[j].Table = childRewrite.ID - } - return nil - }); err != nil { - return err - } - // TODO(lucy): deal with outbound foreign key mutations here as well. origFKs := table.OutboundFKs table.OutboundFKs = nil @@ -1865,8 +1834,7 @@ func doRestorePlan( continue } index := table.GetPrimaryIndex() - if index.IsInterleaved() && - currentVersion.IsActive(clusterversion.PreventNewInterleavedTables) { + if len(index.Interleave.Ancestors) > 0 || len(index.InterleavedBy) > 0 { return errors.Errorf("restoring interleaved tables is no longer allowed. table %s was found to be interleaved", table.Name) } if err := catalog.ForEachNonDropIndex( diff --git a/pkg/ccl/backupccl/targets.go b/pkg/ccl/backupccl/targets.go index 440ea0a121e4..96708de37418 100644 --- a/pkg/ccl/backupccl/targets.go +++ b/pkg/ccl/backupccl/targets.go @@ -228,40 +228,6 @@ func getAllDescChanges( return res, nil } -func ensureInterleavesIncluded(tables []catalog.TableDescriptor) error { - inBackup := make(map[descpb.ID]bool, len(tables)) - for _, t := range tables { - inBackup[t.GetID()] = true - } - - for _, table := range tables { - if err := catalog.ForEachIndex(table, catalog.IndexOpts{ - AddMutations: true, - }, func(index catalog.Index) error { - for i := 0; i < index.NumInterleaveAncestors(); i++ { - a := index.GetInterleaveAncestor(i) - if !inBackup[a.TableID] { - return errors.Errorf( - "cannot backup table %q without interleave parent (ID %d)", table.GetName(), a.TableID, - ) - } - } - for i := 0; i < index.NumInterleavedBy(); i++ { - c := index.GetInterleavedBy(i) - if !inBackup[c.Table] { - return errors.Errorf( - "cannot backup table %q without interleave child table (ID %d)", table.GetName(), c.Table, - ) - } - } - return nil - }); err != nil { - return err - } - } - return nil -} - func lookupDatabaseID( ctx context.Context, txn *kv.Txn, codec keys.SQLCodec, name string, ) (descpb.ID, error) { diff --git a/pkg/ccl/backupccl/testdata/restore_old_versions/create_interleaved.sql b/pkg/ccl/backupccl/testdata/restore_old_versions/create_interleaved.sql deleted file mode 100644 index d9c0c45e8c34..000000000000 --- a/pkg/ccl/backupccl/testdata/restore_old_versions/create_interleaved.sql +++ /dev/null @@ -1,18 +0,0 @@ --- The below SQL is used to create the data that is then exported with BACKUP. --- This should be run on a v21.1, where an interleaved table is intentionally, --- added into the backup - -SET CLUSTER SETTING sql.defaults.interleaved_tables.enabled=yes; - -CREATE DATABASE test; - -SET database = test; - -CREATE TABLE test.customers (id INT PRIMARY KEY, name STRING(50)); - -CREATE TABLE test.orders (customer INT, id INT, total DECIMAL(20, 5), PRIMARY KEY (customer, id), CONSTRAINT fk_customer FOREIGN KEY (customer) REFERENCES customers) INTERLEAVE IN PARENT customers (customer); - -INSERT INTO CUSTOMERS values(1,'BOB'); -INSERT INTO CUSTOMERS values(2,'BILL'); -INSERT INTO ORDERS values(1, 1, 20); -INSERT INTO ORDERS values(2, 2, 30); diff --git a/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/.DS_Store b/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/.DS_Store deleted file mode 100644 index 784da34c9e9aa0cb09151ec01f17d62e90ceeb04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%TB{E5FA4#DmYLss6s-@5437&OX)3rAtKaLDR2fAj+J@oc4o!W8H9=EvTkr)x*vrtPrS%f!aaHp$My z?6c>3Y8HFn>iK;_jlBX8OLSXd zS$+#7Ch!<~OdRqJ#kf$S3pM_VVO%)oQ`XgXzP@@d|CR=Pt(Q_=)fXRZ%ONNV; zgHejH6GC+f@o+E-I7l!UFa(GsFmMHfO`3JG1KlJhOq0~PxPd0I3psiE_%JYXaRE7O zLQeip42(cS*##KDhDrhr_2e)EI&a$2S?Go`YVmL^2RrY^!d=gph;Sdos4M?5jAA0$ zC>^k?mhGB@<|-yeEM{@}fmQcynv15Ikr|7{id^i#z+l$WV1P1g7=(B^n4v+kgg1c! E04&mPj{pDw diff --git a/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST b/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST deleted file mode 100644 index e1539bf581de26e7c5f0e23d2438b7ece8169f60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 787 zcmV+u1MK`CiwFP!00000|E!c>XdG1(#?QGk$=+NOy&an}oe~&VO2>t4=FZMcX4aSO zu1Xt4ViFPZl1z3cb?oj;n4Q>GeF%t9`&3%0MN}kI@Xv!ZPccxHK8a9#>4T37YQ-n@ z$w!%88q-n&f=~By_|CcC`F?O2D2!gb{r(?!-rBi!NTV9L@phTqxKI{s_pTfXgFKjW zJzMyeZ~9J6n3kvsr`qe~mQ0`Kavhpbc~bG2oJYN!+u8&hVM9UKjT257Wpb9LIVVd0 zRI$wH_T~rIFaP}ItwRPjU6K?f6FhBY_~8wkzEnVbaOh!bs;mbldy9bN_<_lQnNb4#qWmc`v5 zJI7JQGKoX8@WCl?&_D+ROz@xqi?G6}%W-e*HJO55%sG#65v8jLn>b8C0Tn($*h1Jr zSSGLRAHfFV`F`&f9{JY`Xe17`{subg#30*yA&1Z)x5Ch!fJ_?Ezr1S%g(Y*S*xX%-eqySi=QeMui< z^zM-T^By1}`$>+np*+ol4wtksD)Axppb4|r#_oqA&$aJ`vg=@g$0QASAR@|!sZ(_P z-(ZZrV0PW~5e+sf80nHB?L+CFSU3M0r~0r08Z@DSGyIn~F3X?3`TWgmFJ1Ytd|3Kw z@e5=lr&3WBPF2`S=~YWqMRi(x)G{qGWqMPluZ!urDt>!tA zUz_%Px8_=w)_u-g@@M*~$5Z;|nL}7f!b3i@N)O`gcg_I7bz9 R_$L4W|No!v2o1#q003%?gaH5m diff --git a/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST-CHECKSUM b/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST-CHECKSUM deleted file mode 100644 index 5edce59c8d62..000000000000 --- a/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST-CHECKSUM +++ /dev/null @@ -1 +0,0 @@ -kLJ \ No newline at end of file diff --git a/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/data/684192909531021315.sst b/pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/data/684192909531021315.sst deleted file mode 100644 index 81a78d641da1b7bee5323455b660ec94385e80e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1009 zcmah|O-NNi6rOp$*Sq(6)6AZMlqhMq$jR)-z=X0g1EDB)6}Nf!9-kNI&fI3^>3h0~ zD6qDHRGXkpJ82Qsrf3muqgK%}ipZ@&L14|ir`9GqaN(Xa=X~co-zBwFw%^j5 z=C8=hyg)6#>W0eeXXxGD)@P{E31w6yT9JfSEHR7aW}KL>vqarlDZ*)GvWm?7kRnwO z5lxE>NX#Rs5M52d!nSY0sf5Y6LMCITLB_n)-`%dC%FFq*PKZWN4a6LBpva6zefRze$2pvEb)9P19C8#Y-n{}Tspr;Kch z>thMjb|N}X068v>sHQ~4b1>&NVql<+EM`iDoDK&nW`0gtpp*`b8bC@+6s-?exUgbb zYtLtAK-zIK#&a5RUAG!2@|tj^jz}2rlAc2DW-MevHCZb{P65|9V8x9UDJzhc0&2=) zOKe9|8=j%8Y!;S3IvNl>Dr>}5*6}m*TJYEX=u<1rwOdJ@LEpV-nvT6 zO<-V;)rAS8?T9GzXrjo@N|7F4KN_voSNEK#34P9X|3}It$E=FZ)_7PEF8kh^2-fSf zy4$ybZhsh9MO!y6cWSyBHfFp<{4=+ab?x(~WxSa0n%ize|1rD8?{B`{czW#P!fz)a BMBe}a diff --git a/pkg/ccl/importccl/csv_internal_test.go b/pkg/ccl/importccl/csv_internal_test.go index 5040d9880e28..49d4da25a2b0 100644 --- a/pkg/ccl/importccl/csv_internal_test.go +++ b/pkg/ccl/importccl/csv_internal_test.go @@ -33,10 +33,6 @@ func TestMakeSimpleTableDescriptorErrors(t *testing.T) { stmt: "create table if not exists a (i int)", error: "unsupported IF NOT EXISTS", }, - { - stmt: "create table a (i int) interleave in parent b (id)", - error: "interleaved not supported", - }, { stmt: "create table a as select 1", error: "CREATE AS not supported", diff --git a/pkg/ccl/importccl/import_planning.go b/pkg/ccl/importccl/import_planning.go index 45cb4ddf6d20..c2a1edb1c6e8 100644 --- a/pkg/ccl/importccl/import_planning.go +++ b/pkg/ccl/importccl/import_planning.go @@ -788,12 +788,6 @@ func importPlanHook( return err } - // IMPORT INTO does not currently support interleaved tables. - if found.IsInterleaved() { - // TODO(miretskiy): Handle import into when tables are interleaved. - return pgerror.New(pgcode.FeatureNotSupported, "Cannot use IMPORT INTO with interleaved tables") - } - // Validate target columns. var intoCols []string var isTargetCol = make(map[string]bool) diff --git a/pkg/ccl/importccl/read_import_mysql_test.go b/pkg/ccl/importccl/read_import_mysql_test.go index 14b9b923703a..010bfeb7b77d 100644 --- a/pkg/ccl/importccl/read_import_mysql_test.go +++ b/pkg/ccl/importccl/read_import_mysql_test.go @@ -231,13 +231,13 @@ func compareTables(t *testing.T, expected, got *descpb.TableDescriptor) { expectedDesc := tabledesc.NewBuilder(expected).BuildImmutableTable() gotDesc := tabledesc.NewBuilder(got).BuildImmutableTable() e, err := catformat.IndexForDisplay( - ctx, expectedDesc, tableName, expectedDesc.PublicNonPrimaryIndexes()[i], "" /* partition */, "" /* interleave */, &semaCtx, + ctx, expectedDesc, tableName, expectedDesc.PublicNonPrimaryIndexes()[i], "" /* partition */, &semaCtx, ) if err != nil { t.Fatalf("unexpected error: %s", err) } g, err := catformat.IndexForDisplay( - ctx, gotDesc, tableName, gotDesc.PublicNonPrimaryIndexes()[i], "" /* partition */, "" /* interleave */, &semaCtx, + ctx, gotDesc, tableName, gotDesc.PublicNonPrimaryIndexes()[i], "" /* partition */, &semaCtx, ) if err != nil { t.Fatalf("unexpected error: %s", err) diff --git a/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index b/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index index 81aaf77cf214..5a955b5554a1 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index +++ b/pkg/ccl/logictestccl/testdata/logic_test/partitioning_index @@ -213,17 +213,6 @@ CREATE TABLE public.t60019 ( ) -- Warning: Partitioned table with no zone configurations. -# Regression test for #60699. Do not allow creation of interleaved partitioned -# indexes. -statement ok -CREATE TABLE t60699_a (a INT PRIMARY KEY); -CREATE TABLE t60699_b (b INT PRIMARY KEY, a INT REFERENCES t60699_a (a)); - -statement error interleaved indexes cannot be partitioned -CREATE INDEX i ON t60699_b (a) INTERLEAVE IN PARENT t60699_a (a) PARTITION BY LIST (a) ( - partition part1 VALUES IN (1) -) - # Regression test for #63733. Scanning a partitioned index should produce rows # where the first index column is NULL. statement ok diff --git a/pkg/cli/testdata/explain-bundle/bundle/env.sql b/pkg/cli/testdata/explain-bundle/bundle/env.sql index 5d506f8e742f..6d5e2a0f845a 100644 --- a/pkg/cli/testdata/explain-bundle/bundle/env.sql +++ b/pkg/cli/testdata/explain-bundle/bundle/env.sql @@ -204,7 +204,6 @@ -- sql.cross_db_fks.enabled = false (if true, creating foreign key references across databases is allowed) -- sql.cross_db_sequence_owners.enabled = false (if true, creating sequences owned by tables from other databases is allowed) -- sql.cross_db_views.enabled = false (if true, creating views that refer to other databases is allowed) --- sql.defaults.copy_partitioning_when_deinterleaving_table.enabled = false (default value for enable_copying_partitioning_when_deinterleaving_table session variable) -- sql.defaults.default_int_size = 8 (the size, in bytes, of an INT type) -- sql.defaults.disallow_full_table_scans.enabled = false (setting to true rejects queries that have planned a full table scan) -- sql.defaults.distsql = auto (default distributed SQL execution mode [off = 0, auto = 1, on = 2]) @@ -223,7 +222,6 @@ -- sql.defaults.idle_in_transaction_session_timeout = 0s (default value for the idle_in_transaction_session_timeout; controls the duration a session is permitted to idle in a transaction before the session is terminated; if set to 0, there is no timeout) -- sql.defaults.implicit_select_for_update.enabled = true (default value for enable_implicit_select_for_update session setting; enables FOR UPDATE locking during the row-fetch phase of mutation statements) -- sql.defaults.insert_fast_path.enabled = true (default value for enable_insert_fast_path session setting; enables a specialized insert path) --- sql.defaults.interleaved_tables.enabled = false (allows creation of interleaved tables or indexes) -- sql.defaults.intervalstyle = postgres (default value for IntervalStyle session setting [postgres = 0, iso_8601 = 1, sql_standard = 2]) -- sql.defaults.intervalstyle.enabled = false (default value for enable_intervalstyle session setting) -- sql.defaults.locality_optimized_partitioned_index_scan.enabled = true (default value for locality_optimized_partitioned_index_scan session setting; enables searching for rows in the current region before searching remote regions) diff --git a/pkg/clusterversion/cockroach_versions.go b/pkg/clusterversion/cockroach_versions.go index 543f40892287..46dedc598e14 100644 --- a/pkg/clusterversion/cockroach_versions.go +++ b/pkg/clusterversion/cockroach_versions.go @@ -213,12 +213,6 @@ const ( // AutoSpanConfigReconciliationJob adds the AutoSpanConfigReconciliationJob // type. AutoSpanConfigReconciliationJob - // PreventNewInterleavedTables interleaved table creation is completely - // blocked on this version. - PreventNewInterleavedTables - // EnsureNoInterleavedTables interleaved tables no longer exist in - // this version. - EnsureNoInterleavedTables // DefaultPrivileges default privileges are supported in this version. DefaultPrivileges // ZonesTableForSecondaryTenants adds system.zones for all secondary tenants. @@ -390,14 +384,6 @@ var versionsSingleton = keyedVersions{ Key: AutoSpanConfigReconciliationJob, Version: roachpb.Version{Major: 21, Minor: 1, Internal: 1136}, }, - { - Key: PreventNewInterleavedTables, - Version: roachpb.Version{Major: 21, Minor: 1, Internal: 1138}, - }, - { - Key: EnsureNoInterleavedTables, - Version: roachpb.Version{Major: 21, Minor: 1, Internal: 1140}, - }, { Key: DefaultPrivileges, Version: roachpb.Version{Major: 21, Minor: 1, Internal: 1142}, diff --git a/pkg/clusterversion/key_string.go b/pkg/clusterversion/key_string.go index 1d2e73449f2f..30fc96c9a39a 100644 --- a/pkg/clusterversion/key_string.go +++ b/pkg/clusterversion/key_string.go @@ -27,31 +27,29 @@ func _() { _ = x[RetryJobsWithExponentialBackoff-16] _ = x[RecordsBasedRegistry-17] _ = x[AutoSpanConfigReconciliationJob-18] - _ = x[PreventNewInterleavedTables-19] - _ = x[EnsureNoInterleavedTables-20] - _ = x[DefaultPrivileges-21] - _ = x[ZonesTableForSecondaryTenants-22] - _ = x[UseKeyEncodeForHashShardedIndexes-23] - _ = x[DatabasePlacementPolicy-24] - _ = x[GeneratedAsIdentity-25] - _ = x[OnUpdateExpressions-26] - _ = x[SpanConfigurationsTable-27] - _ = x[BoundedStaleness-28] - _ = x[DateAndIntervalStyle-29] - _ = x[PebbleFormatVersioned-30] - _ = x[MarkerDataKeysRegistry-31] - _ = x[PebbleSetWithDelete-32] - _ = x[TenantUsageSingleConsumptionColumn-33] - _ = x[SQLStatsTables-34] - _ = x[SQLStatsCompactionScheduledJob-35] - _ = x[V21_2-36] - _ = x[Start22_1-37] - _ = x[TargetBytesAvoidExcess-38] + _ = x[DefaultPrivileges-19] + _ = x[ZonesTableForSecondaryTenants-20] + _ = x[UseKeyEncodeForHashShardedIndexes-21] + _ = x[DatabasePlacementPolicy-22] + _ = x[GeneratedAsIdentity-23] + _ = x[OnUpdateExpressions-24] + _ = x[SpanConfigurationsTable-25] + _ = x[BoundedStaleness-26] + _ = x[DateAndIntervalStyle-27] + _ = x[PebbleFormatVersioned-28] + _ = x[MarkerDataKeysRegistry-29] + _ = x[PebbleSetWithDelete-30] + _ = x[TenantUsageSingleConsumptionColumn-31] + _ = x[SQLStatsTables-32] + _ = x[SQLStatsCompactionScheduledJob-33] + _ = x[V21_2-34] + _ = x[Start22_1-35] + _ = x[TargetBytesAvoidExcess-36] } -const _Key_name = "V21_1Start21_1PLUSStart21_2JoinTokensTableAcquisitionTypeInLeaseHistorySerializeViewUDTsExpressionIndexesDeleteDeprecatedNamespaceTableDescriptorMigrationFixDescriptorsDatabaseRoleSettingsTenantUsageTableSQLInstancesTableNewRetryableRangefeedErrorsAlterSystemWebSessionsCreateIndexesSeparatedIntentsMigrationPostSeparatedIntentsMigrationRetryJobsWithExponentialBackoffRecordsBasedRegistryAutoSpanConfigReconciliationJobPreventNewInterleavedTablesEnsureNoInterleavedTablesDefaultPrivilegesZonesTableForSecondaryTenantsUseKeyEncodeForHashShardedIndexesDatabasePlacementPolicyGeneratedAsIdentityOnUpdateExpressionsSpanConfigurationsTableBoundedStalenessDateAndIntervalStylePebbleFormatVersionedMarkerDataKeysRegistryPebbleSetWithDeleteTenantUsageSingleConsumptionColumnSQLStatsTablesSQLStatsCompactionScheduledJobV21_2Start22_1TargetBytesAvoidExcess" +const _Key_name = "V21_1Start21_1PLUSStart21_2JoinTokensTableAcquisitionTypeInLeaseHistorySerializeViewUDTsExpressionIndexesDeleteDeprecatedNamespaceTableDescriptorMigrationFixDescriptorsDatabaseRoleSettingsTenantUsageTableSQLInstancesTableNewRetryableRangefeedErrorsAlterSystemWebSessionsCreateIndexesSeparatedIntentsMigrationPostSeparatedIntentsMigrationRetryJobsWithExponentialBackoffRecordsBasedRegistryAutoSpanConfigReconciliationJobDefaultPrivilegesZonesTableForSecondaryTenantsUseKeyEncodeForHashShardedIndexesDatabasePlacementPolicyGeneratedAsIdentityOnUpdateExpressionsSpanConfigurationsTableBoundedStalenessDateAndIntervalStylePebbleFormatVersionedMarkerDataKeysRegistryPebbleSetWithDeleteTenantUsageSingleConsumptionColumnSQLStatsTablesSQLStatsCompactionScheduledJobV21_2Start22_1TargetBytesAvoidExcess" -var _Key_index = [...]uint16{0, 5, 18, 27, 42, 71, 88, 105, 154, 168, 188, 204, 221, 248, 283, 308, 337, 368, 388, 419, 446, 471, 488, 517, 550, 573, 592, 611, 634, 650, 670, 691, 713, 732, 766, 780, 810, 815, 824, 846} +var _Key_index = [...]uint16{0, 5, 18, 27, 42, 71, 88, 105, 154, 168, 188, 204, 221, 248, 283, 308, 337, 368, 388, 419, 436, 465, 498, 521, 540, 559, 582, 598, 618, 639, 661, 680, 714, 728, 758, 763, 772, 794} func (i Key) String() string { if i < 0 || i >= Key(len(_Key_index)-1) { diff --git a/pkg/jobs/jobspb/jobs.pb.go b/pkg/jobs/jobspb/jobs.pb.go index cb7caa47ac35..6581fcc3ae38 100644 --- a/pkg/jobs/jobspb/jobs.pb.go +++ b/pkg/jobs/jobspb/jobs.pb.go @@ -1601,11 +1601,6 @@ var xxx_messageInfo_DroppedTableDetails proto.InternalMessageInfo type SchemaChangeGCDetails struct { // Indexes to GC. Indexes []SchemaChangeGCDetails_DroppedIndex `protobuf:"bytes,1,rep,name=indexes,proto3" json:"indexes"` - // InterleavedTable is the table being truncated. In particular, it is the - // TableDescriptor before any of the truncate modifications have been applied. - InterleavedTable *descpb.TableDescriptor `protobuf:"bytes,4,opt,name=interleaved_table,json=interleavedTable,proto3" json:"interleaved_table,omitempty"` - // InterleavedIndexes is the set of interleaved indexes to truncate. - InterleavedIndexes []descpb.IndexDescriptor `protobuf:"bytes,5,rep,name=interleaved_indexes,json=interleavedIndexes,proto3" json:"interleaved_indexes"` // Entire tables to GC. Tables []SchemaChangeGCDetails_DroppedID `protobuf:"bytes,2,rep,name=tables,proto3" json:"tables"` // If dropping indexes, the table ID which has those indexes. If dropping a @@ -3183,390 +3178,387 @@ func init() { func init() { proto.RegisterFile("jobs/jobspb/jobs.proto", fileDescriptor_6c315f3a2536c4ef) } var fileDescriptor_6c315f3a2536c4ef = []byte{ - // 6118 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x7c, 0x5b, 0x6c, 0x1b, 0xd9, - 0x7d, 0xb7, 0x86, 0xa2, 0x78, 0xf9, 0x8b, 0xa4, 0x86, 0x47, 0xb2, 0x44, 0x33, 0x5e, 0x91, 0xcb, - 0xb5, 0x77, 0xed, 0x4d, 0x4c, 0xed, 0x7a, 0x93, 0xcd, 0xae, 0x93, 0xf5, 0x2e, 0x6f, 0x92, 0x28, - 0xeb, 0xe6, 0xa1, 0xe4, 0xbd, 0x7d, 0x9b, 0xf9, 0x86, 0x9c, 0x23, 0x6a, 0x3e, 0x91, 0x33, 0xf4, - 0x9c, 0xa1, 0x6d, 0x25, 0x40, 0x10, 0x24, 0x5f, 0x80, 0xc2, 0xe8, 0x43, 0x0a, 0xb4, 0x7d, 0x69, - 0x5d, 0x14, 0x4d, 0x02, 0xf4, 0xa1, 0x41, 0xd1, 0xa0, 0x68, 0xfb, 0xd8, 0xc7, 0x3c, 0xa4, 0x45, - 0x90, 0xa2, 0x68, 0xda, 0x07, 0xa5, 0x55, 0x5e, 0xfa, 0xd0, 0x87, 0xa2, 0x8f, 0x7e, 0x2a, 0xce, - 0x65, 0x86, 0x43, 0x8a, 0xa2, 0x28, 0xd9, 0x49, 0x5e, 0x64, 0xce, 0xff, 0x9c, 0xf3, 0x3b, 0x97, - 0xf9, 0x9f, 0xff, 0x7d, 0x0c, 0xf3, 0xff, 0xcf, 0xaa, 0x93, 0x25, 0xfa, 0xa7, 0x53, 0x67, 0xff, - 0xe4, 0x3b, 0xb6, 0xe5, 0x58, 0xe8, 0x72, 0xc3, 0x6a, 0x1c, 0xd8, 0x96, 0xd6, 0xd8, 0xcf, 0x93, - 0x07, 0xad, 0x3c, 0x6b, 0xe1, 0xbd, 0xd2, 0x97, 0xb0, 0x6d, 0x5b, 0x36, 0xed, 0xcf, 0x7f, 0xf0, - 0x11, 0xe9, 0xb9, 0xa6, 0xd5, 0xb4, 0xd8, 0xcf, 0x25, 0xfa, 0x4b, 0x50, 0x93, 0x0c, 0xa3, 0x53, - 0x5f, 0xd2, 0x3a, 0x86, 0x20, 0x21, 0x97, 0xa4, 0x6b, 0x8e, 0x26, 0x68, 0x29, 0x97, 0x66, 0x58, - 0x37, 0xf7, 0x2c, 0xbb, 0xad, 0x39, 0x2e, 0xec, 0x2b, 0xe4, 0x41, 0x6b, 0xa9, 0xa1, 0x39, 0x5a, - 0xcb, 0x6a, 0x2e, 0xe9, 0x98, 0x34, 0x3a, 0xf5, 0x25, 0xe2, 0xd8, 0xdd, 0x86, 0xd3, 0xb5, 0xb1, - 0x2e, 0x3a, 0x65, 0x86, 0x74, 0x72, 0xb0, 0xa9, 0x99, 0x8e, 0x8b, 0xdf, 0x75, 0x8c, 0xd6, 0xd2, - 0x7e, 0xab, 0xb1, 0xe4, 0x18, 0x6d, 0x4c, 0x1c, 0xad, 0xdd, 0x11, 0x2d, 0x2f, 0xd3, 0xa1, 0xa4, - 0xb1, 0x8f, 0xdb, 0x5a, 0x63, 0x5f, 0x33, 0x9b, 0xd8, 0x5e, 0xe2, 0x73, 0x34, 0x3a, 0x75, 0xd1, - 0xe5, 0x6a, 0xa3, 0xd5, 0x25, 0x0e, 0xb6, 0x1f, 0x62, 0x9b, 0x18, 0x96, 0xb9, 0x24, 0x1e, 0x55, - 0xf1, 0xec, 0xae, 0xa1, 0x69, 0x59, 0xcd, 0x16, 0x5e, 0x62, 0x4f, 0xf5, 0xee, 0xde, 0xe0, 0x4c, - 0xb9, 0x1f, 0x06, 0x60, 0xa1, 0xa8, 0x35, 0x0e, 0xba, 0x9d, 0x8a, 0xd9, 0xb0, 0x0f, 0x3b, 0x8e, - 0x61, 0x99, 0x5b, 0xec, 0x2f, 0x41, 0x32, 0x4c, 0x1e, 0xe0, 0xc3, 0x94, 0x94, 0x95, 0xae, 0xc7, - 0x14, 0xfa, 0x13, 0xbd, 0x07, 0xc1, 0xb6, 0xa5, 0xe3, 0x54, 0x20, 0x2b, 0x5d, 0x4f, 0xdc, 0xba, - 0x91, 0x3f, 0xf5, 0x7d, 0xe4, 0x7b, 0x68, 0x1b, 0x96, 0x8e, 0x15, 0x36, 0x0c, 0xd5, 0x21, 0x72, - 0xd0, 0x26, 0xaa, 0x61, 0xee, 0x59, 0xa9, 0xc9, 0xac, 0x74, 0x7d, 0xfa, 0xd6, 0xed, 0x11, 0x10, - 0xa7, 0x2c, 0x2b, 0x7f, 0x77, 0xa3, 0x56, 0x35, 0xf7, 0xac, 0xe2, 0xf4, 0xf1, 0x51, 0x26, 0x2c, - 0x1e, 0x94, 0xf0, 0x41, 0x9b, 0xd0, 0x1f, 0xe9, 0x2d, 0x70, 0x69, 0x74, 0xfd, 0x5d, 0xdb, 0x60, - 0xeb, 0x8f, 0x2a, 0xf4, 0x27, 0xfa, 0x02, 0x20, 0xcc, 0xf1, 0xb0, 0xae, 0xd2, 0x37, 0xad, 0xd2, - 0x0d, 0x06, 0xd8, 0x06, 0x65, 0xaf, 0xa5, 0xac, 0x39, 0xda, 0x5d, 0x7c, 0x78, 0x3b, 0xf8, 0x9f, - 0x7f, 0x9a, 0x91, 0xf8, 0xdf, 0xdc, 0xb7, 0x26, 0x21, 0xd1, 0x5b, 0x0a, 0x83, 0x5f, 0x85, 0x10, - 0x7b, 0x45, 0x98, 0xcd, 0x90, 0xb8, 0xf5, 0xc6, 0x58, 0xc7, 0x41, 0x87, 0xe6, 0x6b, 0x6c, 0x9c, - 0x22, 0xc6, 0x23, 0x04, 0x41, 0xa2, 0xb5, 0x1c, 0xb1, 0x10, 0xf6, 0x1b, 0xfd, 0x91, 0x04, 0xd9, - 0xc1, 0x15, 0x15, 0x0f, 0xef, 0x6e, 0xd4, 0x36, 0x34, 0xfa, 0x9e, 0xef, 0xe2, 0xc3, 0x6a, 0x39, - 0x35, 0x99, 0x9d, 0xbc, 0x3e, 0x7d, 0x6b, 0x6b, 0xfc, 0x89, 0x2b, 0x67, 0x20, 0x56, 0x4c, 0xc7, - 0x3e, 0x54, 0xce, 0x9c, 0x38, 0x5d, 0x83, 0x6b, 0x63, 0x41, 0xf9, 0x79, 0x28, 0xca, 0x79, 0x68, - 0x0e, 0xa6, 0x1e, 0x6a, 0xad, 0x2e, 0x16, 0xbb, 0xe5, 0x0f, 0xb7, 0x03, 0xef, 0x48, 0xb9, 0x05, - 0x08, 0xf1, 0x83, 0x41, 0x71, 0x88, 0x16, 0x2a, 0xb5, 0x5b, 0x5f, 0x7a, 0x7b, 0xa5, 0xb4, 0x21, - 0x4f, 0x88, 0x57, 0xf0, 0xbb, 0x01, 0x98, 0xaf, 0x39, 0x36, 0xd6, 0xda, 0x55, 0xb3, 0x89, 0x09, - 0xdd, 0x53, 0x19, 0x3b, 0x9a, 0xd1, 0x22, 0xe8, 0x1a, 0x24, 0x08, 0x6b, 0x51, 0x35, 0x5d, 0xb7, - 0x31, 0x21, 0x62, 0xc2, 0x38, 0xa7, 0x16, 0x38, 0x11, 0xdd, 0x80, 0xa8, 0xe8, 0x66, 0xe8, 0xa9, - 0x60, 0x56, 0xba, 0x1e, 0x2c, 0xc6, 0x8e, 0x8f, 0x32, 0x11, 0x81, 0x5a, 0x56, 0x22, 0xbc, 0xb9, - 0xaa, 0xa3, 0x37, 0x21, 0x48, 0x3a, 0x9a, 0xc9, 0x16, 0x39, 0x7d, 0x6b, 0xc1, 0x77, 0xc2, 0x42, - 0x28, 0xe4, 0x6b, 0x1d, 0xcd, 0x2c, 0x06, 0x7f, 0x72, 0x94, 0x99, 0x50, 0x58, 0x57, 0x54, 0x04, - 0x20, 0x8e, 0x66, 0x3b, 0x2a, 0xbd, 0x63, 0x82, 0xbf, 0x5f, 0xf2, 0x0d, 0xa4, 0xb7, 0x3d, 0xbf, - 0xdf, 0x6a, 0xe4, 0x77, 0xdc, 0x3b, 0x28, 0x86, 0x47, 0xd9, 0x30, 0x4a, 0xa5, 0x2b, 0xe4, 0x22, - 0x82, 0xae, 0x70, 0xaa, 0xb7, 0xc2, 0x1d, 0x46, 0xa4, 0x2b, 0xe4, 0xcd, 0x55, 0x3d, 0xf7, 0x2f, - 0x93, 0xb0, 0x30, 0x70, 0x1c, 0xdb, 0xb6, 0xd5, 0x64, 0x1b, 0x5d, 0x86, 0x58, 0xa3, 0xeb, 0x58, - 0x0f, 0xb1, 0xcd, 0x17, 0x23, 0x8d, 0xbf, 0x98, 0x69, 0x31, 0x90, 0x2d, 0xe7, 0x9b, 0x80, 0x3a, - 0x9a, 0xed, 0x18, 0x14, 0x5c, 0xed, 0x08, 0xf4, 0x54, 0x80, 0x71, 0x5d, 0x75, 0x04, 0xd7, 0x9d, - 0xb2, 0xae, 0xfc, 0xb6, 0x0b, 0xe6, 0x52, 0x18, 0x93, 0x88, 0x99, 0x93, 0x9d, 0xc1, 0xd6, 0x74, - 0x13, 0x92, 0x27, 0x86, 0x20, 0x05, 0x90, 0xc1, 0x90, 0xb1, 0xae, 0x7a, 0xe2, 0xec, 0x3c, 0x5b, - 0x4c, 0xba, 0xc3, 0xbd, 0x86, 0xf4, 0x13, 0x09, 0xe6, 0x87, 0x2f, 0x6e, 0x08, 0x07, 0x7f, 0xe2, - 0xe7, 0xe0, 0xe9, 0x5b, 0xe5, 0x17, 0x71, 0x10, 0xfe, 0x7b, 0x50, 0x85, 0x14, 0x1f, 0xa7, 0xe0, - 0x4e, 0xcb, 0x68, 0x68, 0x7e, 0x4e, 0xbf, 0x09, 0x53, 0x94, 0xd9, 0x28, 0x83, 0x4f, 0x8e, 0x60, - 0x4c, 0x85, 0xf7, 0xca, 0x69, 0x70, 0xf9, 0x04, 0x94, 0x77, 0x90, 0x65, 0x00, 0xfc, 0xb8, 0x63, - 0xd8, 0x8c, 0x2a, 0x0e, 0x30, 0x9d, 0xe7, 0x1a, 0x23, 0xef, 0x6a, 0x0c, 0xdf, 0xe9, 0x45, 0xe8, - 0xe9, 0x7d, 0xef, 0x97, 0x19, 0x49, 0xf1, 0x8d, 0xcb, 0xfd, 0x28, 0x00, 0x97, 0xe9, 0xb5, 0xd5, - 0xbb, 0x2d, 0xbc, 0xbd, 0x53, 0x2b, 0xed, 0x6b, 0x86, 0x69, 0x98, 0x4d, 0x05, 0x37, 0x2c, 0x5b, - 0x47, 0xbf, 0x27, 0x41, 0x9a, 0x42, 0xe1, 0x46, 0xdf, 0xeb, 0x52, 0x6d, 0xd6, 0xcc, 0x75, 0x4b, - 0xb1, 0xf6, 0x6f, 0x47, 0x99, 0xb7, 0x9a, 0x86, 0xb3, 0xdf, 0xad, 0xe7, 0x1b, 0x56, 0x7b, 0xc9, - 0xdb, 0x93, 0x5e, 0xef, 0xfd, 0x5e, 0xea, 0x1c, 0x34, 0x97, 0x98, 0xc6, 0xec, 0x76, 0x0d, 0x3d, - 0xbf, 0xbb, 0x5b, 0x2d, 0x1f, 0x1f, 0x65, 0x52, 0xdb, 0x2e, 0xb8, 0xb7, 0x4e, 0x3e, 0xb3, 0x92, - 0xea, 0x9c, 0xd2, 0x82, 0xee, 0x43, 0x48, 0x6b, 0xb0, 0x3d, 0x73, 0x3d, 0x76, 0x67, 0xd4, 0x0b, - 0x3c, 0x6d, 0x67, 0xf9, 0xed, 0x9d, 0x5a, 0x81, 0xa1, 0x28, 0x02, 0x2d, 0x77, 0x15, 0xa2, 0x1e, - 0x11, 0x01, 0x84, 0x76, 0xb7, 0xcb, 0x85, 0x9d, 0x8a, 0x3c, 0x81, 0xa6, 0x21, 0xac, 0x54, 0xd6, - 0x2b, 0x85, 0x5a, 0x45, 0x96, 0x72, 0xff, 0x14, 0x86, 0x38, 0x57, 0x6d, 0xee, 0x3b, 0xed, 0x17, - 0x1c, 0xd2, 0x85, 0x04, 0xc7, 0x1d, 0x88, 0x60, 0x93, 0x1f, 0xb0, 0x60, 0xcb, 0xb1, 0x10, 0xc2, - 0xd8, 0x64, 0xc7, 0x83, 0x2e, 0x73, 0x5d, 0x49, 0xa5, 0x56, 0xb4, 0x18, 0x3e, 0x3e, 0xca, 0x4c, - 0xee, 0x2a, 0x55, 0xae, 0x34, 0xbf, 0x23, 0xc1, 0x6c, 0xd7, 0x36, 0x88, 0x5a, 0x3f, 0x54, 0x5b, - 0x56, 0x43, 0x6b, 0x19, 0xce, 0xa1, 0x7a, 0xf0, 0x30, 0x35, 0xc5, 0x38, 0xf0, 0xce, 0x99, 0x1a, - 0x5c, 0x6c, 0x33, 0xbf, 0x6b, 0x1b, 0xa4, 0x78, 0xb8, 0x2e, 0x10, 0xee, 0x3e, 0xe4, 0x77, 0x7f, - 0xee, 0xf8, 0x28, 0x23, 0xef, 0x2a, 0x55, 0x7f, 0xd3, 0x7d, 0x45, 0xee, 0x0e, 0x74, 0x46, 0x5f, - 0x85, 0xb4, 0x8e, 0x3b, 0x36, 0x6e, 0x68, 0x94, 0x91, 0xea, 0x0c, 0x59, 0x6d, 0x6b, 0xa6, 0xb1, - 0x87, 0x89, 0xc3, 0x84, 0x79, 0x4c, 0x49, 0xf5, 0x7a, 0xf0, 0xa9, 0x37, 0x44, 0x3b, 0xd2, 0x3c, - 0xc5, 0x4f, 0x25, 0x99, 0xc5, 0x2d, 0x89, 0x54, 0x88, 0x1d, 0xd4, 0xad, 0xf3, 0xdb, 0x20, 0x4a, - 0x12, 0x9f, 0xb0, 0x96, 0x14, 0x98, 0xf1, 0x4d, 0xc1, 0x6c, 0x9c, 0x28, 0xc3, 0xbf, 0x31, 0xb6, - 0x7a, 0x56, 0x12, 0xb8, 0xdf, 0xc4, 0x38, 0xe3, 0xf6, 0x84, 0x7f, 0x1b, 0xb7, 0xe7, 0x1d, 0x48, - 0x34, 0xac, 0x56, 0x0b, 0x33, 0x36, 0x57, 0x77, 0x95, 0x6a, 0x2a, 0xc2, 0x98, 0x26, 0x79, 0x7c, - 0x94, 0x89, 0x97, 0xbc, 0x16, 0xca, 0x3e, 0xf1, 0x86, 0xff, 0x11, 0xfd, 0xbe, 0x04, 0x57, 0x88, - 0xb8, 0x4f, 0x6a, 0xc7, 0x21, 0x6a, 0x43, 0xdc, 0x28, 0x77, 0x3f, 0xc0, 0xce, 0xeb, 0x8b, 0x17, - 0xb9, 0x8e, 0xc5, 0x97, 0x8e, 0x8f, 0x32, 0xa7, 0xcb, 0x21, 0xe5, 0xb2, 0x3b, 0xf1, 0xb6, 0x43, - 0xfa, 0x9b, 0xd2, 0x25, 0xb8, 0x34, 0x94, 0x35, 0xcf, 0xb2, 0x5d, 0xa2, 0x7e, 0x99, 0x2d, 0x43, - 0x82, 0xf3, 0x8a, 0x2b, 0x5d, 0x73, 0x7f, 0xb2, 0x00, 0x09, 0x05, 0x13, 0xc7, 0xb2, 0xb1, 0x7b, - 0xd1, 0xfd, 0x97, 0x34, 0x78, 0x81, 0x4b, 0xfa, 0x63, 0x09, 0x66, 0xa9, 0x23, 0x61, 0x1b, 0x1d, - 0xc7, 0xb2, 0x55, 0x1b, 0x3f, 0xb2, 0x0d, 0x07, 0xbb, 0x0a, 0xb9, 0x30, 0xe2, 0xdc, 0xfa, 0x17, - 0x92, 0x2f, 0x7b, 0x20, 0x8a, 0xc0, 0xe0, 0x97, 0xf1, 0xce, 0xb7, 0x7f, 0x99, 0xb9, 0x3d, 0x16, - 0x2b, 0x9d, 0xf4, 0x6d, 0xf2, 0xd5, 0xb2, 0x82, 0xf4, 0x13, 0xc0, 0xe8, 0x0a, 0x04, 0xe9, 0x65, - 0x66, 0xb6, 0x6a, 0xb4, 0x18, 0x39, 0x3e, 0xca, 0x04, 0xe9, 0x75, 0x57, 0x18, 0x15, 0x39, 0x30, - 0x27, 0xee, 0xb2, 0x27, 0x5a, 0xd8, 0xd5, 0x09, 0xb3, 0x2d, 0x7d, 0x75, 0xfc, 0x2d, 0xf1, 0xd3, - 0x77, 0x5f, 0x21, 0x73, 0x10, 0xf8, 0xe9, 0xa1, 0xfa, 0x89, 0x16, 0xb4, 0x0d, 0x09, 0x6a, 0xfd, - 0xd7, 0x35, 0x82, 0x55, 0xba, 0x64, 0x92, 0x92, 0xd9, 0x7c, 0x83, 0x57, 0x95, 0x3c, 0x68, 0xd1, - 0x3e, 0xf9, 0xb2, 0xe8, 0xec, 0x3b, 0xb7, 0xb8, 0xee, 0xa3, 0x11, 0xb4, 0x02, 0xd3, 0x8e, 0x56, - 0x6f, 0xb9, 0x70, 0x5c, 0x36, 0xbe, 0x7a, 0x0a, 0xdc, 0x0e, 0xed, 0xe9, 0xc3, 0x02, 0xc7, 0x25, - 0x30, 0xa5, 0xec, 0x1c, 0x76, 0x5c, 0x9c, 0x04, 0xc3, 0xb9, 0x76, 0x1a, 0xce, 0x61, 0xc7, 0x0f, - 0x13, 0x75, 0xc4, 0x33, 0x41, 0x6b, 0x10, 0xe3, 0xee, 0xa3, 0xc0, 0x99, 0x61, 0x38, 0xaf, 0x9d, - 0x82, 0xc3, 0xac, 0x6e, 0xcd, 0x87, 0x34, 0x4d, 0x3c, 0x0a, 0xc5, 0x0a, 0x73, 0xa3, 0x93, 0xa4, - 0x2e, 0x31, 0x98, 0xd7, 0x4f, 0x5b, 0x0e, 0x37, 0x4d, 0xcd, 0x3d, 0xeb, 0x43, 0xc3, 0xd9, 0xdf, - 0x25, 0x5a, 0x13, 0xbb, 0x1c, 0x2c, 0x00, 0xd0, 0x12, 0x4c, 0x53, 0xe3, 0xd2, 0x36, 0x74, 0xac, - 0xea, 0x75, 0x26, 0x80, 0xa3, 0xc5, 0xc4, 0xf1, 0x51, 0x06, 0xb6, 0x04, 0xb9, 0x5c, 0x54, 0xc0, - 0xed, 0x52, 0xae, 0xa3, 0xcf, 0x43, 0xb2, 0x63, 0xe3, 0x8e, 0x66, 0x63, 0xb5, 0x61, 0xb5, 0x3b, - 0x2d, 0xec, 0x60, 0x9d, 0x09, 0x9c, 0x88, 0x22, 0x8b, 0x86, 0x92, 0x4b, 0xe7, 0x6e, 0x80, 0xe6, - 0x50, 0x0f, 0x93, 0x60, 0x9b, 0xf6, 0x8c, 0xb2, 0x9e, 0x71, 0x46, 0xad, 0x0a, 0x22, 0x3a, 0x84, - 0x79, 0x72, 0x48, 0x1c, 0xdc, 0x56, 0xd9, 0xb9, 0x13, 0xb5, 0x6d, 0x34, 0x6d, 0xaa, 0x34, 0x52, - 0x49, 0xb6, 0xbf, 0xd2, 0xf8, 0x5c, 0x57, 0x63, 0x38, 0xec, 0x7d, 0x92, 0x0d, 0x81, 0xc2, 0x7d, - 0xa8, 0x39, 0x32, 0xa4, 0x09, 0xbd, 0x05, 0x97, 0x7a, 0x57, 0x84, 0xa8, 0x9d, 0x6e, 0xbd, 0x65, - 0x90, 0x7d, 0xcc, 0x45, 0x5f, 0x44, 0x99, 0xf3, 0x35, 0x6e, 0xbb, 0x6d, 0xe8, 0xb0, 0xef, 0xd6, - 0x37, 0xe8, 0xe9, 0x68, 0x4d, 0x9c, 0x9a, 0xce, 0x4a, 0xd7, 0xa7, 0x8a, 0xab, 0xcf, 0x8e, 0x32, - 0xe5, 0xb1, 0xaf, 0x2c, 0xc1, 0xed, 0x25, 0xc7, 0xc6, 0xd8, 0x27, 0x01, 0x4a, 0x02, 0xcf, 0x7f, - 0x79, 0x5d, 0x1a, 0x52, 0x00, 0x7a, 0x2a, 0x29, 0x15, 0xbb, 0xb0, 0xbe, 0xf4, 0xa1, 0x20, 0x13, - 0x90, 0x8d, 0x1f, 0x6a, 0x2d, 0x43, 0xd7, 0x1c, 0xac, 0x1a, 0xa6, 0x8e, 0x1f, 0x63, 0x92, 0x42, - 0xec, 0xe8, 0xdf, 0x1d, 0xff, 0xe8, 0x15, 0x0f, 0xa3, 0x4a, 0x21, 0x5c, 0xdb, 0xde, 0xee, 0x27, - 0x63, 0x82, 0xfe, 0x52, 0x02, 0xe4, 0xdd, 0xf6, 0xb6, 0xa5, 0x1b, 0x7b, 0x06, 0xb6, 0x49, 0x6a, - 0x96, 0x4d, 0xf8, 0xc1, 0x39, 0x84, 0xa6, 0xc0, 0xd8, 0x70, 0x21, 0x5e, 0x8c, 0xcc, 0x4c, 0xea, - 0x83, 0xb8, 0xe8, 0x2a, 0x24, 0x74, 0x5c, 0xef, 0x36, 0xd5, 0x8e, 0xd6, 0x25, 0x58, 0xb5, 0xcc, - 0xd4, 0x1c, 0xd3, 0x37, 0x31, 0x46, 0xdd, 0xa6, 0xc4, 0x2d, 0x33, 0xfd, 0x67, 0x01, 0x48, 0x9e, - 0x10, 0xe4, 0x68, 0x07, 0x02, 0x06, 0xb7, 0xab, 0xe3, 0x45, 0xaa, 0xe2, 0x03, 0xd5, 0xf2, 0xb3, - 0xa3, 0xe7, 0x5a, 0x60, 0xc0, 0xd0, 0x51, 0x13, 0xa2, 0xf4, 0xaa, 0x71, 0xbf, 0x34, 0xc0, 0xc0, - 0xd7, 0xa8, 0x5f, 0xba, 0xcd, 0x88, 0xcf, 0x3d, 0x45, 0x84, 0x83, 0x57, 0x75, 0x94, 0x81, 0x69, - 0xc7, 0x52, 0xf1, 0x63, 0x83, 0x38, 0x86, 0xd9, 0x64, 0xf6, 0x68, 0x44, 0x01, 0xc7, 0xaa, 0x08, - 0x0a, 0xba, 0x09, 0xd3, 0x26, 0x7e, 0xa4, 0xea, 0x75, 0xd5, 0xd4, 0x84, 0x1a, 0x8d, 0x16, 0xe3, - 0xc7, 0x47, 0x99, 0xe8, 0x26, 0x7e, 0x54, 0x2e, 0x6e, 0x6a, 0x6d, 0xac, 0x44, 0x4d, 0xfc, 0xa8, - 0x5c, 0xa7, 0x3f, 0xd3, 0x7f, 0x1c, 0x00, 0x74, 0x52, 0x35, 0xa0, 0xbf, 0x93, 0xe0, 0x8a, 0x6b, - 0xd3, 0x5a, 0xb6, 0xd1, 0x34, 0x4c, 0xad, 0xd5, 0x67, 0xdc, 0x72, 0xf7, 0xea, 0x93, 0xe7, 0xd1, - 0x3f, 0xc2, 0xe0, 0xdd, 0x12, 0xf0, 0x83, 0x86, 0xef, 0x15, 0x6a, 0x7f, 0x71, 0xc3, 0xf7, 0x44, - 0x97, 0xfb, 0x4a, 0xaa, 0x7b, 0xca, 0xe0, 0xf4, 0x5d, 0x78, 0x69, 0x24, 0xf0, 0x79, 0xcc, 0x96, - 0xf4, 0xb7, 0x25, 0x58, 0x38, 0xc5, 0x18, 0xf0, 0xe3, 0xc4, 0x39, 0xce, 0xbd, 0x7e, 0xc7, 0xf7, - 0x2b, 0xcf, 0x61, 0x70, 0xf8, 0x17, 0xb1, 0x02, 0x97, 0x4f, 0x95, 0xa3, 0x67, 0xed, 0x26, 0xe2, - 0x07, 0xfa, 0x57, 0x09, 0x66, 0x06, 0xc4, 0x02, 0xfa, 0xd8, 0x77, 0x1f, 0xaa, 0xc7, 0x47, 0x99, - 0x30, 0x9b, 0xe4, 0x85, 0x5c, 0x8a, 0x83, 0x93, 0x97, 0x62, 0x93, 0xce, 0xc0, 0x26, 0x66, 0x33, - 0xbc, 0x7f, 0xe1, 0x19, 0x38, 0x44, 0xef, 0x62, 0xa4, 0xff, 0x5e, 0x02, 0x79, 0x50, 0x02, 0xa1, - 0x2d, 0x90, 0xf1, 0x63, 0xc7, 0xd6, 0x54, 0x9f, 0xc9, 0x20, 0x9d, 0xc7, 0x64, 0x48, 0xb0, 0xe1, - 0x3b, 0x9e, 0xdd, 0xf0, 0x29, 0xc4, 0x6d, 0xdc, 0xa4, 0x86, 0x7d, 0xc3, 0x32, 0xf7, 0x8c, 0xa6, - 0x78, 0xd3, 0x6f, 0x8f, 0x6d, 0x17, 0xe5, 0x15, 0x36, 0xbc, 0xc4, 0x46, 0x2b, 0x31, 0xdb, 0xf7, - 0x94, 0xfe, 0x96, 0x04, 0xf3, 0xc3, 0x85, 0xe8, 0x10, 0x5e, 0xdb, 0xee, 0xe7, 0xb5, 0xdb, 0x17, - 0x97, 0xd3, 0x3e, 0x0e, 0x59, 0x0b, 0x46, 0x24, 0x39, 0xb0, 0x16, 0x8c, 0xc4, 0xe5, 0x44, 0xee, - 0x0d, 0xca, 0x2c, 0x6c, 0xa4, 0x17, 0x11, 0x79, 0x09, 0x60, 0xdf, 0x68, 0xee, 0xab, 0x8f, 0x34, - 0x07, 0xdb, 0x22, 0xf0, 0x1d, 0xa5, 0x94, 0x0f, 0x29, 0x21, 0xf7, 0xf3, 0x18, 0xc4, 0xab, 0xed, - 0x8e, 0x65, 0x3b, 0xae, 0x45, 0xbf, 0x0e, 0x21, 0x6e, 0x43, 0x88, 0x63, 0xcf, 0x8f, 0x58, 0x66, - 0xdf, 0x48, 0x6e, 0x03, 0x0a, 0xa5, 0x25, 0x30, 0xd0, 0x16, 0x84, 0xb9, 0xe1, 0x45, 0x52, 0x0b, - 0x0c, 0x6e, 0x69, 0x6c, 0x38, 0x6e, 0xc2, 0xb9, 0xe6, 0x96, 0x40, 0x41, 0x55, 0x98, 0xa2, 0x9c, - 0x41, 0x52, 0x69, 0x06, 0x77, 0x73, 0xfc, 0xd5, 0x1d, 0x76, 0xdc, 0xc5, 0x71, 0x04, 0xcf, 0x8c, - 0x0f, 0x0c, 0x35, 0xe3, 0xdf, 0x83, 0x10, 0xcf, 0x90, 0x88, 0xb8, 0x67, 0x66, 0x48, 0x5c, 0xaa, - 0xba, 0xb5, 0x6c, 0xb4, 0xf0, 0x32, 0xeb, 0xe6, 0x6e, 0x9c, 0x0f, 0x42, 0xaf, 0x42, 0x84, 0x10, - 0x47, 0x25, 0xc6, 0xd7, 0xb9, 0x44, 0x9f, 0xe4, 0xc1, 0xfd, 0x5a, 0x6d, 0xa7, 0x66, 0x7c, 0x1d, - 0x2b, 0x61, 0x42, 0x1c, 0xfa, 0x03, 0x2d, 0x02, 0xb3, 0x0d, 0x89, 0x46, 0x2d, 0x3e, 0x66, 0xdc, - 0x4d, 0x2a, 0x3e, 0x0a, 0xc3, 0x39, 0x30, 0x3a, 0xea, 0xde, 0x01, 0xe1, 0x16, 0x95, 0xc0, 0x39, - 0x30, 0x3a, 0xcb, 0x77, 0x89, 0x12, 0xa6, 0x8d, 0xcb, 0x07, 0x04, 0xa5, 0x21, 0xf2, 0x48, 0x6b, - 0xb5, 0x98, 0x23, 0x36, 0xc5, 0x50, 0xbc, 0xe7, 0x7e, 0x55, 0x17, 0xfa, 0xf5, 0xaa, 0x3a, 0xe1, - 0xfa, 0x74, 0x34, 0x67, 0x9f, 0x39, 0xf3, 0x51, 0x05, 0x38, 0x69, 0x5b, 0x73, 0xf6, 0x51, 0x0a, - 0xc2, 0x7c, 0x5f, 0x24, 0x15, 0xc9, 0x4e, 0x5e, 0x8f, 0x29, 0xee, 0x23, 0x7a, 0x0d, 0x66, 0x78, - 0x0c, 0x53, 0xd5, 0x0d, 0x1b, 0x37, 0x9c, 0xd6, 0x21, 0xb3, 0x06, 0x23, 0x4a, 0x82, 0x93, 0xcb, - 0x82, 0x8a, 0x6e, 0x80, 0x3c, 0x68, 0x3e, 0x33, 0x2b, 0x2e, 0xa2, 0xcc, 0x0c, 0x58, 0xcf, 0xd4, - 0xd2, 0x16, 0x6c, 0xe3, 0x33, 0x4b, 0x53, 0xdc, 0xd2, 0x16, 0x0d, 0x3d, 0x93, 0xf4, 0x06, 0xc8, - 0xc2, 0x76, 0xee, 0xf5, 0x8d, 0x73, 0x5c, 0x4e, 0xef, 0x75, 0xcd, 0xc3, 0x6c, 0x47, 0xb3, 0x09, - 0x56, 0xeb, 0x5d, 0x53, 0x6f, 0x61, 0x95, 0x63, 0xa5, 0x12, 0xac, 0x77, 0x92, 0x35, 0x15, 0x59, - 0x0b, 0x67, 0xe1, 0xb3, 0x62, 0x1e, 0xf3, 0xbf, 0x8d, 0x98, 0xc7, 0x75, 0x90, 0x75, 0xbc, 0xa7, - 0x75, 0x5b, 0x8e, 0x6a, 0x98, 0x82, 0x4f, 0x2f, 0x53, 0xf3, 0x5b, 0x49, 0x08, 0x7a, 0xd5, 0xe4, - 0x1c, 0xfa, 0x4d, 0x58, 0xf0, 0x6c, 0xcd, 0x8e, 0x6d, 0xb4, 0x35, 0xfb, 0x50, 0xe5, 0x42, 0x30, - 0xf5, 0x39, 0x66, 0xaa, 0x2c, 0x3f, 0x3b, 0xca, 0x14, 0x2f, 0xca, 0x3f, 0x5c, 0xb8, 0x32, 0x1b, - 0xe7, 0x92, 0x3b, 0xcd, 0x36, 0x9f, 0x85, 0x37, 0xa5, 0x7f, 0x18, 0x80, 0x29, 0x26, 0x5a, 0xd0, - 0x6d, 0x08, 0xd2, 0x51, 0x22, 0x9e, 0x38, 0xae, 0x2b, 0xca, 0xc6, 0x20, 0x04, 0x41, 0x66, 0x5d, - 0x21, 0xc6, 0x93, 0xec, 0x37, 0x5a, 0x80, 0x30, 0xc1, 0x0f, 0xd4, 0x87, 0x5a, 0x2b, 0x35, 0xcb, - 0xae, 0x4c, 0x88, 0xe0, 0x07, 0xf7, 0xb5, 0x16, 0xba, 0x04, 0x21, 0x83, 0xa8, 0x26, 0x7e, 0xc4, - 0xac, 0xd4, 0x88, 0x32, 0x65, 0x90, 0x4d, 0xfc, 0x08, 0x7d, 0x0e, 0xa2, 0x8f, 0x34, 0xa2, 0xe2, - 0x76, 0xc7, 0x39, 0x64, 0x6f, 0x2d, 0x42, 0x2f, 0x19, 0xa9, 0xd0, 0x67, 0x66, 0xe6, 0x69, 0x76, - 0x13, 0x3b, 0x6a, 0xc3, 0x6a, 0x71, 0xbf, 0x32, 0x4a, 0xdd, 0x60, 0x4a, 0x2a, 0x59, 0x2d, 0xb2, - 0x16, 0x8c, 0x04, 0xe4, 0xc9, 0xb5, 0x60, 0x64, 0x52, 0x0e, 0xae, 0x05, 0x23, 0x41, 0x79, 0x6a, - 0x2d, 0x18, 0x99, 0x92, 0x43, 0x6b, 0xc1, 0x48, 0x48, 0x0e, 0xaf, 0x05, 0x23, 0x61, 0x39, 0xb2, - 0x16, 0x8c, 0x44, 0xe4, 0xe8, 0x5a, 0x30, 0x12, 0x95, 0x61, 0x2d, 0x18, 0x01, 0x79, 0x7a, 0x2d, - 0x18, 0x99, 0x96, 0x63, 0x6b, 0xc1, 0x48, 0x4c, 0x8e, 0x73, 0x21, 0xbf, 0x16, 0x8c, 0x24, 0xe4, - 0x99, 0xb5, 0x60, 0x64, 0x46, 0x96, 0xd7, 0x82, 0x11, 0x59, 0x4e, 0xae, 0x05, 0x23, 0x49, 0x19, - 0xa5, 0x2b, 0x22, 0xd7, 0xa4, 0xa1, 0xaf, 0xf4, 0x9d, 0xd3, 0xd8, 0x2e, 0x32, 0x1b, 0x94, 0x2e, - 0x40, 0x90, 0x8a, 0x4a, 0xf4, 0x6e, 0x1f, 0xc8, 0x98, 0xca, 0x97, 0x0d, 0xc9, 0xfd, 0x58, 0x02, - 0xb9, 0x86, 0x1f, 0x74, 0xb1, 0xd9, 0xc0, 0xf7, 0xb5, 0x56, 0x69, 0xbf, 0x6b, 0x1e, 0xa0, 0x57, - 0x61, 0xa6, 0x41, 0x7f, 0xa8, 0x3c, 0x30, 0x4c, 0x0f, 0x5d, 0x62, 0x87, 0x1e, 0x67, 0xe4, 0x1a, - 0xa5, 0xd2, 0xb3, 0x7f, 0x09, 0x40, 0xf4, 0xa3, 0x2c, 0x19, 0x60, 0x5d, 0xa2, 0xbc, 0x0b, 0xe5, - 0xc6, 0x01, 0x18, 0xdb, 0x7a, 0xc4, 0xe4, 0x73, 0x1f, 0x8c, 0x62, 0x3d, 0x42, 0x4b, 0x30, 0x67, - 0xe2, 0xc7, 0x8e, 0x3a, 0xd8, 0x99, 0xc9, 0x62, 0x25, 0x49, 0xdb, 0x4a, 0xfe, 0x01, 0xb9, 0x7f, - 0x08, 0xc0, 0x8c, 0xbb, 0x68, 0x57, 0x17, 0xee, 0x81, 0x4c, 0x19, 0xc4, 0xd0, 0x55, 0xc7, 0xe2, - 0x48, 0xae, 0x56, 0x7c, 0x6f, 0x54, 0x44, 0xaf, 0x1f, 0x85, 0x3e, 0x57, 0xf5, 0x1d, 0x8b, 0x4d, - 0xc7, 0x8d, 0x03, 0x25, 0x4e, 0xfc, 0xb4, 0xf4, 0x2e, 0x24, 0xdc, 0x41, 0x9c, 0x82, 0x4a, 0x10, - 0xea, 0x9b, 0xef, 0xf3, 0x63, 0xcc, 0xe7, 0x1e, 0xb5, 0x22, 0x86, 0xa6, 0xbf, 0x01, 0xe8, 0xe4, - 0xdc, 0x7e, 0xc3, 0x64, 0x8a, 0x1b, 0x26, 0x5b, 0xfd, 0x86, 0xc9, 0xbb, 0xe7, 0xdb, 0x9b, 0x6f, - 0xd9, 0xfe, 0xf0, 0xe1, 0x77, 0x27, 0x21, 0xc1, 0x35, 0xb0, 0x67, 0x8b, 0x50, 0x79, 0x4c, 0xc5, - 0xbd, 0x61, 0x36, 0x7b, 0xa9, 0x37, 0xba, 0xbf, 0x80, 0x22, 0xbb, 0x0d, 0x5e, 0xe7, 0x57, 0xa8, - 0xdd, 0xa6, 0xe9, 0xfd, 0x39, 0xba, 0x00, 0xb5, 0xbf, 0x34, 0xdd, 0xeb, 0x74, 0x0d, 0x12, 0xcc, - 0xf6, 0xee, 0xf5, 0x9a, 0x64, 0xbd, 0xe2, 0x8c, 0xea, 0x75, 0x2b, 0x42, 0x9c, 0x74, 0x34, 0x5f, - 0xbe, 0x2f, 0x38, 0x32, 0xd5, 0x24, 0x54, 0x79, 0x8c, 0x8e, 0xf1, 0x1b, 0x52, 0x36, 0x26, 0xdd, - 0x36, 0x56, 0x3b, 0x16, 0x8f, 0x86, 0x4d, 0x2a, 0x51, 0x4e, 0xd9, 0xb6, 0x08, 0xda, 0x65, 0xac, - 0xc2, 0xce, 0x42, 0xd5, 0xf9, 0xe1, 0xa4, 0x42, 0x43, 0x63, 0x4b, 0x23, 0x8e, 0x53, 0x99, 0x21, - 0x03, 0x1c, 0xf8, 0x01, 0x84, 0x49, 0xb7, 0x4d, 0xa5, 0x21, 0xd3, 0xa6, 0xd3, 0xb7, 0xb2, 0x43, - 0xd6, 0x5c, 0xec, 0xb6, 0x0e, 0xb6, 0x3a, 0x35, 0xde, 0xcf, 0x33, 0x98, 0xf8, 0x63, 0xee, 0xaf, - 0x24, 0x58, 0xa0, 0xb7, 0x94, 0x5f, 0xf7, 0x12, 0xab, 0xbd, 0x70, 0xd1, 0x35, 0x08, 0x33, 0x33, - 0xdb, 0x73, 0x27, 0x56, 0x8f, 0x8f, 0x32, 0x21, 0xda, 0xfb, 0xb9, 0x8d, 0x82, 0x10, 0x05, 0xae, - 0xb2, 0xf0, 0x90, 0x63, 0x6b, 0x26, 0x61, 0xa9, 0x41, 0xfa, 0xe2, 0xdb, 0xb8, 0x5d, 0xc7, 0x36, - 0x7f, 0x9d, 0x31, 0x65, 0xae, 0xaf, 0x71, 0x83, 0xb7, 0xe5, 0xd2, 0x90, 0x1a, 0x5c, 0xb2, 0x17, - 0x84, 0xfe, 0x3f, 0x30, 0xbf, 0x89, 0x1f, 0x0d, 0xdb, 0x4d, 0x11, 0xc2, 0x5c, 0xdc, 0xba, 0x97, - 0xe6, 0xfa, 0xa0, 0xd0, 0xf2, 0x97, 0x9f, 0xe4, 0xd9, 0x4a, 0x77, 0xd8, 0x00, 0xc5, 0x1d, 0x98, - 0xfb, 0x14, 0x16, 0x06, 0xd0, 0x3d, 0x06, 0xf8, 0x00, 0x42, 0xc4, 0xd1, 0x1c, 0x61, 0x18, 0x27, - 0xc6, 0x41, 0xaf, 0x39, 0x9a, 0xd3, 0x25, 0x8a, 0x18, 0x97, 0xbb, 0x06, 0xaf, 0x14, 0xba, 0x8e, - 0x45, 0x59, 0x4c, 0x78, 0x13, 0xb8, 0x61, 0x99, 0x0d, 0xa3, 0x65, 0xf8, 0x13, 0xa2, 0xb9, 0x57, - 0xe1, 0xea, 0xa8, 0x6e, 0xde, 0x49, 0x28, 0x2c, 0x1a, 0xdf, 0x6d, 0x63, 0xda, 0x73, 0xdd, 0x20, - 0x0e, 0xfa, 0x00, 0x62, 0x82, 0x47, 0xc7, 0xc9, 0xa8, 0xba, 0xe9, 0x71, 0xdb, 0x03, 0x21, 0xb9, - 0xbf, 0x96, 0x60, 0xb6, 0x6c, 0x5b, 0x9d, 0x0e, 0xd6, 0x85, 0x1e, 0xe5, 0x67, 0xeb, 0xaa, 0x4f, - 0xc9, 0xa7, 0x3e, 0x37, 0x21, 0x50, 0x2d, 0x0b, 0x2f, 0xf1, 0xce, 0xf3, 0x3a, 0x9f, 0xd5, 0x32, - 0x7a, 0x97, 0x1f, 0x70, 0x97, 0x30, 0x89, 0x9e, 0xb8, 0xf5, 0xf2, 0xc8, 0x2c, 0x74, 0xef, 0x64, - 0xbb, 0x24, 0xf7, 0x83, 0x30, 0x5c, 0xf2, 0xbf, 0xb4, 0x95, 0x92, 0xbb, 0xf0, 0xcf, 0x20, 0xec, - 0xc6, 0xe3, 0xc6, 0x90, 0xdc, 0xc3, 0x20, 0xf2, 0xe2, 0x3c, 0xfc, 0x31, 0x39, 0x17, 0x13, 0xd5, - 0x20, 0x69, 0x98, 0x0e, 0xb6, 0x5b, 0x58, 0x7b, 0x48, 0x6d, 0x3b, 0x7a, 0x66, 0x22, 0x11, 0x32, - 0xae, 0x7d, 0x22, 0xfb, 0x00, 0xb8, 0x9d, 0xf3, 0x19, 0xcc, 0xfa, 0x41, 0xdd, 0xf5, 0x8f, 0x8e, - 0xc0, 0xb3, 0xe5, 0xf5, 0x60, 0xdd, 0x54, 0x81, 0x0f, 0xc8, 0x8d, 0x1e, 0x7e, 0xe4, 0x79, 0x78, - 0x3c, 0xcb, 0x72, 0xfb, 0xc2, 0x27, 0x52, 0x1e, 0xf0, 0xf6, 0xfa, 0x1c, 0x0d, 0xa6, 0x96, 0x7f, - 0x4d, 0x8e, 0xc6, 0x7d, 0x08, 0xf1, 0xf8, 0xbb, 0x48, 0x78, 0xde, 0xb9, 0xe8, 0x16, 0x78, 0x80, - 0x5f, 0x11, 0x68, 0xe9, 0x3f, 0x94, 0x20, 0xe6, 0x7f, 0xdd, 0xc8, 0x80, 0x08, 0x3b, 0x7e, 0x57, - 0x44, 0x4e, 0xbe, 0xf0, 0x78, 0x08, 0x67, 0xa5, 0xaa, 0x4e, 0xad, 0x4b, 0xdd, 0xb6, 0x3a, 0xbd, - 0x84, 0xf7, 0xa4, 0x12, 0xa1, 0x04, 0x6a, 0xb9, 0xa7, 0xbf, 0x09, 0x51, 0xef, 0xd0, 0x7d, 0x01, - 0xd1, 0xc9, 0x17, 0x18, 0x10, 0x1d, 0x39, 0x7f, 0x19, 0xe2, 0x7d, 0x27, 0x86, 0xe6, 0xbd, 0x35, - 0x04, 0x8b, 0x21, 0xbe, 0x86, 0x33, 0x51, 0x72, 0x3f, 0x0a, 0xc3, 0xec, 0x30, 0xc9, 0xfd, 0x31, - 0xc8, 0x3e, 0xb9, 0xa5, 0xb6, 0x0c, 0xe2, 0x08, 0xde, 0xbc, 0x31, 0x3a, 0x48, 0xe2, 0x13, 0x7e, - 0x82, 0x15, 0x13, 0x76, 0xbf, 0x48, 0xfc, 0x14, 0x12, 0x3a, 0x5f, 0xb8, 0x48, 0x8d, 0x88, 0x0a, - 0xb3, 0x51, 0x61, 0x8d, 0x21, 0x02, 0x50, 0xa0, 0xc7, 0x75, 0x5f, 0x13, 0x41, 0x0d, 0x88, 0x7b, - 0xe0, 0x2c, 0x28, 0x41, 0x9d, 0xda, 0xe7, 0x17, 0x86, 0x31, 0x77, 0x16, 0x16, 0xa6, 0x68, 0xc2, - 0x8c, 0x3b, 0x89, 0x1b, 0x4a, 0x89, 0xbe, 0x90, 0x69, 0xdc, 0x83, 0xa9, 0x89, 0xd0, 0xca, 0x77, - 0x24, 0x98, 0x75, 0x67, 0xf2, 0x3c, 0x3e, 0x51, 0x56, 0x16, 0x2f, 0xd6, 0x8e, 0x8f, 0x32, 0x49, - 0x71, 0x32, 0x6e, 0x3c, 0xea, 0xb9, 0xf9, 0x2e, 0xa9, 0x0f, 0x00, 0xea, 0xd4, 0x26, 0xa1, 0xed, - 0x6e, 0xb5, 0x98, 0xb0, 0x49, 0xa8, 0x60, 0x7b, 0x7e, 0x9b, 0x84, 0xfe, 0xac, 0xea, 0xe8, 0xbb, - 0x12, 0x24, 0x79, 0x6a, 0xb3, 0xdd, 0x75, 0x34, 0x5e, 0xdc, 0xe0, 0x06, 0x46, 0x3e, 0x3e, 0x3e, - 0xca, 0xcc, 0xb0, 0xd7, 0xbb, 0x21, 0xda, 0xd8, 0xb4, 0x17, 0xf6, 0x6f, 0x7b, 0x28, 0x22, 0x8e, - 0xe0, 0x11, 0x74, 0x74, 0x17, 0x12, 0x3c, 0x5a, 0xe4, 0x96, 0xb8, 0x32, 0x1b, 0x2f, 0x5e, 0xbc, - 0xfa, 0xec, 0x28, 0x93, 0x1d, 0x72, 0x4f, 0x78, 0xa0, 0xe9, 0x3e, 0xef, 0xab, 0xc4, 0xf7, 0xfc, - 0x8f, 0x68, 0x1d, 0x66, 0xb8, 0x29, 0xdc, 0x2b, 0x20, 0x83, 0xf1, 0x13, 0xf2, 0xdc, 0x8c, 0xf6, - 0xa8, 0x3c, 0xaa, 0x98, 0x9b, 0x87, 0xb9, 0xa1, 0x36, 0xd8, 0x2f, 0x42, 0x30, 0xdf, 0x2f, 0x56, - 0x3d, 0x2b, 0x49, 0x1d, 0xd4, 0xb7, 0xef, 0x8f, 0x2d, 0x9a, 0xbd, 0x52, 0x32, 0x26, 0x1a, 0xdd, - 0xa7, 0x41, 0x8d, 0xfb, 0xd9, 0x80, 0xf6, 0xba, 0x00, 0x3e, 0x7b, 0xbd, 0x03, 0xf8, 0xae, 0x0a, - 0xfb, 0xc8, 0xd3, 0x2c, 0x3c, 0xec, 0xf7, 0xc1, 0x05, 0xe0, 0xd9, 0x78, 0xaf, 0x0c, 0xce, 0xd5, - 0x2d, 0x3f, 0x95, 0x20, 0xde, 0xb7, 0xb3, 0xdf, 0xa4, 0x72, 0xd9, 0xf6, 0x6c, 0x2b, 0x5e, 0x20, - 0xf6, 0xce, 0xf9, 0xb7, 0xd5, 0x6f, 0x72, 0xa5, 0xff, 0x56, 0x82, 0x78, 0xdf, 0x41, 0xfe, 0x9a, - 0xd4, 0xd2, 0x8b, 0x5f, 0x79, 0x1d, 0x12, 0xfd, 0xaf, 0xc8, 0x37, 0x87, 0xf4, 0x62, 0xe6, 0xc8, - 0x7d, 0x19, 0x42, 0x9c, 0x82, 0x10, 0x24, 0x3e, 0x2c, 0x54, 0x77, 0xaa, 0x9b, 0x2b, 0xea, 0xf2, - 0x96, 0xa2, 0xae, 0x94, 0xe4, 0x09, 0x14, 0x83, 0x48, 0xb9, 0xb2, 0x5e, 0xa1, 0x44, 0x59, 0x42, - 0xd3, 0x10, 0x66, 0x4f, 0x95, 0xb2, 0x1c, 0xc8, 0x15, 0x41, 0xe6, 0xd8, 0x7b, 0x98, 0xaa, 0x19, - 0xea, 0x95, 0xa0, 0x3c, 0xcc, 0x32, 0x0f, 0xa2, 0x4d, 0x2d, 0x2b, 0x7a, 0xbd, 0x55, 0x9f, 0x2d, - 0x9e, 0xf4, 0x9a, 0xe8, 0xed, 0xdd, 0xd4, 0xda, 0x38, 0xf7, 0x37, 0x41, 0x48, 0xf6, 0x40, 0x5c, - 0x25, 0xfb, 0x17, 0x52, 0xcf, 0x3f, 0x0a, 0x9d, 0x99, 0x9a, 0x3e, 0x31, 0x5e, 0xb8, 0x4a, 0x22, - 0x45, 0xfc, 0x21, 0xbd, 0x34, 0xcf, 0x8e, 0x32, 0xc9, 0xc1, 0xc5, 0x92, 0xe7, 0xcc, 0x1d, 0xbb, - 0x4b, 0x64, 0x81, 0x6f, 0xc3, 0x3c, 0x50, 0x7b, 0x35, 0x7c, 0x3c, 0xf0, 0x6d, 0x98, 0x07, 0xbb, - 0x4a, 0x55, 0x09, 0xd3, 0xc6, 0x5d, 0xdb, 0x40, 0x6b, 0x10, 0xb4, 0x3a, 0x8e, 0xeb, 0xd2, 0xbf, - 0x7d, 0xae, 0x2d, 0x6d, 0x75, 0xc4, 0x7e, 0x14, 0x86, 0x81, 0xd6, 0x78, 0xb5, 0x45, 0xef, 0xa0, - 0x85, 0xd3, 0x3d, 0x96, 0x08, 0x8d, 0xf7, 0xbd, 0x88, 0x74, 0x13, 0x62, 0xfe, 0x13, 0x1b, 0x92, - 0x0f, 0x2a, 0xf4, 0x87, 0x5d, 0x3e, 0x3f, 0xd6, 0xd2, 0x85, 0xc3, 0xea, 0x4b, 0x11, 0x7e, 0x19, - 0xa2, 0xde, 0x3e, 0xce, 0x93, 0x29, 0xf5, 0x32, 0x47, 0x3c, 0x2c, 0x39, 0x25, 0x87, 0x72, 0x3f, - 0x08, 0x40, 0x4c, 0xc1, 0xc4, 0x6a, 0x3d, 0xc4, 0x3a, 0xb5, 0xa0, 0xbc, 0x9a, 0x71, 0x69, 0xfc, - 0x9a, 0xf1, 0x02, 0x44, 0x7b, 0x1a, 0xe8, 0x1c, 0x75, 0x9b, 0xbd, 0x51, 0xe8, 0x63, 0x88, 0xd7, - 0xad, 0xae, 0xa9, 0x6b, 0xf6, 0x21, 0xb3, 0xab, 0x98, 0x05, 0x92, 0x18, 0x59, 0x45, 0xe7, 0x5f, - 0x75, 0xbe, 0x28, 0x06, 0x53, 0xfb, 0x49, 0x89, 0xd5, 0x7d, 0x4f, 0xb9, 0xf7, 0x20, 0xe6, 0x6f, - 0x45, 0x11, 0x08, 0x6e, 0x6e, 0x6d, 0x56, 0xf8, 0x9d, 0x2c, 0x16, 0x4a, 0x77, 0x97, 0xab, 0xeb, - 0xeb, 0xb2, 0x44, 0xe9, 0x95, 0x8f, 0xaa, 0x3b, 0x72, 0x80, 0x57, 0xba, 0xd6, 0x76, 0x0a, 0xca, - 0x8e, 0x1b, 0xbd, 0xcd, 0x61, 0x88, 0xfb, 0xe7, 0xa3, 0x92, 0x8f, 0x9a, 0x9d, 0x8c, 0xd0, 0xe7, - 0x79, 0xbf, 0x36, 0xe6, 0x8a, 0x5d, 0x0e, 0xb2, 0xfd, 0xa8, 0xb9, 0x7f, 0x0e, 0x00, 0xea, 0xbd, - 0x78, 0x4f, 0x58, 0x7d, 0x04, 0xd0, 0xd8, 0xc7, 0x8d, 0x83, 0x8e, 0x65, 0x98, 0x8e, 0xf0, 0x35, - 0xdf, 0x19, 0x8b, 0x77, 0x3c, 0x61, 0x55, 0xf2, 0xc6, 0x2b, 0x3e, 0x2c, 0xf4, 0x07, 0xa3, 0xf3, - 0x14, 0x93, 0x2c, 0x4f, 0xc1, 0x6e, 0xfe, 0x6f, 0x34, 0x57, 0x91, 0x2e, 0x00, 0xf4, 0x56, 0x8c, - 0xde, 0x1a, 0xaf, 0x5e, 0xdc, 0xcd, 0xf5, 0xb1, 0xbe, 0x7e, 0x5e, 0xcf, 0xfd, 0x4f, 0x10, 0x50, - 0xc9, 0xc6, 0x9a, 0x83, 0xa9, 0x88, 0x26, 0xa3, 0x42, 0x1c, 0x45, 0x98, 0xe2, 0x2e, 0x7d, 0xe0, - 0x3c, 0x2e, 0xbd, 0x97, 0x66, 0x64, 0xde, 0xfc, 0xd7, 0x20, 0xd6, 0xb0, 0x5a, 0xdd, 0xb6, 0xa9, - 0xb2, 0x9a, 0x2d, 0xe1, 0x7f, 0x7c, 0x69, 0xd4, 0x1b, 0x3b, 0xb1, 0xb8, 0x7c, 0xc9, 0x6a, 0xd1, - 0x67, 0xef, 0x8b, 0x06, 0x06, 0xc8, 0x7a, 0xa0, 0x2b, 0x10, 0xf5, 0x24, 0x0f, 0x2f, 0x1e, 0x51, - 0x7a, 0x04, 0x74, 0x0b, 0xa6, 0x34, 0xa2, 0x5a, 0x7b, 0xcc, 0x98, 0x3e, 0xeb, 0x2a, 0x2a, 0x41, - 0x8d, 0x6c, 0xed, 0xa1, 0xd7, 0x21, 0xd9, 0xd6, 0x1e, 0xab, 0x7b, 0x36, 0xaf, 0x02, 0x57, 0x0d, - 0xbd, 0xc5, 0x25, 0xa1, 0xa4, 0xcc, 0xb4, 0xb5, 0xc7, 0xcb, 0x82, 0x5e, 0xd5, 0x5b, 0x18, 0xbd, - 0x05, 0xf1, 0xbd, 0x07, 0xdc, 0xb5, 0xe2, 0x5a, 0x89, 0x17, 0xc0, 0xcd, 0x1c, 0x1f, 0x65, 0xa6, - 0x97, 0xef, 0xb1, 0x83, 0x61, 0xc9, 0x9d, 0xe9, 0xbd, 0x07, 0xde, 0x43, 0xfa, 0xbf, 0x25, 0x08, - 0x8b, 0x1d, 0xa1, 0x0e, 0x80, 0x38, 0x1e, 0x43, 0xe7, 0xef, 0x34, 0x5e, 0xbc, 0x77, 0x7c, 0x94, - 0x89, 0x96, 0x18, 0xb5, 0x5a, 0x26, 0xcf, 0x8e, 0x32, 0x1f, 0x5c, 0x54, 0xa3, 0xb8, 0x20, 0x4a, - 0x94, 0x4f, 0x52, 0xd5, 0x59, 0x64, 0x79, 0x5f, 0x23, 0xea, 0xbe, 0x41, 0x1c, 0xab, 0x69, 0x6b, - 0x6d, 0x51, 0x75, 0x11, 0xdb, 0xd7, 0xc8, 0xaa, 0x4b, 0x43, 0x69, 0x6a, 0x9b, 0x3d, 0xe4, 0x25, - 0x77, 0xbc, 0x64, 0xc7, 0x7b, 0x46, 0xb7, 0xe0, 0x92, 0x37, 0x58, 0xa5, 0x27, 0x55, 0xef, 0x36, - 0x0e, 0x30, 0xd3, 0x41, 0x54, 0xb8, 0xcf, 0x7a, 0x8d, 0x1b, 0xda, 0xe3, 0x22, 0x6f, 0xca, 0x5d, - 0x82, 0x59, 0xdf, 0x6b, 0xf5, 0x2c, 0x69, 0x0c, 0x32, 0x2f, 0x0e, 0xf1, 0x7d, 0x10, 0x71, 0x0f, - 0x66, 0x06, 0x3e, 0x7d, 0x13, 0xf2, 0xd7, 0x1f, 0x71, 0xec, 0xff, 0x56, 0x2e, 0x5f, 0xe2, 0x8f, - 0xae, 0x6f, 0x90, 0x68, 0xf4, 0x3d, 0xe7, 0xde, 0x84, 0xa4, 0x37, 0x8d, 0x27, 0x48, 0xae, 0x40, - 0x94, 0x55, 0x05, 0xb4, 0x35, 0xfb, 0xc0, 0xad, 0x0c, 0xf0, 0x08, 0xb9, 0x0c, 0xbc, 0xc4, 0xa2, - 0x90, 0xf7, 0xd6, 0xd9, 0x8a, 0x4b, 0x56, 0xbb, 0xc3, 0x5f, 0xbb, 0x1b, 0xa6, 0xcc, 0xc2, 0xe2, - 0xf0, 0x0e, 0xde, 0xe6, 0x7e, 0x2a, 0x43, 0x78, 0x5b, 0x3b, 0x6c, 0x59, 0x9a, 0x8e, 0xb2, 0x30, - 0xed, 0x16, 0xe3, 0xb9, 0x1b, 0x8a, 0x2a, 0x7e, 0x52, 0x3f, 0x1f, 0xcb, 0x2c, 0x7d, 0xe6, 0xe3, - 0x63, 0x03, 0x12, 0x5d, 0x82, 0x6d, 0xca, 0x62, 0x2a, 0xfb, 0x90, 0x83, 0xeb, 0xb3, 0x62, 0xf1, - 0xd9, 0x51, 0xe6, 0xce, 0x78, 0xdc, 0x81, 0x1b, 0x5d, 0xdb, 0x70, 0x0e, 0xf3, 0xb5, 0x7b, 0xeb, - 0xbb, 0x02, 0x8a, 0x0a, 0x23, 0x4b, 0x89, 0x77, 0xfd, 0x8f, 0xa2, 0xe6, 0x92, 0xbe, 0x69, 0xb5, - 0x6d, 0x34, 0x6c, 0x8b, 0xb8, 0x19, 0x26, 0x41, 0xdd, 0x60, 0x44, 0xf4, 0x1a, 0xcc, 0xec, 0x19, - 0x26, 0xcb, 0x08, 0xbb, 0xfd, 0x78, 0x72, 0x29, 0xe1, 0x92, 0x45, 0xc7, 0x87, 0x90, 0xf0, 0x15, - 0x3b, 0x52, 0x2e, 0x0f, 0x31, 0x2e, 0xdf, 0x3a, 0x3e, 0xca, 0xc4, 0x7b, 0x52, 0x83, 0x73, 0xfa, - 0xf3, 0xd8, 0x4e, 0xf1, 0xde, 0x34, 0x94, 0xcf, 0xe7, 0x60, 0x8a, 0x7d, 0x39, 0xca, 0xab, 0xd9, - 0x15, 0xfe, 0x80, 0x2a, 0x10, 0x17, 0xb1, 0x16, 0xfe, 0x59, 0xa9, 0xa8, 0x10, 0xf5, 0xe7, 0x15, - 0xdc, 0x0f, 0x4f, 0xf3, 0x15, 0xb3, 0x61, 0xe9, 0x58, 0xaf, 0xd0, 0x67, 0x45, 0x84, 0x96, 0xd9, - 0x03, 0x41, 0x2b, 0x90, 0x68, 0xb4, 0xb0, 0x66, 0x76, 0x3b, 0x2e, 0x0e, 0x1a, 0x13, 0x27, 0x2e, - 0xc6, 0x09, 0xa0, 0x4d, 0x40, 0x7b, 0xac, 0x56, 0xcd, 0xbf, 0x2a, 0x96, 0x8f, 0x1d, 0x07, 0x4c, - 0x66, 0x63, 0x95, 0xde, 0xca, 0xd0, 0x55, 0x88, 0x9b, 0x96, 0xd9, 0xd0, 0xcc, 0x06, 0x6e, 0x31, - 0xd1, 0xcd, 0x53, 0xb8, 0xfd, 0x44, 0x54, 0x84, 0x10, 0x2f, 0x4b, 0x10, 0x4e, 0xf2, 0xf5, 0x71, - 0xbf, 0xf9, 0x58, 0x9d, 0x50, 0xc4, 0x48, 0x54, 0x81, 0xb0, 0xcd, 0xab, 0x6d, 0x58, 0xa9, 0xc2, - 0x99, 0xc1, 0x2a, 0x5f, 0x45, 0xcf, 0xea, 0x84, 0xe2, 0x8e, 0x45, 0x3b, 0x6e, 0x61, 0x33, 0x57, - 0xd4, 0xa2, 0x24, 0x35, 0x3f, 0xa6, 0x0b, 0xd2, 0x03, 0xec, 0x43, 0xa1, 0x1b, 0x34, 0x58, 0xf6, - 0x8d, 0x15, 0x31, 0x8c, 0xde, 0x60, 0x5f, 0xa1, 0x0c, 0xdd, 0x20, 0x1f, 0x89, 0x36, 0xa9, 0xa5, - 0xe1, 0x1a, 0x0f, 0xac, 0xbc, 0x61, 0xfa, 0xd6, 0x17, 0xce, 0x63, 0x60, 0xaf, 0x4e, 0x28, 0x3e, - 0x04, 0x74, 0x0f, 0xa6, 0x1b, 0x3d, 0x19, 0x98, 0x9a, 0x61, 0x80, 0x37, 0xcf, 0xa5, 0x08, 0x57, - 0xa9, 0xf2, 0xeb, 0x51, 0xd1, 0x27, 0x90, 0x20, 0x7d, 0x0e, 0x59, 0xea, 0x12, 0x43, 0x7d, 0xe3, - 0xbc, 0x01, 0xe1, 0xd5, 0x09, 0x65, 0x00, 0x09, 0xfd, 0x5f, 0x90, 0x9d, 0x81, 0x2c, 0x14, 0xcb, - 0xfa, 0x8f, 0xae, 0x17, 0x3e, 0x25, 0xd7, 0xb6, 0x3a, 0xa1, 0x9c, 0x40, 0x43, 0x9f, 0xc1, 0x0c, - 0xe9, 0xff, 0x9c, 0x2e, 0xb5, 0xc0, 0x26, 0x78, 0x73, 0xfc, 0x0f, 0xf0, 0x7a, 0xf8, 0x83, 0x58, - 0x14, 0xde, 0xec, 0x4f, 0x66, 0xb1, 0xea, 0x97, 0xd1, 0xf0, 0xc3, 0x93, 0x6b, 0x14, 0x7e, 0x00, - 0x0b, 0xdd, 0x85, 0x68, 0xdb, 0x55, 0x2a, 0xac, 0x76, 0x64, 0xb4, 0x0f, 0x33, 0xa8, 0xe7, 0x56, - 0x27, 0x94, 0xde, 0x78, 0xf4, 0xff, 0x25, 0xb8, 0xa2, 0x8d, 0xc8, 0x7a, 0xb1, 0x5a, 0x93, 0xd1, - 0x81, 0xfe, 0x31, 0x72, 0x6b, 0xab, 0x13, 0xca, 0xc8, 0x59, 0x90, 0x0d, 0xf3, 0xda, 0x50, 0xa5, - 0x96, 0x5a, 0x3c, 0xd3, 0xd0, 0x1e, 0xa9, 0x2e, 0x57, 0x27, 0x94, 0x53, 0x90, 0x51, 0x03, 0x92, - 0x64, 0xf0, 0x8b, 0xc6, 0xd4, 0xcb, 0x6c, 0xba, 0xb7, 0xce, 0xe4, 0x83, 0x93, 0x1f, 0x54, 0xae, - 0x4e, 0x28, 0x27, 0xf1, 0xd0, 0xcb, 0x10, 0xe3, 0xa5, 0xd7, 0x36, 0xd6, 0x88, 0x65, 0xa6, 0xae, - 0x70, 0x05, 0xcc, 0x68, 0x0a, 0x23, 0xa1, 0x6f, 0x40, 0xc6, 0xc6, 0x8e, 0x6d, 0x30, 0x5b, 0x0e, - 0x3f, 0xc6, 0x8d, 0x2e, 0xb3, 0xfe, 0xf6, 0x34, 0xa3, 0xd5, 0xb5, 0xb1, 0xda, 0xb2, 0x9a, 0xa9, - 0x2c, 0x93, 0xf1, 0xa3, 0x1d, 0x31, 0x81, 0x50, 0x71, 0x01, 0x96, 0xf9, 0x78, 0xe5, 0x8a, 0x7d, - 0x5a, 0xd3, 0xba, 0xd5, 0x2c, 0x46, 0x21, 0x2c, 0xd2, 0xe6, 0x5e, 0x59, 0x0c, 0x2f, 0x88, 0xe1, - 0xa5, 0x30, 0x69, 0xf9, 0x73, 0xb9, 0x7f, 0x8c, 0x41, 0xc4, 0x33, 0x5e, 0x96, 0x00, 0x79, 0xf6, - 0x69, 0xef, 0x33, 0x0a, 0x6a, 0x56, 0x04, 0xe8, 0x01, 0xb8, 0x6d, 0xbd, 0x2f, 0x29, 0xee, 0xf4, - 0x15, 0x42, 0x8e, 0xf3, 0x2d, 0x33, 0x65, 0x50, 0xaf, 0x52, 0x92, 0xaa, 0x7b, 0x51, 0x69, 0xef, - 0xa9, 0x7b, 0x9e, 0xde, 0x48, 0xb8, 0x64, 0xa1, 0xee, 0xaf, 0x41, 0xc2, 0xee, 0x9a, 0x2c, 0xd7, - 0x2d, 0x82, 0x4a, 0xdc, 0x28, 0x8f, 0x0b, 0xaa, 0x88, 0x0b, 0x95, 0x06, 0x34, 0xd0, 0x8d, 0x33, - 0x35, 0x90, 0xbb, 0xf7, 0x55, 0xc9, 0x53, 0x41, 0xcb, 0x83, 0x2a, 0xe8, 0xf5, 0xb3, 0x55, 0x90, - 0x0f, 0xc6, 0xd3, 0x41, 0xbb, 0x43, 0x75, 0xd0, 0xd2, 0x98, 0x42, 0xd4, 0x87, 0xd8, 0xaf, 0x84, - 0x4a, 0x03, 0x4a, 0xe8, 0xc6, 0x99, 0x4a, 0xc8, 0xbf, 0x47, 0xa1, 0x85, 0xb6, 0x86, 0x68, 0xa1, - 0x9b, 0xe7, 0xf2, 0x77, 0x57, 0xa5, 0x3e, 0x35, 0xa4, 0x0c, 0x53, 0x43, 0xf9, 0xf1, 0xd4, 0x90, - 0x0f, 0xb2, 0x4f, 0x0f, 0x7d, 0x7a, 0x42, 0x0f, 0xc9, 0x67, 0x0b, 0xf2, 0xa1, 0x91, 0xc4, 0x55, - 0xe9, 0x84, 0x22, 0xd2, 0x86, 0x28, 0xa2, 0xe4, 0x99, 0xf2, 0xe1, 0xb4, 0x0a, 0x8a, 0x55, 0x69, - 0x88, 0x26, 0xfa, 0x08, 0x62, 0x7e, 0xed, 0xc1, 0xca, 0xe4, 0x46, 0xeb, 0xb9, 0x53, 0xbe, 0x03, - 0x67, 0x3c, 0xe0, 0x6b, 0x42, 0x5f, 0x3b, 0xa9, 0x84, 0x66, 0xcf, 0x04, 0x3f, 0xa5, 0x06, 0x63, - 0x55, 0x3a, 0xa9, 0x85, 0xd6, 0xfd, 0x5a, 0x68, 0xee, 0x4c, 0x1b, 0xe5, 0x84, 0x1b, 0xb4, 0x2a, - 0xf9, 0xd5, 0xd0, 0x77, 0x25, 0xb8, 0x32, 0x4a, 0x8f, 0x08, 0x03, 0xe0, 0xfd, 0x0b, 0xaa, 0x21, - 0xdf, 0xa4, 0x23, 0xa7, 0x41, 0xe4, 0x54, 0x3d, 0xb4, 0x70, 0x66, 0x8d, 0xd6, 0x68, 0xaf, 0x6c, - 0x55, 0x3a, 0x55, 0x11, 0xe9, 0xc3, 0x14, 0x51, 0xea, 0xec, 0x2f, 0x58, 0x4f, 0xfb, 0x1c, 0x7f, - 0x55, 0x1a, 0xa6, 0x89, 0x1e, 0x40, 0xc4, 0xb1, 0xb5, 0x06, 0x4b, 0x2d, 0x5e, 0x62, 0xf9, 0xe3, - 0xfb, 0x22, 0xa4, 0x54, 0x1a, 0x3f, 0xa4, 0x44, 0x11, 0x0c, 0xb3, 0xe9, 0xfe, 0x4b, 0xd9, 0x9d, - 0x62, 0xb2, 0x10, 0x53, 0x58, 0xfc, 0x54, 0xc2, 0x6c, 0x9e, 0xaa, 0x5e, 0x04, 0x88, 0xb8, 0xa5, - 0x5f, 0x3e, 0x45, 0x93, 0xfb, 0xbe, 0x04, 0x93, 0x6b, 0x56, 0x1d, 0xbd, 0xe4, 0x4b, 0x5c, 0xc4, - 0xc5, 0x5a, 0xa6, 0xd6, 0xac, 0xba, 0xc8, 0x40, 0xbc, 0xdf, 0x1b, 0x2d, 0xe2, 0x40, 0xaf, 0x8c, - 0x38, 0x0d, 0x2f, 0xef, 0xe3, 0x0d, 0x42, 0x5f, 0x85, 0x70, 0x87, 0xbb, 0xc1, 0x42, 0xef, 0xe4, - 0x46, 0x8d, 0xe7, 0x3d, 0x15, 0x77, 0x48, 0xee, 0xbf, 0x02, 0x70, 0xf9, 0x54, 0xad, 0x8a, 0xe6, - 0xfb, 0x52, 0x17, 0x51, 0x37, 0x01, 0x81, 0xbe, 0x08, 0xf3, 0x3d, 0x15, 0xce, 0xcb, 0x1f, 0xfb, - 0xb4, 0xd6, 0x9c, 0xd7, 0xca, 0x2a, 0x20, 0x85, 0xee, 0x7a, 0x03, 0x7a, 0x74, 0x15, 0x9b, 0x03, - 0x0e, 0x30, 0xf2, 0xda, 0x2a, 0xa6, 0xab, 0xed, 0x4c, 0x98, 0x36, 0x4c, 0xe2, 0x50, 0xcf, 0xca, - 0xcd, 0x15, 0x4f, 0x15, 0x37, 0xc4, 0x21, 0x7e, 0x79, 0xac, 0x17, 0xca, 0xab, 0x51, 0xef, 0xad, - 0x57, 0x05, 0x0e, 0x7b, 0x89, 0xd0, 0x7b, 0x52, 0xc0, 0x9d, 0xa1, 0xaa, 0xa3, 0xb7, 0x5d, 0xa7, - 0x76, 0x6a, 0x4c, 0x0f, 0x51, 0xb8, 0xbd, 0xaf, 0xc1, 0x8c, 0x63, 0x77, 0x4d, 0xfe, 0xad, 0x3d, - 0x47, 0x60, 0x91, 0x2a, 0x25, 0xe1, 0x91, 0x59, 0xff, 0xd7, 0x6f, 0xf8, 0xff, 0x57, 0x9c, 0x0d, - 0x4b, 0xc7, 0x28, 0x01, 0xb0, 0xad, 0x11, 0xd2, 0xd9, 0xb7, 0x35, 0x82, 0xe5, 0x09, 0x14, 0x86, - 0xc9, 0xbb, 0x1b, 0x35, 0x59, 0x7a, 0xfd, 0x23, 0x7f, 0x92, 0xa7, 0xac, 0x14, 0xaa, 0x9b, 0xd5, - 0xcd, 0x15, 0x75, 0xb3, 0xb0, 0x51, 0xa9, 0xc9, 0x13, 0x28, 0x05, 0x73, 0x1f, 0x16, 0xaa, 0x3b, - 0x22, 0xeb, 0xa3, 0x56, 0x37, 0x77, 0x2a, 0xca, 0xfd, 0xc2, 0xba, 0x2c, 0xa1, 0x79, 0x40, 0xca, - 0x56, 0xe9, 0x6e, 0xad, 0x5c, 0x54, 0x4b, 0x5b, 0x1b, 0xdb, 0x85, 0xd2, 0x4e, 0x75, 0x6b, 0x53, - 0x0e, 0xa0, 0x08, 0x04, 0xcb, 0x5b, 0x9b, 0x15, 0x19, 0x5e, 0xff, 0xf9, 0x94, 0xa8, 0xc2, 0xbd, - 0x0a, 0xd3, 0xbb, 0x9b, 0xb5, 0xed, 0x4a, 0xa9, 0xba, 0x5c, 0xad, 0x94, 0xe5, 0x89, 0xf4, 0xec, - 0x93, 0xa7, 0xd9, 0x19, 0xda, 0xb4, 0x6b, 0x92, 0x0e, 0x6e, 0x30, 0xc3, 0x03, 0xa5, 0x21, 0x54, - 0x2c, 0x94, 0xee, 0xee, 0x6e, 0xcb, 0x52, 0x3a, 0xf1, 0xe4, 0x69, 0x16, 0xd8, 0xc7, 0x0e, 0xdc, - 0x44, 0xb8, 0xc2, 0xe3, 0xd7, 0x5b, 0x4a, 0x45, 0x0e, 0xa4, 0x67, 0x9e, 0x3c, 0xcd, 0x4e, 0xb3, - 0xb0, 0xb8, 0x50, 0xfc, 0xaf, 0x41, 0xbc, 0x56, 0x5a, 0xad, 0x6c, 0x14, 0xd4, 0xd2, 0x6a, 0x61, - 0x73, 0xa5, 0x22, 0x4f, 0xa6, 0xe7, 0x9e, 0x3c, 0xcd, 0xca, 0x83, 0xca, 0x83, 0x4e, 0x51, 0xdd, - 0xd8, 0xde, 0x52, 0x76, 0xe4, 0x60, 0x6f, 0x0a, 0xae, 0xb3, 0x51, 0x0e, 0x80, 0x8f, 0x5e, 0xae, - 0x54, 0xca, 0xf2, 0x54, 0x1a, 0x3d, 0x79, 0x9a, 0x4d, 0xd0, 0xf6, 0x9e, 0x2a, 0x46, 0xd7, 0x20, - 0x56, 0x52, 0x2a, 0x85, 0x9d, 0x8a, 0x5a, 0xdb, 0x29, 0xec, 0xd4, 0xe4, 0x50, 0x6f, 0x27, 0x3e, - 0xf5, 0x8a, 0xf2, 0x90, 0x2c, 0xec, 0xee, 0x6c, 0xa9, 0x7d, 0x7d, 0xc3, 0xe9, 0x85, 0x27, 0x4f, - 0xb3, 0xb3, 0xb4, 0x2f, 0x95, 0x6d, 0xfe, 0xfe, 0x5f, 0x00, 0xb9, 0x6f, 0xfd, 0xea, 0x4a, 0x49, - 0x8e, 0xa4, 0xe7, 0x9f, 0x3c, 0xcd, 0xa2, 0xc1, 0x2d, 0xac, 0x94, 0xe8, 0xa5, 0xd8, 0xf9, 0x78, - 0xbb, 0x52, 0xae, 0xd4, 0x4a, 0x6a, 0xff, 0xb6, 0xa3, 0xe9, 0xd4, 0x93, 0xa7, 0xd9, 0x39, 0x3a, - 0xe6, 0xc4, 0xd6, 0x6f, 0x82, 0x5c, 0xdb, 0x51, 0x2a, 0x85, 0x0d, 0xb5, 0xba, 0xb9, 0x52, 0xa9, - 0xb1, 0x97, 0x05, 0xbd, 0x25, 0x0d, 0x28, 0x42, 0xba, 0x85, 0xcd, 0xca, 0x87, 0x03, 0xf8, 0xd3, - 0xbd, 0xfe, 0x03, 0xba, 0x0d, 0x65, 0x21, 0xba, 0x51, 0x5d, 0x51, 0x0a, 0x0c, 0x37, 0x96, 0x4e, - 0x3e, 0x79, 0x9a, 0x8d, 0xd3, 0x7e, 0x9e, 0xa6, 0x42, 0x55, 0xc8, 0xb0, 0x43, 0xa9, 0x6d, 0x17, - 0x36, 0xd5, 0xd2, 0xd6, 0xe6, 0x72, 0x75, 0x45, 0x55, 0x2a, 0xa5, 0xad, 0xcd, 0x52, 0x75, 0xbd, - 0xca, 0xc7, 0xc5, 0xd3, 0x57, 0x9f, 0x3c, 0xcd, 0x66, 0xdd, 0x23, 0x3a, 0x55, 0xaf, 0xbc, 0x07, - 0x97, 0x39, 0xd4, 0xbd, 0x75, 0x7e, 0xb8, 0x7e, 0x0e, 0x4c, 0xa4, 0x17, 0x9f, 0x3c, 0xcd, 0xa6, - 0x3d, 0x90, 0x93, 0x1a, 0xe2, 0x4d, 0x40, 0xe2, 0x28, 0x94, 0xca, 0xf6, 0x7a, 0xb5, 0xc4, 0x27, - 0x9f, 0x49, 0x5f, 0x7e, 0xf2, 0x34, 0x7b, 0xa9, 0x77, 0x18, 0x3e, 0x71, 0x9f, 0x8e, 0xfc, 0xce, - 0xf7, 0x17, 0x27, 0xfe, 0xfc, 0x07, 0x8b, 0x13, 0xc5, 0xeb, 0x3f, 0xf9, 0x8f, 0xc5, 0x89, 0x9f, - 0x1c, 0x2f, 0x4a, 0x3f, 0x3b, 0x5e, 0x94, 0x7e, 0x71, 0xbc, 0x28, 0xfd, 0xfb, 0xf1, 0xa2, 0xf4, - 0xbd, 0x5f, 0x2d, 0x4e, 0xfc, 0xec, 0x57, 0x8b, 0x13, 0xbf, 0xf8, 0xd5, 0xe2, 0xc4, 0x27, 0x21, - 0x2e, 0x03, 0xeb, 0x21, 0x16, 0xe1, 0x7b, 0xeb, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x77, - 0x4e, 0xe2, 0x36, 0x4d, 0x00, 0x00, + // 6077 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x7c, 0x5d, 0x6c, 0x1b, 0xd9, + 0x75, 0xbf, 0x86, 0xa2, 0xf8, 0x71, 0x44, 0x52, 0xa3, 0x2b, 0x59, 0xa2, 0x19, 0xaf, 0xa8, 0xe5, + 0xda, 0xbb, 0xf6, 0x26, 0xa6, 0x76, 0xbd, 0xc9, 0x66, 0xd7, 0xc9, 0x7a, 0x97, 0x5f, 0x92, 0x46, + 0xd6, 0x97, 0x87, 0x92, 0xf7, 0xeb, 0x9f, 0xcc, 0x7f, 0xc8, 0xb9, 0xa2, 0xa6, 0x22, 0x67, 0xe8, + 0xb9, 0x43, 0xdb, 0x4a, 0x80, 0x20, 0x48, 0x1a, 0xa0, 0x30, 0xfa, 0x90, 0x02, 0x6d, 0x5f, 0x5a, + 0x17, 0x45, 0x93, 0x00, 0x7d, 0x68, 0x50, 0x34, 0x28, 0xda, 0x3e, 0xf6, 0x31, 0x0f, 0x69, 0x11, + 0xa4, 0x28, 0x9a, 0xf6, 0x41, 0x69, 0x95, 0x97, 0x3e, 0xf4, 0xa1, 0xe8, 0xa3, 0xd1, 0x87, 0xe2, + 0x7e, 0xcc, 0x70, 0x48, 0x51, 0x14, 0x25, 0x3b, 0xc9, 0x8b, 0xcc, 0x7b, 0xee, 0xbd, 0xbf, 0xfb, + 0x75, 0xee, 0x39, 0xe7, 0x9e, 0x73, 0xc6, 0x30, 0xf7, 0x5b, 0x76, 0x8d, 0x2c, 0xd1, 0x3f, 0xed, + 0x1a, 0xfb, 0x27, 0xdf, 0x76, 0x6c, 0xd7, 0x46, 0x97, 0xeb, 0x76, 0xfd, 0xc0, 0xb1, 0xf5, 0xfa, + 0x7e, 0x9e, 0x3c, 0x68, 0xe6, 0x59, 0x0d, 0x6f, 0x95, 0xb9, 0x84, 0x1d, 0xc7, 0x76, 0x68, 0x7b, + 0xfe, 0x83, 0xf7, 0xc8, 0xcc, 0x36, 0xec, 0x86, 0xcd, 0x7e, 0x2e, 0xd1, 0x5f, 0x82, 0x3a, 0xcd, + 0x30, 0xda, 0xb5, 0x25, 0xbd, 0x6d, 0x0a, 0x12, 0xf2, 0x48, 0x86, 0xee, 0xea, 0x82, 0x96, 0xf6, + 0x68, 0xa6, 0x7d, 0x73, 0xcf, 0x76, 0x5a, 0xba, 0xeb, 0xc1, 0xbe, 0x42, 0x1e, 0x34, 0x97, 0xea, + 0xba, 0xab, 0x37, 0xed, 0xc6, 0x92, 0x81, 0x49, 0xbd, 0x5d, 0x5b, 0x22, 0xae, 0xd3, 0xa9, 0xbb, + 0x1d, 0x07, 0x1b, 0xa2, 0x51, 0x76, 0x40, 0x23, 0x17, 0x5b, 0xba, 0xe5, 0x7a, 0xf8, 0x1d, 0xd7, + 0x6c, 0x2e, 0xed, 0x37, 0xeb, 0x4b, 0xae, 0xd9, 0xc2, 0xc4, 0xd5, 0x5b, 0x6d, 0x51, 0xf3, 0x32, + 0xed, 0x4a, 0xea, 0xfb, 0xb8, 0xa5, 0xd7, 0xf7, 0x75, 0xab, 0x81, 0x9d, 0x25, 0x3e, 0x46, 0xbd, + 0x5d, 0x13, 0x4d, 0xae, 0xd6, 0x9b, 0x1d, 0xe2, 0x62, 0xe7, 0x21, 0x76, 0x88, 0x69, 0x5b, 0x4b, + 0xa2, 0xa8, 0x89, 0xb2, 0x37, 0x87, 0x86, 0x6d, 0x37, 0x9a, 0x78, 0x89, 0x95, 0x6a, 0x9d, 0xbd, + 0xfe, 0x91, 0x72, 0x3f, 0x08, 0xc1, 0x7c, 0x51, 0xaf, 0x1f, 0x74, 0xda, 0x15, 0xab, 0xee, 0x1c, + 0xb6, 0x5d, 0xd3, 0xb6, 0xb6, 0xd8, 0x5f, 0x82, 0x64, 0x18, 0x3f, 0xc0, 0x87, 0x69, 0x69, 0x51, + 0xba, 0x9e, 0x50, 0xe9, 0x4f, 0xf4, 0x1e, 0x84, 0x5b, 0xb6, 0x81, 0xd3, 0xa1, 0x45, 0xe9, 0x7a, + 0xea, 0xd6, 0x8d, 0xfc, 0xa9, 0xe7, 0x91, 0xef, 0xa2, 0x6d, 0xd8, 0x06, 0x56, 0x59, 0x37, 0x54, + 0x83, 0xd8, 0x41, 0x8b, 0x68, 0xa6, 0xb5, 0x67, 0xa7, 0xc7, 0x17, 0xa5, 0xeb, 0x93, 0xb7, 0x6e, + 0x0f, 0x81, 0x38, 0x65, 0x5a, 0xf9, 0xbb, 0x1b, 0x55, 0xc5, 0xda, 0xb3, 0x8b, 0x93, 0xc7, 0x47, + 0xd9, 0xa8, 0x28, 0xa8, 0xd1, 0x83, 0x16, 0xa1, 0x3f, 0x32, 0x5b, 0xe0, 0xd1, 0xe8, 0xfc, 0x3b, + 0x8e, 0xc9, 0xe6, 0x1f, 0x57, 0xe9, 0x4f, 0xf4, 0x39, 0x40, 0x98, 0xe3, 0x61, 0x43, 0xa3, 0x27, + 0xad, 0xd1, 0x05, 0x86, 0xd8, 0x02, 0x65, 0xbf, 0xa6, 0xac, 0xbb, 0xfa, 0x5d, 0x7c, 0x78, 0x3b, + 0xfc, 0x9f, 0x7f, 0x9a, 0x95, 0xf8, 0xdf, 0xdc, 0x37, 0xc7, 0x21, 0xd5, 0x9d, 0x0a, 0x83, 0x5f, + 0x85, 0x08, 0x3b, 0x22, 0xcc, 0x46, 0x48, 0xdd, 0x7a, 0x63, 0xa4, 0xed, 0xa0, 0x5d, 0xf3, 0x55, + 0xd6, 0x4f, 0x15, 0xfd, 0x11, 0x82, 0x30, 0xd1, 0x9b, 0xae, 0x98, 0x08, 0xfb, 0x8d, 0xfe, 0x48, + 0x82, 0xc5, 0xfe, 0x19, 0x15, 0x0f, 0xef, 0x6e, 0x54, 0x37, 0x74, 0x7a, 0xce, 0x77, 0xf1, 0xa1, + 0x52, 0x4e, 0x8f, 0x2f, 0x8e, 0x5f, 0x9f, 0xbc, 0xb5, 0x35, 0xfa, 0xc0, 0x95, 0x33, 0x10, 0x2b, + 0x96, 0xeb, 0x1c, 0xaa, 0x67, 0x0e, 0x9c, 0xa9, 0xc2, 0xb5, 0x91, 0xa0, 0x82, 0x3c, 0x14, 0xe7, + 0x3c, 0x34, 0x0b, 0x13, 0x0f, 0xf5, 0x66, 0x07, 0x8b, 0xd5, 0xf2, 0xc2, 0xed, 0xd0, 0x3b, 0x52, + 0x6e, 0x1e, 0x22, 0x7c, 0x63, 0x50, 0x12, 0xe2, 0x85, 0x4a, 0xf5, 0xd6, 0x17, 0xde, 0x5e, 0x29, + 0x6d, 0xc8, 0x63, 0xe2, 0x08, 0x7e, 0x37, 0x04, 0x73, 0x55, 0xd7, 0xc1, 0x7a, 0x4b, 0xb1, 0x1a, + 0x98, 0xd0, 0x35, 0x95, 0xb1, 0xab, 0x9b, 0x4d, 0x82, 0xae, 0x41, 0x8a, 0xb0, 0x1a, 0x4d, 0x37, + 0x0c, 0x07, 0x13, 0x22, 0x06, 0x4c, 0x72, 0x6a, 0x81, 0x13, 0xd1, 0x0d, 0x88, 0x8b, 0x66, 0xa6, + 0x91, 0x0e, 0x2f, 0x4a, 0xd7, 0xc3, 0xc5, 0xc4, 0xf1, 0x51, 0x36, 0x26, 0x50, 0xcb, 0x6a, 0x8c, + 0x57, 0x2b, 0x06, 0x7a, 0x13, 0xc2, 0xa4, 0xad, 0x5b, 0x6c, 0x92, 0x93, 0xb7, 0xe6, 0x03, 0x3b, + 0x2c, 0x84, 0x42, 0xbe, 0xda, 0xd6, 0xad, 0x62, 0xf8, 0xc7, 0x47, 0xd9, 0x31, 0x95, 0x35, 0x45, + 0x45, 0x00, 0xe2, 0xea, 0x8e, 0xab, 0xd1, 0x3b, 0x26, 0xf8, 0xfb, 0xa5, 0x40, 0x47, 0x7a, 0xdb, + 0xf3, 0xfb, 0xcd, 0x7a, 0x7e, 0xc7, 0xbb, 0x83, 0xa2, 0x7b, 0x9c, 0x75, 0xa3, 0x54, 0x3a, 0x43, + 0x2e, 0x22, 0xe8, 0x0c, 0x27, 0xba, 0x33, 0xdc, 0x61, 0x44, 0x3a, 0x43, 0x5e, 0xad, 0x18, 0xb9, + 0x7f, 0x19, 0x87, 0xf9, 0xbe, 0xed, 0xd8, 0x76, 0xec, 0x06, 0x5b, 0xe8, 0x32, 0x24, 0xea, 0x1d, + 0xd7, 0x7e, 0x88, 0x1d, 0x3e, 0x19, 0x69, 0xf4, 0xc9, 0x4c, 0x8a, 0x8e, 0x6c, 0x3a, 0xdf, 0x00, + 0xd4, 0xd6, 0x1d, 0xd7, 0xa4, 0xe0, 0x5a, 0x5b, 0xa0, 0xa7, 0x43, 0x8c, 0xeb, 0x94, 0x21, 0x5c, + 0x77, 0xca, 0xbc, 0xf2, 0xdb, 0x1e, 0x98, 0x47, 0x61, 0x4c, 0x22, 0x46, 0x9e, 0x6e, 0xf7, 0xd7, + 0x66, 0x1a, 0x30, 0x7d, 0xa2, 0x0b, 0x52, 0x01, 0x99, 0x0c, 0x19, 0x1b, 0x9a, 0x2f, 0xce, 0xce, + 0xb3, 0xc4, 0x69, 0xaf, 0xbb, 0x5f, 0x91, 0x79, 0x22, 0xc1, 0xdc, 0xe0, 0xc9, 0x0d, 0xe0, 0xe0, + 0x4f, 0x82, 0x1c, 0x3c, 0x79, 0xab, 0xfc, 0x22, 0x36, 0x22, 0x78, 0x0f, 0x14, 0x48, 0xf3, 0x7e, + 0x2a, 0x6e, 0x37, 0xcd, 0xba, 0x1e, 0xe4, 0xf4, 0x9b, 0x30, 0x41, 0x99, 0x8d, 0x32, 0xf8, 0xf8, + 0x10, 0xc6, 0x54, 0x79, 0xab, 0x9c, 0x0e, 0x97, 0x4f, 0x40, 0xf9, 0x1b, 0x59, 0x06, 0xc0, 0x8f, + 0xdb, 0xa6, 0xc3, 0xa8, 0x62, 0x03, 0x33, 0x79, 0xae, 0x31, 0xf2, 0x9e, 0xc6, 0x08, 0xec, 0x5e, + 0x8c, 0xee, 0xde, 0x77, 0x7f, 0x91, 0x95, 0xd4, 0x40, 0xbf, 0xdc, 0x0f, 0x43, 0x70, 0x99, 0x5e, + 0x5b, 0xa3, 0xd3, 0xc4, 0xdb, 0x3b, 0xd5, 0xd2, 0xbe, 0x6e, 0x5a, 0xa6, 0xd5, 0x50, 0x71, 0xdd, + 0x76, 0x0c, 0xf4, 0x7b, 0x12, 0x64, 0x28, 0x14, 0xae, 0xf7, 0x1c, 0x97, 0xe6, 0xb0, 0x6a, 0xae, + 0x5b, 0x8a, 0xd5, 0x7f, 0x3b, 0xca, 0xbe, 0xd5, 0x30, 0xdd, 0xfd, 0x4e, 0x2d, 0x5f, 0xb7, 0x5b, + 0x4b, 0xfe, 0x9a, 0x8c, 0x5a, 0xf7, 0xf7, 0x52, 0xfb, 0xa0, 0xb1, 0xc4, 0x34, 0x66, 0xa7, 0x63, + 0x1a, 0xf9, 0xdd, 0x5d, 0xa5, 0x7c, 0x7c, 0x94, 0x4d, 0x6f, 0x7b, 0xe0, 0xfe, 0x3c, 0xf9, 0xc8, + 0x6a, 0xba, 0x7d, 0x4a, 0x0d, 0xba, 0x0f, 0x11, 0xbd, 0xce, 0xd6, 0xcc, 0xf5, 0xd8, 0x9d, 0x61, + 0x07, 0x78, 0xda, 0xca, 0xf2, 0xdb, 0x3b, 0xd5, 0x02, 0x43, 0x51, 0x05, 0x5a, 0xee, 0x2a, 0xc4, + 0x7d, 0x22, 0x02, 0x88, 0xec, 0x6e, 0x97, 0x0b, 0x3b, 0x15, 0x79, 0x0c, 0x4d, 0x42, 0x54, 0xad, + 0xac, 0x57, 0x0a, 0xd5, 0x8a, 0x2c, 0xe5, 0xfe, 0x29, 0x0a, 0x49, 0xae, 0xda, 0xbc, 0x33, 0xed, + 0x15, 0x1c, 0xd2, 0x85, 0x04, 0xc7, 0x1d, 0x88, 0x61, 0x8b, 0x6f, 0xb0, 0x60, 0xcb, 0x91, 0x10, + 0xa2, 0xd8, 0x62, 0xdb, 0x83, 0x2e, 0x73, 0x5d, 0x49, 0xa5, 0x56, 0xbc, 0x18, 0x3d, 0x3e, 0xca, + 0x8e, 0xef, 0xaa, 0x0a, 0x57, 0x9a, 0xdf, 0x96, 0x60, 0xa6, 0xe3, 0x98, 0x44, 0xab, 0x1d, 0x6a, + 0x4d, 0xbb, 0xae, 0x37, 0x4d, 0xf7, 0x50, 0x3b, 0x78, 0x98, 0x9e, 0x60, 0x1c, 0x78, 0xe7, 0x4c, + 0x0d, 0x2e, 0x96, 0x99, 0xdf, 0x75, 0x4c, 0x52, 0x3c, 0x5c, 0x17, 0x08, 0x77, 0x1f, 0xf2, 0xbb, + 0x3f, 0x7b, 0x7c, 0x94, 0x95, 0x77, 0x55, 0x25, 0x58, 0x75, 0x5f, 0x95, 0x3b, 0x7d, 0x8d, 0xd1, + 0x97, 0x21, 0x63, 0xe0, 0xb6, 0x83, 0xeb, 0x3a, 0x65, 0xa4, 0x1a, 0x43, 0xd6, 0x5a, 0xba, 0x65, + 0xee, 0x61, 0xe2, 0x32, 0x61, 0x9e, 0x50, 0xd3, 0xdd, 0x16, 0x7c, 0xe8, 0x0d, 0x51, 0x8f, 0x74, + 0x5f, 0xf1, 0x53, 0x49, 0x66, 0x73, 0x4b, 0x22, 0x1d, 0x61, 0x1b, 0x75, 0xeb, 0xfc, 0x36, 0x88, + 0x3a, 0x8d, 0x4f, 0x58, 0x4b, 0x2a, 0x4c, 0x05, 0x86, 0x60, 0x36, 0x4e, 0x9c, 0xe1, 0xdf, 0x18, + 0x59, 0x3d, 0xab, 0x29, 0xdc, 0x6b, 0x62, 0x9c, 0x71, 0x7b, 0xa2, 0xbf, 0x89, 0xdb, 0xf3, 0x0e, + 0xa4, 0xea, 0x76, 0xb3, 0x89, 0x19, 0x9b, 0x6b, 0xbb, 0xaa, 0x92, 0x8e, 0x31, 0xa6, 0x99, 0x3e, + 0x3e, 0xca, 0x26, 0x4b, 0x7e, 0x0d, 0x65, 0x9f, 0x64, 0x3d, 0x58, 0x44, 0xbf, 0x2f, 0xc1, 0x15, + 0x22, 0xee, 0x93, 0xd6, 0x76, 0x89, 0x56, 0x17, 0x37, 0xca, 0x5b, 0x0f, 0xb0, 0xfd, 0xfa, 0xfc, + 0x45, 0xae, 0x63, 0xf1, 0xa5, 0xe3, 0xa3, 0xec, 0xe9, 0x72, 0x48, 0xbd, 0xec, 0x0d, 0xbc, 0xed, + 0x92, 0xde, 0xaa, 0x4c, 0x09, 0x2e, 0x0d, 0x64, 0xcd, 0xb3, 0x6c, 0x97, 0x78, 0x50, 0x66, 0xcb, + 0x90, 0xe2, 0xbc, 0xe2, 0x49, 0xd7, 0xdc, 0x9f, 0xcc, 0x43, 0x4a, 0xc5, 0xc4, 0xb5, 0x1d, 0xec, + 0x5d, 0xf4, 0xe0, 0x25, 0x0d, 0x5f, 0xe0, 0x92, 0xfe, 0x48, 0x82, 0x19, 0xfa, 0x90, 0x70, 0xcc, + 0xb6, 0x6b, 0x3b, 0x9a, 0x83, 0x1f, 0x39, 0xa6, 0x8b, 0x3d, 0x85, 0x5c, 0x18, 0xb2, 0x6f, 0xbd, + 0x13, 0xc9, 0x97, 0x7d, 0x10, 0x55, 0x60, 0xf0, 0xcb, 0x78, 0xe7, 0x5b, 0xbf, 0xc8, 0xde, 0x1e, + 0x89, 0x95, 0x4e, 0xbe, 0x6d, 0xf2, 0x4a, 0x59, 0x45, 0xc6, 0x09, 0x60, 0x74, 0x05, 0xc2, 0xf4, + 0x32, 0x33, 0x5b, 0x35, 0x5e, 0x8c, 0x1d, 0x1f, 0x65, 0xc3, 0xf4, 0xba, 0xab, 0x8c, 0x8a, 0x5c, + 0x98, 0x15, 0x77, 0xd9, 0x17, 0x2d, 0xec, 0xea, 0x44, 0xd9, 0x92, 0xbe, 0x3c, 0xfa, 0x92, 0xf8, + 0xee, 0x7b, 0x47, 0xc8, 0x1e, 0x08, 0x7c, 0xf7, 0x50, 0xed, 0x44, 0x0d, 0xda, 0x86, 0x14, 0xb5, + 0xfe, 0x6b, 0x3a, 0xc1, 0x1a, 0x9d, 0x32, 0x49, 0xcb, 0x6c, 0xbc, 0xfe, 0xab, 0x4a, 0x1e, 0x34, + 0x69, 0x9b, 0x7c, 0x59, 0x34, 0x0e, 0xec, 0x5b, 0xd2, 0x08, 0xd0, 0x08, 0x5a, 0x81, 0x49, 0x57, + 0xaf, 0x35, 0x3d, 0x38, 0x2e, 0x1b, 0x5f, 0x3d, 0x05, 0x6e, 0x87, 0xb6, 0x0c, 0x60, 0x81, 0xeb, + 0x11, 0x98, 0x52, 0x76, 0x0f, 0xdb, 0x1e, 0x4e, 0x8a, 0xe1, 0x5c, 0x3b, 0x0d, 0xe7, 0xb0, 0x1d, + 0x84, 0x89, 0xbb, 0xa2, 0x4c, 0xd0, 0x1a, 0x24, 0xf8, 0xf3, 0x51, 0xe0, 0x4c, 0x31, 0x9c, 0xd7, + 0x4e, 0xc1, 0x61, 0x56, 0xb7, 0x1e, 0x40, 0x9a, 0x24, 0x3e, 0x85, 0x62, 0x45, 0xb9, 0xd1, 0x49, + 0xd2, 0x97, 0x18, 0xcc, 0xeb, 0xa7, 0x4d, 0x87, 0x9b, 0xa6, 0xd6, 0x9e, 0xfd, 0xa1, 0xe9, 0xee, + 0xef, 0x12, 0xbd, 0x81, 0x3d, 0x0e, 0x16, 0x00, 0x68, 0x09, 0x26, 0xa9, 0x71, 0xe9, 0x98, 0x06, + 0xd6, 0x8c, 0x1a, 0x13, 0xc0, 0xf1, 0x62, 0xea, 0xf8, 0x28, 0x0b, 0x5b, 0x82, 0x5c, 0x2e, 0xaa, + 0xe0, 0x35, 0x29, 0xd7, 0xd0, 0x67, 0x61, 0xba, 0xed, 0xe0, 0xb6, 0xee, 0x60, 0xad, 0x6e, 0xb7, + 0xda, 0x4d, 0xec, 0x62, 0x83, 0x09, 0x9c, 0x98, 0x2a, 0x8b, 0x8a, 0x92, 0x47, 0xe7, 0xcf, 0x00, + 0xdd, 0xa5, 0x2f, 0x4c, 0x82, 0x1d, 0xda, 0x32, 0xce, 0x5a, 0x26, 0x19, 0x55, 0x11, 0x44, 0x74, + 0x08, 0x73, 0xe4, 0x90, 0xb8, 0xb8, 0xa5, 0xb1, 0x7d, 0x27, 0x5a, 0xcb, 0x6c, 0x38, 0x54, 0x69, + 0xa4, 0xa7, 0xd9, 0xfa, 0x4a, 0xa3, 0x73, 0x5d, 0x95, 0xe1, 0xb0, 0xf3, 0x24, 0x1b, 0x02, 0x85, + 0xbf, 0xa1, 0x66, 0xc9, 0x80, 0x2a, 0xf4, 0x16, 0x5c, 0xea, 0x5e, 0x11, 0xa2, 0xb5, 0x3b, 0xb5, + 0xa6, 0x49, 0xf6, 0x31, 0x17, 0x7d, 0x31, 0x75, 0x36, 0x50, 0xb9, 0xed, 0xd5, 0xa1, 0xc3, 0x9e, + 0x5b, 0x5f, 0xa7, 0xbb, 0xa3, 0x37, 0x70, 0x7a, 0x72, 0x51, 0xba, 0x3e, 0x51, 0x5c, 0x7d, 0x76, + 0x94, 0x2d, 0x8f, 0x7c, 0x65, 0x09, 0x6e, 0x2d, 0xb9, 0x0e, 0xc6, 0x01, 0x09, 0x50, 0x12, 0x78, + 0xc1, 0xcb, 0xeb, 0xd1, 0x90, 0x0a, 0xd0, 0x55, 0x49, 0xe9, 0xc4, 0x85, 0xf5, 0x65, 0x00, 0x05, + 0x59, 0x80, 0x1c, 0xfc, 0x50, 0x6f, 0x9a, 0x86, 0xee, 0x62, 0xcd, 0xb4, 0x0c, 0xfc, 0x18, 0x93, + 0x34, 0x62, 0x5b, 0xff, 0xee, 0xe8, 0x5b, 0xaf, 0xfa, 0x18, 0x0a, 0x85, 0xf0, 0x6c, 0x7b, 0xa7, + 0x97, 0x8c, 0x09, 0xfa, 0x4b, 0x09, 0x90, 0x7f, 0xdb, 0x5b, 0xb6, 0x61, 0xee, 0x99, 0xd8, 0x21, + 0xe9, 0x19, 0x36, 0xe0, 0x07, 0xe7, 0x10, 0x9a, 0x02, 0x63, 0xc3, 0x83, 0x78, 0x31, 0x32, 0x73, + 0xda, 0xe8, 0xc7, 0x45, 0x57, 0x21, 0x65, 0xe0, 0x5a, 0xa7, 0xa1, 0xb5, 0xf5, 0x0e, 0xc1, 0x9a, + 0x6d, 0xa5, 0x67, 0x99, 0xbe, 0x49, 0x30, 0xea, 0x36, 0x25, 0x6e, 0x59, 0x99, 0x3f, 0x0b, 0xc1, + 0xf4, 0x09, 0x41, 0x8e, 0x76, 0x20, 0x64, 0x72, 0xbb, 0x3a, 0x59, 0xa4, 0x2a, 0x3e, 0xa4, 0x94, + 0x9f, 0x1d, 0x3d, 0xd7, 0x04, 0x43, 0xa6, 0x81, 0x1a, 0x10, 0xa7, 0x57, 0x8d, 0xbf, 0x4b, 0x43, + 0x0c, 0x7c, 0x8d, 0xbe, 0x4b, 0xb7, 0x19, 0xf1, 0xb9, 0x87, 0x88, 0x71, 0x70, 0xc5, 0x40, 0x59, + 0x98, 0x74, 0x6d, 0x0d, 0x3f, 0x36, 0x89, 0x6b, 0x5a, 0x0d, 0x66, 0x8f, 0xc6, 0x54, 0x70, 0xed, + 0x8a, 0xa0, 0xa0, 0x9b, 0x30, 0x69, 0xe1, 0x47, 0x9a, 0x51, 0xd3, 0x2c, 0x5d, 0xa8, 0xd1, 0x78, + 0x31, 0x79, 0x7c, 0x94, 0x8d, 0x6f, 0xe2, 0x47, 0xe5, 0xe2, 0xa6, 0xde, 0xc2, 0x6a, 0xdc, 0xc2, + 0x8f, 0xca, 0x35, 0xfa, 0x33, 0xf3, 0xc7, 0x21, 0x40, 0x27, 0x55, 0x03, 0xfa, 0x3b, 0x09, 0xae, + 0x78, 0x36, 0xad, 0xed, 0x98, 0x0d, 0xd3, 0xd2, 0x9b, 0x3d, 0xc6, 0x2d, 0x7f, 0x5e, 0x7d, 0xf2, + 0x3c, 0xfa, 0x47, 0x18, 0xbc, 0x5b, 0x02, 0xbe, 0xdf, 0xf0, 0xbd, 0x42, 0xed, 0x2f, 0x6e, 0xf8, + 0x9e, 0x68, 0x72, 0x5f, 0x4d, 0x77, 0x4e, 0xe9, 0x9c, 0xb9, 0x0b, 0x2f, 0x0d, 0x05, 0x3e, 0x8f, + 0xd9, 0x92, 0xf9, 0x96, 0x04, 0xf3, 0xa7, 0x18, 0x03, 0x41, 0x9c, 0x24, 0xc7, 0xb9, 0xd7, 0xfb, + 0xf0, 0xfd, 0xd2, 0x73, 0x18, 0x1c, 0xc1, 0x49, 0xac, 0xc0, 0xe5, 0x53, 0xe5, 0xe8, 0x59, 0xab, + 0x89, 0x05, 0x81, 0xfe, 0x55, 0x82, 0xa9, 0x3e, 0xb1, 0x80, 0x3e, 0x0e, 0xdc, 0x07, 0xe5, 0xf8, + 0x28, 0x1b, 0x65, 0x83, 0xbc, 0x90, 0x4b, 0x71, 0x70, 0xf2, 0x52, 0x6c, 0xd2, 0x11, 0xd8, 0xc0, + 0x6c, 0x84, 0xf7, 0x2f, 0x3c, 0x02, 0x87, 0xe8, 0x5e, 0x8c, 0xcc, 0xdf, 0x4b, 0x20, 0xf7, 0x4b, + 0x20, 0xb4, 0x05, 0x32, 0x7e, 0xec, 0x3a, 0xba, 0x16, 0x30, 0x19, 0xa4, 0xf3, 0x98, 0x0c, 0x29, + 0xd6, 0x7d, 0xc7, 0xb7, 0x1b, 0x3e, 0x85, 0xa4, 0x83, 0x1b, 0xd4, 0xb0, 0xaf, 0xdb, 0xd6, 0x9e, + 0xd9, 0x10, 0x27, 0xfd, 0xf6, 0xc8, 0x76, 0x51, 0x5e, 0x65, 0xdd, 0x4b, 0xac, 0xb7, 0x9a, 0x70, + 0x02, 0xa5, 0xcc, 0x37, 0x25, 0x98, 0x1b, 0x2c, 0x44, 0x07, 0xf0, 0xda, 0x76, 0x2f, 0xaf, 0xdd, + 0xbe, 0xb8, 0x9c, 0x0e, 0x70, 0xc8, 0x5a, 0x38, 0x26, 0xc9, 0xa1, 0xb5, 0x70, 0x2c, 0x29, 0xa7, + 0x72, 0x6f, 0x50, 0x66, 0x61, 0x3d, 0x7d, 0x8f, 0xc8, 0x4b, 0x00, 0xfb, 0x66, 0x63, 0x5f, 0x7b, + 0xa4, 0xbb, 0xd8, 0x11, 0x8e, 0xef, 0x38, 0xa5, 0x7c, 0x48, 0x09, 0xb9, 0x9f, 0x25, 0x20, 0xa9, + 0xb4, 0xda, 0xb6, 0xe3, 0x7a, 0x16, 0xfd, 0x3a, 0x44, 0xb8, 0x0d, 0x21, 0xb6, 0x3d, 0x3f, 0x64, + 0x9a, 0x3d, 0x3d, 0xb9, 0x0d, 0x28, 0x94, 0x96, 0xc0, 0x40, 0x5b, 0x10, 0xe5, 0x86, 0x17, 0x49, + 0xcf, 0x33, 0xb8, 0xa5, 0x91, 0xe1, 0xb8, 0x09, 0xe7, 0x99, 0x5b, 0x02, 0x05, 0x29, 0x30, 0x41, + 0x39, 0x83, 0xa4, 0x33, 0x0c, 0xee, 0xe6, 0xe8, 0xb3, 0x3b, 0x6c, 0x7b, 0x93, 0xe3, 0x08, 0xbe, + 0x19, 0x1f, 0x1a, 0x68, 0xc6, 0xbf, 0x07, 0x11, 0x1e, 0x21, 0x11, 0x7e, 0xcf, 0xec, 0x00, 0xbf, + 0x94, 0xb2, 0xb5, 0x6c, 0x36, 0xf1, 0x32, 0x6b, 0xe6, 0x2d, 0x9c, 0x77, 0x42, 0xaf, 0x42, 0x8c, + 0x10, 0x57, 0x23, 0xe6, 0xd7, 0xb8, 0x44, 0x1f, 0xe7, 0xce, 0xfd, 0x6a, 0x75, 0xa7, 0x6a, 0x7e, + 0x0d, 0xab, 0x51, 0x42, 0x5c, 0xfa, 0x03, 0x2d, 0x00, 0xb3, 0x0d, 0x89, 0x4e, 0x2d, 0x3e, 0x66, + 0xdc, 0x8d, 0xab, 0x01, 0x0a, 0xc3, 0x39, 0x30, 0xdb, 0xda, 0xde, 0x01, 0xe1, 0x16, 0x95, 0xc0, + 0x39, 0x30, 0xdb, 0xcb, 0x77, 0x89, 0x1a, 0xa5, 0x95, 0xcb, 0x07, 0x04, 0x65, 0x20, 0xf6, 0x48, + 0x6f, 0x36, 0xd9, 0x43, 0x6c, 0x82, 0xa1, 0xf8, 0xe5, 0x5e, 0x55, 0x17, 0xf9, 0xd5, 0xaa, 0x3a, + 0xf1, 0xf4, 0x69, 0xeb, 0xee, 0x3e, 0x7b, 0xcc, 0xc7, 0x55, 0xe0, 0xa4, 0x6d, 0xdd, 0xdd, 0x47, + 0x69, 0x88, 0xf2, 0x75, 0x91, 0x74, 0x6c, 0x71, 0xfc, 0x7a, 0x42, 0xf5, 0x8a, 0xe8, 0x35, 0x98, + 0xe2, 0x3e, 0x4c, 0xcd, 0x30, 0x1d, 0x5c, 0x77, 0x9b, 0x87, 0xcc, 0x1a, 0x8c, 0xa9, 0x29, 0x4e, + 0x2e, 0x0b, 0x2a, 0xba, 0x01, 0x72, 0xbf, 0xf9, 0xcc, 0xac, 0xb8, 0x98, 0x3a, 0xd5, 0x67, 0x3d, + 0x53, 0x4b, 0x5b, 0xb0, 0x4d, 0xc0, 0x2c, 0x4d, 0x73, 0x4b, 0x5b, 0x54, 0x74, 0x4d, 0xd2, 0x1b, + 0x20, 0x0b, 0xdb, 0xb9, 0xdb, 0x36, 0xc9, 0x71, 0x39, 0xbd, 0xdb, 0x34, 0x0f, 0x33, 0x6d, 0xdd, + 0x21, 0x58, 0xab, 0x75, 0x2c, 0xa3, 0x89, 0x35, 0x8e, 0x95, 0x4e, 0xb1, 0xd6, 0xd3, 0xac, 0xaa, + 0xc8, 0x6a, 0x38, 0x0b, 0x9f, 0xe5, 0xf3, 0x98, 0xfb, 0x4d, 0xf8, 0x3c, 0xae, 0x83, 0x6c, 0xe0, + 0x3d, 0xbd, 0xd3, 0x74, 0x35, 0xd3, 0x12, 0x7c, 0x7a, 0x99, 0x9a, 0xdf, 0x6a, 0x4a, 0xd0, 0x15, + 0x8b, 0x73, 0xe8, 0x37, 0x60, 0xde, 0xb7, 0x35, 0xdb, 0x8e, 0xd9, 0xd2, 0x9d, 0x43, 0x8d, 0x0b, + 0xc1, 0xf4, 0x67, 0x98, 0xa9, 0xb2, 0xfc, 0xec, 0x28, 0x5b, 0xbc, 0x28, 0xff, 0x70, 0xe1, 0xca, + 0x6c, 0x9c, 0x4b, 0xde, 0x30, 0xdb, 0x7c, 0x14, 0x5e, 0x95, 0xf9, 0x41, 0x08, 0x26, 0x98, 0x68, + 0x41, 0xb7, 0x21, 0x4c, 0x7b, 0x09, 0x7f, 0xe2, 0xa8, 0x4f, 0x51, 0xd6, 0x07, 0x21, 0x08, 0x33, + 0xeb, 0x0a, 0x31, 0x9e, 0x64, 0xbf, 0xd1, 0x3c, 0x44, 0x09, 0x7e, 0xa0, 0x3d, 0xd4, 0x9b, 0xe9, + 0x19, 0x76, 0x65, 0x22, 0x04, 0x3f, 0xb8, 0xaf, 0x37, 0xd1, 0x25, 0x88, 0x98, 0x44, 0xb3, 0xf0, + 0x23, 0x66, 0xa5, 0xc6, 0xd4, 0x09, 0x93, 0x6c, 0xe2, 0x47, 0xe8, 0x33, 0x10, 0x7f, 0xa4, 0x13, + 0x0d, 0xb7, 0xda, 0xee, 0x21, 0x3b, 0xb5, 0x18, 0xbd, 0x64, 0xa4, 0x42, 0xcb, 0xcc, 0xcc, 0xd3, + 0x9d, 0x06, 0x76, 0xb5, 0xba, 0xdd, 0xe4, 0xef, 0xca, 0x38, 0x7d, 0x06, 0x53, 0x52, 0xc9, 0x6e, + 0x92, 0xb5, 0x70, 0x2c, 0x24, 0x8f, 0xaf, 0x85, 0x63, 0xe3, 0x72, 0x78, 0x2d, 0x1c, 0x0b, 0xcb, + 0x13, 0x6b, 0xe1, 0xd8, 0x84, 0x1c, 0x59, 0x0b, 0xc7, 0x22, 0x72, 0x74, 0x2d, 0x1c, 0x8b, 0xca, + 0xb1, 0xb5, 0x70, 0x2c, 0x26, 0xc7, 0xd7, 0xc2, 0xb1, 0xb8, 0x0c, 0x6b, 0xe1, 0x18, 0xc8, 0x93, + 0x6b, 0xe1, 0xd8, 0xa4, 0x9c, 0x58, 0x0b, 0xc7, 0x12, 0x72, 0x92, 0x0b, 0xf9, 0xb5, 0x70, 0x2c, + 0x25, 0x4f, 0xad, 0x85, 0x63, 0x53, 0xb2, 0xbc, 0x16, 0x8e, 0xc9, 0xf2, 0xf4, 0x5a, 0x38, 0x36, + 0x2d, 0xa3, 0x4c, 0x45, 0xc4, 0x9a, 0x74, 0xf4, 0xa5, 0x9e, 0x7d, 0x1a, 0xf9, 0x89, 0xcc, 0x3a, + 0x65, 0x0a, 0x10, 0xa6, 0xa2, 0x12, 0xbd, 0xdb, 0x03, 0x32, 0xa2, 0xf2, 0x65, 0x5d, 0x72, 0x3f, + 0x92, 0x40, 0xae, 0xe2, 0x07, 0x1d, 0x6c, 0xd5, 0xf1, 0x7d, 0xbd, 0x59, 0xda, 0xef, 0x58, 0x07, + 0xe8, 0x55, 0x98, 0xaa, 0xd3, 0x1f, 0x1a, 0x77, 0x0c, 0xd3, 0x4d, 0x97, 0xd8, 0xa6, 0x27, 0x19, + 0xb9, 0x4a, 0xa9, 0x74, 0xef, 0x5f, 0x02, 0x10, 0xed, 0x28, 0x4b, 0x86, 0x58, 0x93, 0x38, 0x6f, + 0x42, 0xb9, 0xb1, 0x0f, 0xc6, 0xb1, 0x1f, 0x31, 0xf9, 0xdc, 0x03, 0xa3, 0xda, 0x8f, 0xd0, 0x12, + 0xcc, 0x5a, 0xf8, 0xb1, 0xab, 0xf5, 0x37, 0x66, 0xb2, 0x58, 0x9d, 0xa6, 0x75, 0xa5, 0x60, 0x87, + 0xdc, 0x3f, 0x84, 0x60, 0xca, 0x9b, 0xb4, 0xa7, 0x0b, 0xf7, 0x40, 0xa6, 0x0c, 0x62, 0x1a, 0x9a, + 0x6b, 0x73, 0x24, 0x4f, 0x2b, 0xbe, 0x37, 0xcc, 0xa3, 0xd7, 0x8b, 0x42, 0xcb, 0x8a, 0xb1, 0x63, + 0xb3, 0xe1, 0xb8, 0x71, 0xa0, 0x26, 0x49, 0x90, 0x96, 0xd9, 0x85, 0x94, 0xd7, 0x89, 0x53, 0x50, + 0x09, 0x22, 0x3d, 0xe3, 0x7d, 0x76, 0x84, 0xf1, 0xbc, 0xad, 0x56, 0x45, 0xd7, 0xcc, 0xd7, 0x01, + 0x9d, 0x1c, 0x3b, 0x68, 0x98, 0x4c, 0x70, 0xc3, 0x64, 0xab, 0xd7, 0x30, 0x79, 0xf7, 0x7c, 0x6b, + 0x0b, 0x4c, 0x3b, 0xe8, 0x3e, 0xfc, 0xce, 0x38, 0xa4, 0xb8, 0x06, 0xf6, 0x6d, 0x11, 0x2a, 0x8f, + 0xa9, 0xb8, 0x37, 0xad, 0x46, 0x37, 0xf4, 0x46, 0xd7, 0x17, 0x52, 0x65, 0xaf, 0xc2, 0x6f, 0xfc, + 0x0a, 0xb5, 0xdb, 0x74, 0xa3, 0x37, 0x46, 0x17, 0xa2, 0xf6, 0x97, 0x6e, 0xf8, 0x8d, 0xae, 0x41, + 0x8a, 0xd9, 0xde, 0xdd, 0x56, 0xe3, 0xac, 0x55, 0x92, 0x51, 0xfd, 0x66, 0x45, 0x48, 0x92, 0xb6, + 0x1e, 0x88, 0xf7, 0x85, 0x87, 0x86, 0x9a, 0x84, 0x2a, 0x4f, 0xd0, 0x3e, 0x41, 0x43, 0xca, 0xc1, + 0xa4, 0xd3, 0xc2, 0x5a, 0xdb, 0xe6, 0xde, 0xb0, 0x71, 0x35, 0xce, 0x29, 0xdb, 0x36, 0x41, 0xbb, + 0x8c, 0x55, 0xd8, 0x5e, 0x68, 0x06, 0xdf, 0x9c, 0x74, 0x64, 0xa0, 0x6f, 0x69, 0xc8, 0x76, 0xaa, + 0x53, 0xa4, 0x8f, 0x03, 0x3f, 0x80, 0x28, 0xe9, 0xb4, 0xa8, 0x34, 0x64, 0xda, 0x74, 0xf2, 0xd6, + 0xe2, 0x80, 0x39, 0x17, 0x3b, 0xcd, 0x83, 0xad, 0x76, 0x95, 0xb7, 0xf3, 0x0d, 0x26, 0x5e, 0xcc, + 0xfd, 0x95, 0x04, 0xf3, 0xf4, 0x96, 0xf2, 0xeb, 0x5e, 0x62, 0xb9, 0x17, 0x1e, 0xba, 0x0e, 0x51, + 0x66, 0x66, 0xfb, 0xcf, 0x89, 0xd5, 0xe3, 0xa3, 0x6c, 0x84, 0xb6, 0x7e, 0x6e, 0xa3, 0x20, 0x42, + 0x81, 0x15, 0xe6, 0x1e, 0x72, 0x1d, 0xdd, 0x22, 0x2c, 0x34, 0x48, 0x0f, 0xbe, 0x85, 0x5b, 0x35, + 0xec, 0xf0, 0xe3, 0x4c, 0xa8, 0xb3, 0x3d, 0x95, 0x1b, 0xbc, 0x2e, 0x97, 0x81, 0x74, 0xff, 0x94, + 0x7d, 0x27, 0xf4, 0xff, 0x83, 0xb9, 0x4d, 0xfc, 0x68, 0xd0, 0x6a, 0x8a, 0x10, 0xe5, 0xe2, 0xd6, + 0xbb, 0x34, 0xd7, 0xfb, 0x85, 0x56, 0x30, 0xfd, 0x24, 0xcf, 0x66, 0xba, 0xc3, 0x3a, 0xa8, 0x5e, + 0xc7, 0xdc, 0xa7, 0x30, 0xdf, 0x87, 0xee, 0x33, 0xc0, 0x07, 0x10, 0x21, 0xae, 0xee, 0x0a, 0xc3, + 0x38, 0x35, 0x0a, 0x7a, 0xd5, 0xd5, 0xdd, 0x0e, 0x51, 0x45, 0xbf, 0xdc, 0x35, 0x78, 0xa5, 0xd0, + 0x71, 0x6d, 0xca, 0x62, 0xe2, 0x35, 0x81, 0xeb, 0xb6, 0x55, 0x37, 0x9b, 0x66, 0x30, 0x20, 0x9a, + 0x7b, 0x15, 0xae, 0x0e, 0x6b, 0xe6, 0xef, 0x84, 0xca, 0xbc, 0xf1, 0x9d, 0x16, 0xa6, 0x2d, 0xd7, + 0x4d, 0xe2, 0xa2, 0x0f, 0x20, 0x21, 0x78, 0x74, 0x94, 0x88, 0xaa, 0x17, 0x1e, 0x77, 0x7c, 0x10, + 0x92, 0xfb, 0x6b, 0x09, 0x66, 0xca, 0x8e, 0xdd, 0x6e, 0x63, 0x43, 0xe8, 0x51, 0xbe, 0xb7, 0x9e, + 0xfa, 0x94, 0x02, 0xea, 0x73, 0x13, 0x42, 0x4a, 0x59, 0xbc, 0x12, 0xef, 0x3c, 0xef, 0xe3, 0x53, + 0x29, 0xa3, 0x77, 0xf9, 0x06, 0x77, 0x08, 0x93, 0xe8, 0xa9, 0x5b, 0x2f, 0x0f, 0x8d, 0x42, 0x77, + 0x77, 0xb6, 0x43, 0x72, 0xff, 0x3b, 0x01, 0x97, 0x82, 0x87, 0xb6, 0x52, 0xf2, 0x26, 0xfe, 0x15, + 0x88, 0x7a, 0xfe, 0xb8, 0x11, 0x24, 0xf7, 0x20, 0x88, 0xbc, 0xd8, 0x8f, 0xa0, 0x4f, 0xce, 0xc3, + 0x44, 0x1f, 0xf9, 0xaf, 0x25, 0x1e, 0xb1, 0xb8, 0x7d, 0x61, 0xf4, 0x72, 0xdf, 0xcb, 0xa9, 0xc7, + 0x68, 0x67, 0x2a, 0xee, 0x57, 0x64, 0xb4, 0xdf, 0x87, 0x08, 0xf7, 0x65, 0x8b, 0xe0, 0xe1, 0x9d, + 0x8b, 0x2e, 0x81, 0x3b, 0xcb, 0x55, 0x81, 0x96, 0xf9, 0x43, 0x09, 0x12, 0xc1, 0xad, 0x43, 0x26, + 0xc4, 0xd8, 0xb6, 0x79, 0xe2, 0x66, 0xfc, 0x85, 0xfb, 0x16, 0xf8, 0xb1, 0x28, 0x06, 0xb5, 0xd4, + 0x0c, 0xc7, 0x6e, 0x77, 0x83, 0xc7, 0xe3, 0x6a, 0x8c, 0x12, 0xa8, 0x15, 0x9c, 0xf9, 0x06, 0xc4, + 0xfd, 0x4d, 0x0f, 0x38, 0x17, 0xc7, 0x5f, 0xa0, 0x73, 0x71, 0xe8, 0xf8, 0x65, 0x48, 0xf6, 0xec, + 0x18, 0x9a, 0xf3, 0xe7, 0x10, 0x2e, 0x46, 0xf8, 0x1c, 0xce, 0x44, 0x09, 0x9a, 0x90, 0xb9, 0x1f, + 0x46, 0x61, 0x66, 0x90, 0x44, 0xfc, 0x18, 0xe4, 0x80, 0x3c, 0xd0, 0x9a, 0x26, 0x71, 0x05, 0x9f, + 0xde, 0x18, 0xee, 0x7c, 0x08, 0x08, 0x15, 0xc1, 0x96, 0x29, 0xa7, 0x57, 0xd4, 0x7c, 0x0a, 0x29, + 0x83, 0x2f, 0x42, 0x84, 0x1c, 0x44, 0xe6, 0xd6, 0x30, 0x77, 0xc1, 0x00, 0xc1, 0x22, 0xd0, 0x93, + 0x46, 0xa0, 0x8a, 0xa0, 0x3a, 0x24, 0x7d, 0x70, 0xf6, 0xd8, 0xa7, 0x8f, 0xc5, 0xe7, 0x17, 0x32, + 0x09, 0x6f, 0x14, 0xf6, 0xfc, 0x6f, 0xc0, 0x94, 0x37, 0x88, 0xe7, 0xa2, 0x88, 0xbf, 0x90, 0x61, + 0xbc, 0x8d, 0xa9, 0x0a, 0x97, 0xc5, 0xb7, 0x25, 0x98, 0xf1, 0x46, 0xf2, 0x5f, 0x52, 0x22, 0x5d, + 0x2b, 0x59, 0xac, 0x1e, 0x1f, 0x65, 0xa7, 0xc5, 0xce, 0x78, 0x7e, 0x9e, 0xe7, 0xe6, 0xc1, 0x69, + 0xa3, 0x0f, 0xd0, 0xa0, 0xba, 0x9e, 0xd6, 0x7b, 0x59, 0x58, 0x42, 0xd7, 0x53, 0xdb, 0xfd, 0xf9, + 0x75, 0x3d, 0xfd, 0xa9, 0x18, 0xe8, 0x3b, 0x12, 0x4c, 0xf3, 0x90, 0x61, 0xab, 0xe3, 0xea, 0x3c, + 0x69, 0xc0, 0x73, 0x38, 0x7c, 0x7c, 0x7c, 0x94, 0x9d, 0x62, 0xc7, 0xbb, 0x21, 0xea, 0xd8, 0xb0, + 0x17, 0x7e, 0x37, 0x76, 0x51, 0xc4, 0xfb, 0xdc, 0x27, 0x18, 0xe8, 0x2e, 0xa4, 0xb8, 0x17, 0xc6, + 0x4b, 0x1d, 0x65, 0xb6, 0x53, 0xb2, 0x78, 0xf5, 0xd9, 0x51, 0x76, 0x71, 0xc0, 0x3d, 0xe1, 0x0e, + 0x9c, 0xfb, 0xbc, 0xad, 0x9a, 0xdc, 0x0b, 0x16, 0xd1, 0x3a, 0x4c, 0x71, 0x13, 0xb3, 0x9b, 0x98, + 0x05, 0xa3, 0x07, 0xba, 0xb9, 0x79, 0xea, 0x53, 0xb9, 0xb7, 0x2e, 0x37, 0x07, 0xb3, 0x03, 0x6d, + 0x9b, 0x9f, 0x47, 0x60, 0xae, 0x57, 0xc4, 0xfa, 0xd6, 0x87, 0xd6, 0xaf, 0xc7, 0xde, 0x1f, 0x59, + 0x4c, 0xfb, 0x29, 0x5a, 0x4c, 0x4c, 0x7a, 0xa5, 0x7e, 0x4d, 0xf6, 0x95, 0x3e, 0x4d, 0x76, 0x01, + 0x7c, 0x76, 0xbc, 0x7d, 0xf8, 0x9e, 0x3a, 0xfb, 0xc8, 0xd7, 0x32, 0xdc, 0x9d, 0xf6, 0xc1, 0x05, + 0xe0, 0x59, 0x7f, 0x3f, 0xbd, 0xcc, 0xd3, 0x33, 0x3f, 0x91, 0x20, 0xd9, 0xb3, 0xb2, 0x5f, 0xa7, + 0xa2, 0xd9, 0xf6, 0x6d, 0x16, 0x9e, 0x78, 0xf5, 0xce, 0xf9, 0x97, 0xd5, 0x6b, 0xca, 0x64, 0xfe, + 0x56, 0x82, 0x64, 0xcf, 0x46, 0xfe, 0x8a, 0x54, 0xd4, 0x8b, 0x9f, 0x79, 0x0d, 0x52, 0xbd, 0x47, + 0x14, 0x18, 0x43, 0x7a, 0x31, 0x63, 0xe4, 0xbe, 0x08, 0x11, 0x4e, 0x41, 0x08, 0x52, 0x1f, 0x16, + 0x94, 0x1d, 0x65, 0x73, 0x45, 0x5b, 0xde, 0x52, 0xb5, 0x95, 0x92, 0x3c, 0x86, 0x12, 0x10, 0x2b, + 0x57, 0xd6, 0x2b, 0x94, 0x28, 0x4b, 0x68, 0x12, 0xa2, 0xac, 0x54, 0x29, 0xcb, 0xa1, 0x5c, 0x11, + 0x64, 0x8e, 0xbd, 0x87, 0xa9, 0x9a, 0xa1, 0xd6, 0x3e, 0xca, 0xc3, 0x0c, 0xb3, 0xcc, 0x5b, 0xd4, + 0xca, 0xa2, 0xd7, 0x5b, 0x0b, 0xd8, 0xb8, 0xd3, 0x7e, 0x15, 0xbd, 0xbd, 0x9b, 0x7a, 0x0b, 0xe7, + 0xfe, 0x26, 0x0c, 0xd3, 0x5d, 0x10, 0x4f, 0xc9, 0xfe, 0x85, 0xd4, 0x7d, 0x77, 0x44, 0xce, 0x0c, + 0xf9, 0x9e, 0xe8, 0x2f, 0x9e, 0x20, 0x22, 0xf4, 0xfa, 0x21, 0xbd, 0x34, 0xcf, 0x8e, 0xb2, 0xd3, + 0xfd, 0x93, 0x25, 0xcf, 0x19, 0x93, 0xf5, 0xa6, 0xc8, 0x1c, 0xca, 0xa6, 0x75, 0xa0, 0x75, 0x73, + 0xe3, 0xb8, 0x43, 0xd9, 0xb4, 0x0e, 0x76, 0x55, 0x45, 0x8d, 0xd2, 0xca, 0x5d, 0xc7, 0x44, 0x6b, + 0x10, 0xb6, 0xdb, 0xae, 0xf7, 0x54, 0x7e, 0xfb, 0x5c, 0x4b, 0xda, 0x6a, 0x8b, 0xf5, 0xa8, 0x0c, + 0x03, 0xad, 0xf1, 0x2c, 0x86, 0xee, 0x46, 0x8b, 0xc7, 0xec, 0x48, 0x22, 0x34, 0xd9, 0x73, 0x10, + 0x99, 0x06, 0x24, 0x82, 0x3b, 0x36, 0x20, 0xce, 0x52, 0xe8, 0x75, 0x67, 0x7c, 0x76, 0xa4, 0xa9, + 0x8b, 0x87, 0x60, 0x20, 0xf4, 0xf6, 0x45, 0x88, 0xfb, 0xeb, 0x38, 0x4f, 0x04, 0xd2, 0x8f, 0xc8, + 0x70, 0x77, 0x1f, 0xb5, 0xcf, 0xbe, 0x1f, 0x82, 0x84, 0x8a, 0x89, 0xdd, 0x7c, 0x88, 0x0d, 0x6a, + 0x41, 0xf9, 0xb9, 0xd8, 0xd2, 0xe8, 0xb9, 0xd8, 0x05, 0x88, 0x77, 0x35, 0xd0, 0x39, 0xf2, 0x21, + 0xbb, 0xbd, 0xd0, 0xc7, 0x90, 0xac, 0xd9, 0x1d, 0xcb, 0xd0, 0x9d, 0x43, 0x66, 0x57, 0x31, 0x0b, + 0x24, 0x35, 0x34, 0x3b, 0x2d, 0x38, 0xeb, 0x7c, 0x51, 0x74, 0xa6, 0xf6, 0x93, 0x9a, 0xa8, 0x05, + 0x4a, 0xb9, 0xf7, 0x20, 0x11, 0xac, 0x45, 0x31, 0x08, 0x6f, 0x6e, 0x6d, 0x56, 0xf8, 0x9d, 0x2c, + 0x16, 0x4a, 0x77, 0x97, 0x95, 0xf5, 0x75, 0x59, 0xa2, 0xf4, 0xca, 0x47, 0xca, 0x8e, 0x1c, 0xe2, + 0x19, 0xa4, 0xd5, 0x9d, 0x82, 0xba, 0xe3, 0x79, 0x45, 0x73, 0x18, 0x92, 0xc1, 0xf1, 0xa8, 0xe4, + 0xa3, 0x66, 0x27, 0x23, 0xf4, 0xbc, 0x68, 0x5f, 0x1b, 0x71, 0xc6, 0x1e, 0x07, 0x39, 0x41, 0xd4, + 0xdc, 0x3f, 0x87, 0x00, 0x75, 0x0f, 0xde, 0x17, 0x56, 0x1f, 0x01, 0xd4, 0xf7, 0x71, 0xfd, 0xa0, + 0x6d, 0x9b, 0x96, 0x2b, 0x92, 0xd9, 0xde, 0x19, 0x89, 0x77, 0x7c, 0x61, 0x55, 0xf2, 0xfb, 0xab, + 0x01, 0x2c, 0xf4, 0x07, 0xc3, 0xfd, 0xff, 0xe3, 0xcc, 0xff, 0xcf, 0x6e, 0xfe, 0xaf, 0x35, 0x06, + 0x90, 0x29, 0x00, 0x74, 0x67, 0x8c, 0xde, 0x1a, 0x2d, 0x0f, 0xdb, 0x8b, 0xa1, 0xb1, 0xb6, 0x41, + 0x5e, 0xcf, 0xfd, 0x4f, 0x18, 0x50, 0xc9, 0xc1, 0xba, 0x8b, 0xa9, 0x88, 0x26, 0xc3, 0x5c, 0x07, + 0x45, 0x98, 0x60, 0x76, 0x81, 0x60, 0xe4, 0x11, 0x5d, 0xf9, 0x7e, 0xf8, 0x8e, 0x45, 0x03, 0xbe, + 0x0a, 0x89, 0xba, 0xdd, 0xec, 0xb4, 0x2c, 0x8d, 0xe5, 0x42, 0x89, 0xf7, 0xc7, 0x17, 0x86, 0x9d, + 0xd8, 0x89, 0xc9, 0xe5, 0x4b, 0x76, 0x93, 0x96, 0xfd, 0x2f, 0x05, 0x18, 0x20, 0x6b, 0x81, 0xae, + 0x40, 0xdc, 0x97, 0x3c, 0x3c, 0x29, 0x43, 0xed, 0x12, 0xd0, 0x2d, 0x98, 0xd0, 0x89, 0x66, 0xef, + 0x31, 0x63, 0xfa, 0xac, 0xab, 0xa8, 0x86, 0x75, 0xb2, 0xb5, 0x87, 0x5e, 0x87, 0xe9, 0x96, 0xfe, + 0x58, 0xdb, 0x73, 0x78, 0x76, 0xb5, 0x66, 0x1a, 0x4d, 0x2e, 0x09, 0x25, 0x75, 0xaa, 0xa5, 0x3f, + 0x5e, 0x16, 0x74, 0xc5, 0x68, 0x62, 0xf4, 0x16, 0x24, 0xf7, 0x1e, 0xf0, 0xa7, 0x15, 0xd7, 0x4a, + 0x3c, 0xb1, 0x6c, 0xea, 0xf8, 0x28, 0x3b, 0xb9, 0x7c, 0x8f, 0x6d, 0x0c, 0x0b, 0x9a, 0x4c, 0xee, + 0x3d, 0xf0, 0x0b, 0x99, 0xff, 0x96, 0x20, 0x2a, 0x56, 0x84, 0xda, 0x00, 0x62, 0x7b, 0x4c, 0x83, + 0x9f, 0x69, 0xb2, 0x78, 0xef, 0xf8, 0x28, 0x1b, 0x2f, 0x31, 0xaa, 0x52, 0x26, 0xcf, 0x8e, 0xb2, + 0x1f, 0x5c, 0x54, 0xa3, 0x78, 0x20, 0x6a, 0x9c, 0x0f, 0xa2, 0x18, 0xcc, 0x63, 0xbb, 0xaf, 0x13, + 0x6d, 0xdf, 0x24, 0xae, 0xdd, 0x70, 0xf4, 0x96, 0xc8, 0x66, 0x48, 0xec, 0xeb, 0x64, 0xd5, 0xa3, + 0xa1, 0x0c, 0xb5, 0xcd, 0x1e, 0xf2, 0x54, 0x36, 0x9e, 0x0a, 0xe3, 0x97, 0xd1, 0x2d, 0xb8, 0xe4, + 0x77, 0xd6, 0xe8, 0x4e, 0xd5, 0x3a, 0xf5, 0x03, 0xcc, 0x74, 0x10, 0x15, 0xee, 0x33, 0x7e, 0xe5, + 0x86, 0xfe, 0xb8, 0xc8, 0xab, 0x72, 0x97, 0x60, 0x26, 0x70, 0xac, 0xbe, 0x25, 0x8d, 0x41, 0xe6, + 0x49, 0x17, 0x81, 0x0f, 0x0d, 0xee, 0xc1, 0x54, 0xdf, 0x27, 0x65, 0x42, 0xfe, 0x06, 0x3d, 0x79, + 0xbd, 0xdf, 0xa0, 0xe5, 0x4b, 0xbc, 0xe8, 0xbd, 0x0d, 0x52, 0xf5, 0x9e, 0x72, 0xee, 0x4d, 0x98, + 0xf6, 0x87, 0xf1, 0x05, 0xc9, 0x15, 0x88, 0xb3, 0x68, 0x7b, 0x4b, 0x77, 0x0e, 0xbc, 0x88, 0xbb, + 0x4f, 0xc8, 0x65, 0xe1, 0x25, 0xe6, 0xdd, 0xbb, 0xb7, 0xce, 0x66, 0x5c, 0xb2, 0x5b, 0x6d, 0x7e, + 0xec, 0x9e, 0xfb, 0x6f, 0x11, 0x16, 0x06, 0x37, 0xf0, 0x17, 0xf7, 0x13, 0x19, 0xa2, 0xdb, 0xfa, + 0x61, 0xd3, 0xd6, 0x0d, 0xb4, 0x08, 0x93, 0x5e, 0x92, 0x9b, 0xb7, 0xa0, 0xb8, 0x1a, 0x24, 0xf5, + 0xf2, 0xb1, 0xcc, 0xc2, 0x52, 0x01, 0x3e, 0x36, 0x21, 0xd5, 0x21, 0xd8, 0xa1, 0x2c, 0xa6, 0xb1, + 0x0f, 0x24, 0xb8, 0x3e, 0x2b, 0x16, 0x9f, 0x1d, 0x65, 0xef, 0x8c, 0xc6, 0x1d, 0xb8, 0xde, 0x71, + 0x4c, 0xf7, 0x30, 0x5f, 0xbd, 0xb7, 0xbe, 0x2b, 0xa0, 0xa8, 0x30, 0xb2, 0xd5, 0x64, 0x27, 0x58, + 0x14, 0xb9, 0x8c, 0xf4, 0xa4, 0xb5, 0x96, 0x59, 0x77, 0x6c, 0xe2, 0x45, 0x6e, 0x04, 0x75, 0x83, + 0x11, 0xd1, 0x6b, 0x30, 0xb5, 0x67, 0x5a, 0x2c, 0xd2, 0xea, 0xb5, 0xe3, 0x41, 0x9b, 0x94, 0x47, + 0x16, 0x0d, 0x1f, 0x42, 0x2a, 0x90, 0x44, 0x48, 0xb9, 0x3c, 0xc2, 0xb8, 0x7c, 0xeb, 0xf8, 0x28, + 0x9b, 0xec, 0x4a, 0x0d, 0xce, 0xe9, 0xcf, 0x63, 0x3b, 0x25, 0xbb, 0xc3, 0x50, 0x3e, 0x9f, 0x85, + 0x09, 0xf6, 0x45, 0x26, 0xcf, 0x12, 0x57, 0x79, 0x01, 0x55, 0x20, 0x29, 0x7c, 0x2d, 0xfc, 0x73, + 0x4d, 0x91, 0x79, 0x19, 0xf4, 0xd7, 0x7b, 0x1f, 0x74, 0xe6, 0x2b, 0x56, 0xdd, 0x36, 0xb0, 0x51, + 0xa1, 0x65, 0x55, 0xb8, 0x6c, 0x59, 0x81, 0xa0, 0x15, 0x48, 0xd5, 0x9b, 0x58, 0xb7, 0x3a, 0x6d, + 0x0f, 0x07, 0x8d, 0x88, 0x93, 0x14, 0xfd, 0x04, 0xd0, 0x26, 0xa0, 0x3d, 0x96, 0x03, 0x16, 0x9c, + 0x15, 0x8b, 0x73, 0x8e, 0x02, 0x26, 0xb3, 0xbe, 0x6a, 0x77, 0x66, 0xe8, 0x2a, 0x24, 0x2d, 0xdb, + 0xaa, 0xeb, 0x56, 0x1d, 0x37, 0x99, 0xe8, 0xe6, 0xa1, 0xd1, 0x5e, 0x22, 0x2a, 0x42, 0x84, 0x87, + 0xfb, 0xc5, 0x23, 0xf9, 0xfa, 0xa8, 0xdf, 0x52, 0xac, 0x8e, 0xa9, 0xa2, 0x27, 0xaa, 0x40, 0xd4, + 0xe1, 0x59, 0x2c, 0x2c, 0x05, 0xe0, 0x4c, 0x67, 0x55, 0x20, 0x53, 0x66, 0x75, 0x4c, 0xf5, 0xfa, + 0xa2, 0x1d, 0x2f, 0x61, 0x98, 0x2b, 0x6a, 0x91, 0xea, 0x99, 0x1f, 0xf1, 0x09, 0xd2, 0x05, 0xec, + 0x41, 0xa1, 0x0b, 0x34, 0x59, 0x54, 0x8b, 0x25, 0x07, 0x0c, 0x5f, 0x60, 0x4f, 0x02, 0x0a, 0x5d, + 0x20, 0xef, 0x89, 0x36, 0xa9, 0xa5, 0xe1, 0x19, 0x0f, 0x2c, 0x6d, 0x60, 0xf2, 0xd6, 0xe7, 0xce, + 0x63, 0x60, 0xaf, 0x8e, 0xa9, 0x01, 0x04, 0x74, 0x0f, 0x26, 0xeb, 0x5d, 0x19, 0x98, 0x9e, 0x62, + 0x80, 0x37, 0xcf, 0xa5, 0x08, 0x57, 0xa9, 0xf2, 0xeb, 0x52, 0xd1, 0x27, 0x90, 0x22, 0x3d, 0x0f, + 0xb2, 0xf4, 0x25, 0x86, 0xfa, 0xc6, 0x79, 0x9d, 0xc3, 0xab, 0x63, 0x6a, 0x1f, 0x12, 0xfa, 0xff, + 0x20, 0xbb, 0x7d, 0xd1, 0x1d, 0x16, 0x4d, 0x1f, 0x9e, 0x87, 0x7b, 0x4a, 0x0c, 0x6b, 0x75, 0x4c, + 0x3d, 0x81, 0x86, 0xbe, 0x02, 0x53, 0xa4, 0xf7, 0x33, 0xb5, 0xf4, 0x3c, 0x1b, 0xe0, 0xcd, 0xd1, + 0x3f, 0x6c, 0xeb, 0xe2, 0xf7, 0x63, 0x51, 0x78, 0xab, 0x37, 0x48, 0xc4, 0xb2, 0x4a, 0x86, 0xc3, + 0x0f, 0x0e, 0x5a, 0x51, 0xf8, 0x3e, 0x2c, 0x74, 0x17, 0xe2, 0x2d, 0x4f, 0xa9, 0xb0, 0x9c, 0x8c, + 0xe1, 0x6f, 0x98, 0x7e, 0x3d, 0xb7, 0x3a, 0xa6, 0x76, 0xfb, 0xa3, 0xdf, 0x96, 0xe0, 0x8a, 0x3e, + 0x24, 0x9a, 0xc4, 0x72, 0x38, 0x86, 0x3b, 0xfd, 0x47, 0x88, 0x59, 0xad, 0x8e, 0xa9, 0x43, 0x47, + 0x41, 0x0e, 0xcc, 0xe9, 0x03, 0x95, 0x5a, 0x7a, 0xe1, 0x4c, 0x43, 0x7b, 0xa8, 0xba, 0x5c, 0x1d, + 0x53, 0x4f, 0x41, 0x46, 0x75, 0x98, 0x26, 0xfd, 0x5f, 0x0a, 0xa6, 0x5f, 0x66, 0xc3, 0xbd, 0x75, + 0x26, 0x1f, 0x9c, 0xfc, 0x50, 0x71, 0x75, 0x4c, 0x3d, 0x89, 0x87, 0x5e, 0x86, 0x04, 0x4f, 0x69, + 0x76, 0xb0, 0x4e, 0x6c, 0x2b, 0x7d, 0x85, 0x2b, 0x60, 0x46, 0x53, 0x19, 0x09, 0x7d, 0x1d, 0xb2, + 0x0e, 0x76, 0x1d, 0x93, 0xd9, 0x72, 0xf8, 0x31, 0xae, 0x77, 0x98, 0xf5, 0xb7, 0xa7, 0x9b, 0xcd, + 0x8e, 0x83, 0xb5, 0xa6, 0xdd, 0x48, 0x2f, 0x32, 0x19, 0x3f, 0xfc, 0x21, 0x26, 0x10, 0x2a, 0x1e, + 0xc0, 0x32, 0xef, 0xaf, 0x5e, 0x71, 0x4e, 0xab, 0x5a, 0xb7, 0x1b, 0xc5, 0x38, 0x44, 0x45, 0x38, + 0xda, 0x4f, 0x37, 0xe1, 0x89, 0x26, 0x3c, 0xc5, 0x24, 0x23, 0x7f, 0x26, 0xf7, 0x8f, 0x09, 0x88, + 0xf9, 0xc6, 0xcb, 0x12, 0x20, 0xdf, 0x3e, 0xed, 0x7e, 0x9e, 0x40, 0xcd, 0x8a, 0x10, 0xdd, 0x00, + 0xaf, 0xae, 0xfb, 0x85, 0xc2, 0x9d, 0x9e, 0x04, 0xc3, 0x51, 0xbe, 0x11, 0xa6, 0x0c, 0xea, 0x67, + 0x20, 0x52, 0x75, 0x2f, 0x32, 0xd8, 0x7d, 0x75, 0xcf, 0x43, 0x1d, 0x29, 0x8f, 0x2c, 0xd4, 0xfd, + 0x35, 0x48, 0x39, 0x1d, 0x8b, 0xc5, 0x90, 0x85, 0x53, 0x89, 0x1b, 0xe5, 0x49, 0x41, 0x15, 0x7e, + 0xa1, 0x52, 0x9f, 0x06, 0xba, 0x71, 0xa6, 0x06, 0xf2, 0xd6, 0xbe, 0x2a, 0xf9, 0x2a, 0x68, 0xb9, + 0x5f, 0x05, 0xbd, 0x7e, 0xb6, 0x0a, 0x0a, 0xc0, 0xf8, 0x3a, 0x68, 0x77, 0xa0, 0x0e, 0x5a, 0x1a, + 0x51, 0x88, 0x06, 0x10, 0x7b, 0x95, 0x50, 0xa9, 0x4f, 0x09, 0xdd, 0x38, 0x53, 0x09, 0x05, 0xd7, + 0x28, 0xb4, 0xd0, 0xd6, 0x00, 0x2d, 0x74, 0xf3, 0x5c, 0xef, 0xdd, 0x55, 0xa9, 0x47, 0x0d, 0xa9, + 0x83, 0xd4, 0x50, 0x7e, 0x34, 0x35, 0x14, 0x80, 0xec, 0xd1, 0x43, 0x9f, 0x9e, 0xd0, 0x43, 0xf2, + 0xd9, 0x82, 0x7c, 0xa0, 0x27, 0x71, 0x55, 0x3a, 0xa1, 0x88, 0xf4, 0x01, 0x8a, 0x68, 0xfa, 0x4c, + 0xf9, 0x70, 0x5a, 0x66, 0xc2, 0xaa, 0x34, 0x40, 0x13, 0x7d, 0x04, 0x89, 0xa0, 0xf6, 0x60, 0xe9, + 0x67, 0xc3, 0xf5, 0xdc, 0x29, 0xdf, 0x57, 0x33, 0x1e, 0x08, 0x54, 0xa1, 0xaf, 0x9e, 0x54, 0x42, + 0x33, 0x67, 0x82, 0x9f, 0x92, 0xdb, 0xb0, 0x2a, 0x9d, 0xd4, 0x42, 0xeb, 0x41, 0x2d, 0x34, 0x7b, + 0xa6, 0x8d, 0x72, 0xe2, 0x19, 0xb4, 0x2a, 0x05, 0xd5, 0xd0, 0x77, 0x24, 0xb8, 0x32, 0x4c, 0x8f, + 0x08, 0x03, 0xe0, 0xfd, 0x0b, 0xaa, 0xa1, 0xc0, 0xa0, 0x43, 0x87, 0x41, 0xe4, 0x54, 0x3d, 0x34, + 0x7f, 0x66, 0xee, 0xd3, 0xf0, 0x57, 0xd9, 0xaa, 0x74, 0xaa, 0x22, 0x32, 0x06, 0x29, 0xa2, 0xf4, + 0xd9, 0x5f, 0x86, 0x9e, 0xf6, 0x99, 0xfb, 0xaa, 0x34, 0x48, 0x13, 0x3d, 0x80, 0x98, 0xeb, 0xe8, + 0x75, 0x16, 0x5a, 0xbc, 0xc4, 0x62, 0xc9, 0xf7, 0x85, 0x4b, 0xa9, 0x34, 0xba, 0x4b, 0x89, 0x22, + 0x98, 0x56, 0xc3, 0xfb, 0x97, 0xb2, 0x3b, 0xc5, 0x64, 0x2e, 0xa6, 0xa8, 0xf8, 0xa9, 0x46, 0xd9, + 0x38, 0x8a, 0x51, 0x04, 0x88, 0x79, 0x29, 0x55, 0x01, 0x45, 0x93, 0xfb, 0x9e, 0x04, 0xe3, 0x6b, + 0x76, 0x0d, 0xbd, 0x14, 0x08, 0x5c, 0x24, 0xc5, 0x5c, 0x26, 0xd6, 0xec, 0x9a, 0x88, 0x40, 0xbc, + 0xdf, 0xed, 0x2d, 0xfc, 0x40, 0xaf, 0x0c, 0xd9, 0x0d, 0x3f, 0xee, 0xe3, 0x77, 0x42, 0x5f, 0x86, + 0x68, 0x9b, 0x3f, 0x83, 0x85, 0xde, 0xc9, 0x0d, 0xeb, 0xcf, 0x5b, 0xaa, 0x5e, 0x97, 0xdc, 0x7f, + 0x85, 0xe0, 0xf2, 0xa9, 0x5a, 0x15, 0xcd, 0xf5, 0x84, 0x2e, 0xe2, 0x5e, 0x00, 0x02, 0x7d, 0x1e, + 0xe6, 0xba, 0x2a, 0x9c, 0xa7, 0x15, 0xf6, 0x68, 0xad, 0x59, 0xbf, 0x96, 0x65, 0x16, 0x0a, 0xdd, + 0xf5, 0x06, 0x74, 0xe9, 0x1a, 0xb6, 0xfa, 0x1e, 0xc0, 0xc8, 0xaf, 0xab, 0x58, 0x9e, 0xb6, 0xb3, + 0x60, 0xd2, 0xb4, 0x88, 0x4b, 0x5f, 0x56, 0x5e, 0xac, 0x78, 0xa2, 0xb8, 0x21, 0x36, 0xf1, 0x8b, + 0x23, 0x1d, 0x28, 0xcf, 0xf2, 0xbc, 0xb7, 0xae, 0x08, 0x1c, 0x76, 0x88, 0xd0, 0x2d, 0xa9, 0xe0, + 0x8d, 0xa0, 0x18, 0xe8, 0x6d, 0xef, 0x51, 0x3b, 0x31, 0xe2, 0x0b, 0x51, 0x3c, 0x7b, 0x5f, 0x83, + 0x29, 0xd7, 0xe9, 0x58, 0xfc, 0x1b, 0x76, 0x8e, 0xc0, 0x3c, 0x55, 0x6a, 0xca, 0x27, 0xb3, 0xf6, + 0xaf, 0xdf, 0x08, 0xfe, 0x6f, 0x33, 0x1b, 0xb6, 0x81, 0x51, 0x0a, 0x60, 0x5b, 0x27, 0xa4, 0xbd, + 0xef, 0xe8, 0x04, 0xcb, 0x63, 0x28, 0x0a, 0xe3, 0x77, 0x37, 0xaa, 0xb2, 0xf4, 0xfa, 0x47, 0xc1, + 0x20, 0x4f, 0x59, 0x2d, 0x28, 0x9b, 0xca, 0xe6, 0x8a, 0xb6, 0x59, 0xd8, 0xa8, 0x54, 0xe5, 0x31, + 0x94, 0x86, 0xd9, 0x0f, 0x0b, 0xca, 0x8e, 0x88, 0xfa, 0x68, 0xca, 0xe6, 0x4e, 0x45, 0xbd, 0x5f, + 0x58, 0x97, 0x25, 0x34, 0x07, 0x48, 0xdd, 0x2a, 0xdd, 0xad, 0x96, 0x8b, 0x5a, 0x69, 0x6b, 0x63, + 0xbb, 0x50, 0xda, 0x51, 0xb6, 0x36, 0xe5, 0x10, 0x8a, 0x41, 0xb8, 0xbc, 0xb5, 0x59, 0x91, 0xe1, + 0xf5, 0x9f, 0x4d, 0x88, 0xec, 0xd6, 0xab, 0x30, 0xb9, 0xbb, 0x59, 0xdd, 0xae, 0x94, 0x94, 0x65, + 0xa5, 0x52, 0x96, 0xc7, 0x32, 0x33, 0x4f, 0x9e, 0x2e, 0x4e, 0xd1, 0xaa, 0x5d, 0x8b, 0xb4, 0x71, + 0x9d, 0x19, 0x1e, 0x28, 0x03, 0x91, 0x62, 0xa1, 0x74, 0x77, 0x77, 0x5b, 0x96, 0x32, 0xa9, 0x27, + 0x4f, 0x17, 0x81, 0x7d, 0x44, 0xc0, 0x4d, 0x84, 0x2b, 0xdc, 0x7f, 0xbd, 0xa5, 0x56, 0xe4, 0x50, + 0x66, 0xea, 0xc9, 0xd3, 0xc5, 0x49, 0xe6, 0x16, 0x17, 0x8a, 0xff, 0x35, 0x48, 0x56, 0x4b, 0xab, + 0x95, 0x8d, 0x82, 0x56, 0x5a, 0x2d, 0x6c, 0xae, 0x54, 0xe4, 0xf1, 0xcc, 0xec, 0x93, 0xa7, 0x8b, + 0x72, 0xbf, 0xf2, 0xa0, 0x43, 0x28, 0x1b, 0xdb, 0x5b, 0xea, 0x8e, 0x1c, 0xee, 0x0e, 0xc1, 0x75, + 0x36, 0xca, 0x01, 0xf0, 0xde, 0xcb, 0x95, 0x4a, 0x59, 0x9e, 0xc8, 0xa0, 0x27, 0x4f, 0x17, 0x53, + 0xb4, 0xbe, 0xab, 0x8a, 0xd1, 0x35, 0x48, 0x94, 0xd4, 0x4a, 0x61, 0xa7, 0xa2, 0x55, 0x77, 0x0a, + 0x3b, 0x55, 0x39, 0xd2, 0x5d, 0x49, 0x40, 0xbd, 0xa2, 0x3c, 0x4c, 0x17, 0x76, 0x77, 0xb6, 0xb4, + 0x9e, 0xb6, 0xd1, 0xcc, 0xfc, 0x93, 0xa7, 0x8b, 0x33, 0xb4, 0x2d, 0x95, 0x6d, 0xc1, 0xf6, 0x9f, + 0x03, 0xb9, 0x67, 0xfe, 0xda, 0x4a, 0x49, 0x8e, 0x65, 0xe6, 0x9e, 0x3c, 0x5d, 0x44, 0xfd, 0x4b, + 0x58, 0x29, 0xd1, 0x4b, 0xb1, 0xf3, 0xf1, 0x76, 0xa5, 0x5c, 0xa9, 0x96, 0xb4, 0xde, 0x65, 0xc7, + 0x33, 0xe9, 0x27, 0x4f, 0x17, 0x67, 0x69, 0x9f, 0x13, 0x4b, 0xbf, 0x09, 0x72, 0x75, 0x47, 0xad, + 0x14, 0x36, 0x34, 0x65, 0x73, 0xa5, 0x52, 0x65, 0x87, 0x05, 0xdd, 0x29, 0xf5, 0x29, 0x42, 0xba, + 0x84, 0xcd, 0xca, 0x87, 0x7d, 0xf8, 0x93, 0xdd, 0xf6, 0x7d, 0xba, 0x0d, 0x2d, 0x42, 0x7c, 0x43, + 0x59, 0x51, 0x0b, 0x0c, 0x37, 0x91, 0x99, 0x7e, 0xf2, 0x74, 0x31, 0x49, 0xdb, 0xf9, 0x9a, 0x0a, + 0x29, 0x90, 0x65, 0x9b, 0x52, 0xdd, 0x2e, 0x6c, 0x6a, 0xa5, 0xad, 0xcd, 0x65, 0x65, 0x45, 0x53, + 0x2b, 0xa5, 0xad, 0xcd, 0x92, 0xb2, 0xae, 0xf0, 0x7e, 0xc9, 0xcc, 0xd5, 0x27, 0x4f, 0x17, 0x17, + 0xbd, 0x2d, 0x3a, 0x55, 0xaf, 0xbc, 0x07, 0x97, 0x39, 0xd4, 0xbd, 0x75, 0xbe, 0xb9, 0x41, 0x0e, + 0x4c, 0x65, 0x16, 0x9e, 0x3c, 0x5d, 0xcc, 0xf8, 0x20, 0x27, 0x35, 0xc4, 0x9b, 0x80, 0xc4, 0x56, + 0xa8, 0x95, 0xed, 0x75, 0xa5, 0xc4, 0x07, 0x9f, 0xca, 0x5c, 0x7e, 0xf2, 0x74, 0xf1, 0x52, 0x77, + 0x33, 0x02, 0xe2, 0x3e, 0x13, 0xfb, 0x9d, 0xef, 0x2d, 0x8c, 0xfd, 0xf9, 0xf7, 0x17, 0xc6, 0x8a, + 0xd7, 0x7f, 0xfc, 0x1f, 0x0b, 0x63, 0x3f, 0x3e, 0x5e, 0x90, 0x7e, 0x7a, 0xbc, 0x20, 0xfd, 0xfc, + 0x78, 0x41, 0xfa, 0xf7, 0xe3, 0x05, 0xe9, 0xbb, 0xbf, 0x5c, 0x18, 0xfb, 0xe9, 0x2f, 0x17, 0xc6, + 0x7e, 0xfe, 0xcb, 0x85, 0xb1, 0x4f, 0x22, 0x5c, 0x06, 0xd6, 0x22, 0xcc, 0xc3, 0xf7, 0xd6, 0xff, + 0x05, 0x00, 0x00, 0xff, 0xff, 0x37, 0xe2, 0x49, 0x63, 0x8e, 0x4c, 0x00, 0x00, } func (this *BackupEncryptionOptions) Equal(that interface{}) bool { @@ -5642,32 +5634,6 @@ func (m *SchemaChangeGCDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - if len(m.InterleavedIndexes) > 0 { - for iNdEx := len(m.InterleavedIndexes) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.InterleavedIndexes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintJobs(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a - } - } - if m.InterleavedTable != nil { - { - size, err := m.InterleavedTable.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintJobs(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x22 - } if m.ParentID != 0 { i = encodeVarintJobs(dAtA, i, uint64(m.ParentID)) i-- @@ -5834,38 +5800,38 @@ func (m *SchemaChangeDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x52 if len(m.DroppedSchemas) > 0 { - dAtA35 := make([]byte, len(m.DroppedSchemas)*10) - var j34 int + dAtA34 := make([]byte, len(m.DroppedSchemas)*10) + var j33 int for _, num := range m.DroppedSchemas { for num >= 1<<7 { - dAtA35[j34] = uint8(uint64(num)&0x7f | 0x80) + dAtA34[j33] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j34++ + j33++ } - dAtA35[j34] = uint8(num) - j34++ + dAtA34[j33] = uint8(num) + j33++ } - i -= j34 - copy(dAtA[i:], dAtA35[:j34]) - i = encodeVarintJobs(dAtA, i, uint64(j34)) + i -= j33 + copy(dAtA[i:], dAtA34[:j33]) + i = encodeVarintJobs(dAtA, i, uint64(j33)) i-- dAtA[i] = 0x4a } if len(m.DroppedTypes) > 0 { - dAtA37 := make([]byte, len(m.DroppedTypes)*10) - var j36 int + dAtA36 := make([]byte, len(m.DroppedTypes)*10) + var j35 int for _, num := range m.DroppedTypes { for num >= 1<<7 { - dAtA37[j36] = uint8(uint64(num)&0x7f | 0x80) + dAtA36[j35] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j36++ + j35++ } - dAtA37[j36] = uint8(num) - j36++ + dAtA36[j35] = uint8(num) + j35++ } - i -= j36 - copy(dAtA[i:], dAtA37[:j36]) - i = encodeVarintJobs(dAtA, i, uint64(j36)) + i -= j35 + copy(dAtA[i:], dAtA36[:j35]) + i = encodeVarintJobs(dAtA, i, uint64(j35)) i-- dAtA[i] = 0x42 } @@ -6520,20 +6486,20 @@ func (m *CreateStatsDetails_ColStat) MarshalToSizedBuffer(dAtA []byte) (int, err dAtA[i] = 0x10 } if len(m.ColumnIDs) > 0 { - dAtA47 := make([]byte, len(m.ColumnIDs)*10) - var j46 int + dAtA46 := make([]byte, len(m.ColumnIDs)*10) + var j45 int for _, num := range m.ColumnIDs { for num >= 1<<7 { - dAtA47[j46] = uint8(uint64(num)&0x7f | 0x80) + dAtA46[j45] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j46++ + j45++ } - dAtA47[j46] = uint8(num) - j46++ + dAtA46[j45] = uint8(num) + j45++ } - i -= j46 - copy(dAtA[i:], dAtA47[:j46]) - i = encodeVarintJobs(dAtA, i, uint64(j46)) + i -= j45 + copy(dAtA[i:], dAtA46[:j45]) + i = encodeVarintJobs(dAtA, i, uint64(j45)) i-- dAtA[i] = 0xa } @@ -6805,20 +6771,20 @@ func (m *Payload) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x42 } if len(m.DescriptorIDs) > 0 { - dAtA51 := make([]byte, len(m.DescriptorIDs)*10) - var j50 int + dAtA50 := make([]byte, len(m.DescriptorIDs)*10) + var j49 int for _, num := range m.DescriptorIDs { for num >= 1<<7 { - dAtA51[j50] = uint8(uint64(num)&0x7f | 0x80) + dAtA50[j49] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j50++ + j49++ } - dAtA51[j50] = uint8(num) - j50++ + dAtA50[j49] = uint8(num) + j49++ } - i -= j50 - copy(dAtA[i:], dAtA51[:j50]) - i = encodeVarintJobs(dAtA, i, uint64(j50)) + i -= j49 + copy(dAtA[i:], dAtA50[:j49]) + i = encodeVarintJobs(dAtA, i, uint64(j49)) i-- dAtA[i] = 0x32 } @@ -8484,16 +8450,6 @@ func (m *SchemaChangeGCDetails) Size() (n int) { if m.ParentID != 0 { n += 1 + sovJobs(uint64(m.ParentID)) } - if m.InterleavedTable != nil { - l = m.InterleavedTable.Size() - n += 1 + l + sovJobs(uint64(l)) - } - if len(m.InterleavedIndexes) > 0 { - for _, e := range m.InterleavedIndexes { - l = e.Size() - n += 1 + l + sovJobs(uint64(l)) - } - } if m.Tenant != nil { l = m.Tenant.Size() n += 1 + l + sovJobs(uint64(l)) @@ -15141,76 +15097,6 @@ func (m *SchemaChangeGCDetails) Unmarshal(dAtA []byte) error { break } } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InterleavedTable", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowJobs - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthJobs - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthJobs - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.InterleavedTable == nil { - m.InterleavedTable = &descpb.TableDescriptor{} - } - if err := m.InterleavedTable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field InterleavedIndexes", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowJobs - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthJobs - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthJobs - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.InterleavedIndexes = append(m.InterleavedIndexes, descpb.IndexDescriptor{}) - if err := m.InterleavedIndexes[len(m.InterleavedIndexes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tenant", wireType) diff --git a/pkg/jobs/jobspb/jobs.proto b/pkg/jobs/jobspb/jobs.proto index 0cba181da776..820c85a6f2ef 100644 --- a/pkg/jobs/jobspb/jobs.proto +++ b/pkg/jobs/jobspb/jobs.proto @@ -488,14 +488,7 @@ message SchemaChangeGCDetails { // Indexes to GC. repeated DroppedIndex indexes = 1 [(gogoproto.nullable) = false]; - // The below two fields are used only in the case of TRUNCATE operating on - // tables with interleaved indexes. They are only set together. - - // InterleavedTable is the table being truncated. In particular, it is the - // TableDescriptor before any of the truncate modifications have been applied. - sqlbase.TableDescriptor interleaved_table = 4; - // InterleavedIndexes is the set of interleaved indexes to truncate. - repeated sqlbase.IndexDescriptor interleaved_indexes = 5 [(gogoproto.nullable) = false]; + reserved 4, 5; // Entire tables to GC. repeated DroppedID tables = 2 [(gogoproto.nullable) = false]; diff --git a/pkg/migration/migrations/BUILD.bazel b/pkg/migration/migrations/BUILD.bazel index 109418dcc205..371899e16320 100644 --- a/pkg/migration/migrations/BUILD.bazel +++ b/pkg/migration/migrations/BUILD.bazel @@ -7,7 +7,6 @@ go_library( "database_role_settings.go", "delete_deprecated_namespace_tabledesc.go", "fix_descriptor_migration.go", - "interleaved_tables.go", "join_tokens.go", "migrations.go", "records_based_registry.go", @@ -70,7 +69,6 @@ go_test( "delete_deprecated_namespace_tabledesc_external_test.go", "fix_descriptor_migration_external_test.go", "helpers_test.go", - "interleaved_tables_external_test.go", "main_test.go", "on_update_test.go", "retry_jobs_with_exponential_backoff_external_test.go", diff --git a/pkg/migration/migrations/interleaved_tables.go b/pkg/migration/migrations/interleaved_tables.go deleted file mode 100644 index 2bce8d186836..000000000000 --- a/pkg/migration/migrations/interleaved_tables.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package migrations - -import ( - "context" - - "github.com/cockroachdb/cockroach/pkg/clusterversion" - "github.com/cockroachdb/cockroach/pkg/jobs" - "github.com/cockroachdb/cockroach/pkg/migration" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" - "github.com/cockroachdb/errors" -) - -func interleavedTablesRemovedMigration( - ctx context.Context, cv clusterversion.ClusterVersion, d migration.TenantDeps, _ *jobs.Job, -) error { - return interleavedTablesRemovedCheck(ctx, cv, d) -} - -func interleavedTablesRemovedCheck( - ctx context.Context, _ clusterversion.ClusterVersion, d migration.TenantDeps, -) error { - rows, err := d.InternalExecutor.QueryRowEx(ctx, "check-for-interleaved", - nil, sessiondata.InternalExecutorOverride{User: security.RootUserName()}, - "SELECT EXISTS(SELECT * FROM crdb_internal.interleaved);") - if err != nil { - return err - } - if rows.Len() != 1 { - return errors.Newf("unable to detect interleaved tables using crdb_internal.interleaved.") - } - boolVal, ok := rows[0].(*tree.DBool) - if !ok || *boolVal == *tree.DBoolTrue { - return errors.Newf("interleaved tables are no longer supported at this version, please drop or uninterleave any tables visible using crdb_internal.interleaved.") - } - return nil -} diff --git a/pkg/migration/migrations/interleaved_tables_external_test.go b/pkg/migration/migrations/interleaved_tables_external_test.go deleted file mode 100644 index e7dcfdb48f35..000000000000 --- a/pkg/migration/migrations/interleaved_tables_external_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package migrations_test - -import ( - "context" - "testing" - - "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/clusterversion" - "github.com/cockroachdb/cockroach/pkg/server" - "github.com/cockroachdb/cockroach/pkg/testutils" - "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" - "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" - "github.com/cockroachdb/cockroach/pkg/util/leaktest" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/errors" - "github.com/stretchr/testify/require" -) - -func TestInterleavedTableMigration(t *testing.T) { - defer leaktest.AfterTest(t)() - defer log.Scope(t).Close(t) - - ctx := context.Background() - tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{ - ServerArgs: base.TestServerArgs{ - Knobs: base.TestingKnobs{ - Server: &server.TestingKnobs{ - DisableAutomaticVersionUpgrade: 1, - BinaryVersionOverride: clusterversion.ByKey(clusterversion.PreventNewInterleavedTables - 1), - }, - }, - }, - }) - - defer tc.Stopper().Stop(ctx) - db := tc.ServerConn(0) - tdb := sqlutils.MakeSQLRunner(db) - tdb.ExecSucceedsSoon(t, "CREATE TABLE customers (id INT PRIMARY KEY, name STRING(50));") - tdb.ExecSucceedsSoon(t, `CREATE TABLE orders ( - customer INT, - id INT, - total DECIMAL(20, 5), - PRIMARY KEY (customer, id), - CONSTRAINT fk_customer FOREIGN KEY (customer) REFERENCES customers - ) INTERLEAVE IN PARENT customers (customer);`) - // Migration to the second phase should fail, since the pre-condition was violated. - tdb.ExpectErr(t, - "pq: verifying precondition for version 21.1-1140: interleaved tables are no longer supported at this version, please drop or uninterleave any tables visible using crdb_internal.interleaved.", - "SET CLUSTER SETTING version = $1", - clusterversion.ByKey(clusterversion.EnsureNoInterleavedTables).String()) - // Migration to the first phase should be fine, since it only blocks creating - // new interleaved tables. - tdb.ExecSucceedsSoon(t, - "SET CLUSTER SETTING version = $1", - clusterversion.ByKey(clusterversion.PreventNewInterleavedTables).String()) - // Migration to the next phase without interleaved tables should fail. - tdb.ExpectErr(t, - "pq: verifying precondition for version 21.1-1140: interleaved tables are no longer supported at this version, please drop or uninterleave any tables visible using crdb_internal.interleaved.", - "SET CLUSTER SETTING version = $1", - clusterversion.ByKey(clusterversion.EnsureNoInterleavedTables).String()) - // Next drop the old descriptor and wait for the jobs to complete. - _, err := db.Exec(`ALTER RANGE default CONFIGURE ZONE USING gc.ttlseconds=1;`) - require.NoError(t, err) - tdb.ExecSucceedsSoon(t, `ALTER TABLE ORDERS ALTER PRIMARY KEY USING COLUMNS (customer, id);`) - testutils.SucceedsSoon(t, func() error { - row := tdb.QueryRow(t, - `SELECT count(*) from [show jobs] where status not in ('succeeded', 'failed', 'aborted')`) - count := 0 - row.Scan(&count) - if count != 0 { - return errors.New("Waiting for GC jobs to complete") - } - return nil - }) - // Check that creation of interleaved tables is fully disabled. - tdb.Exec(t, "CREATE TABLE customers2 (id INT PRIMARY KEY, name STRING(50));") - tdb.Exec(t, - `CREATE TABLE orders2 ( - customer INT, - id INT, - total DECIMAL(20, 5), - PRIMARY KEY (customer, id), - CONSTRAINT fk_customer FOREIGN KEY (customer) REFERENCES customers2 - ) INTERLEAVE IN PARENT customers2 (customer);`) - // Check that no interleaved tables were created and the syntax is now a no-op. - tdb.CheckQueryResults(t, - "SELECT EXISTS(SELECT * FROM crdb_internal.interleaved);", - [][]string{ - {"false"}, - }) - // Migration to next phase should succeed. - tdb.ExecSucceedsSoon(t, "SET CLUSTER SETTING version = $1", - clusterversion.ByKey(clusterversion.EnsureNoInterleavedTables).String()) - // Check that creation of interleaved tables is a no-op. - tdb.Exec(t, - `CREATE TABLE orders3 ( - customer INT, - id INT, - total DECIMAL(20, 5), - PRIMARY KEY (customer, id), - CONSTRAINT fk_customer FOREIGN KEY (customer) REFERENCES customers2 - ) INTERLEAVE IN PARENT customers2 (customer);`) - // Check that no interleaved tables were created and the syntax is now a no-op. - tdb.CheckQueryResults(t, - "SELECT EXISTS(SELECT * FROM crdb_internal.interleaved);", - [][]string{ - {"false"}, - }) -} diff --git a/pkg/migration/migrations/migrations.go b/pkg/migration/migrations/migrations.go index 19af695acc56..570bc1541d5c 100644 --- a/pkg/migration/migrations/migrations.go +++ b/pkg/migration/migrations/migrations.go @@ -94,12 +94,6 @@ var migrations = []migration.Migration{ toCV(clusterversion.RetryJobsWithExponentialBackoff), NoPrecondition, retryJobsWithExponentialBackoff), - migration.NewTenantMigration( - "validates no interleaved tables exist", - toCV(clusterversion.EnsureNoInterleavedTables), - interleavedTablesRemovedCheck, - interleavedTablesRemovedMigration, - ), migration.NewTenantMigration( "add system.zones table for secondary tenants", toCV(clusterversion.ZonesTableForSecondaryTenants), diff --git a/pkg/server/settingswatcher/row_decoder.go b/pkg/server/settingswatcher/row_decoder.go index 52c8c740d31a..f837e9b90ac1 100644 --- a/pkg/server/settingswatcher/row_decoder.go +++ b/pkg/server/settingswatcher/row_decoder.go @@ -52,7 +52,7 @@ func (d *RowDecoder) DecodeRow( { types := []*types.T{tbl.PublicColumns()[0].GetType()} nameRow := make([]rowenc.EncDatum, 1) - _, matches, _, err := rowenc.DecodeIndexKey(d.codec, tbl, tbl.GetPrimaryIndex(), types, nameRow, nil, kv.Key) + _, matches, _, err := rowenc.DecodeIndexKey(d.codec, types, nameRow, nil, kv.Key) if err != nil { return "", "", "", false, errors.Wrap(err, "failed to decode key") } diff --git a/pkg/settings/registry.go b/pkg/settings/registry.go index 71fd0076f69d..6221d7c61736 100644 --- a/pkg/settings/registry.go +++ b/pkg/settings/registry.go @@ -105,12 +105,13 @@ var retiredSettings = map[string]struct{}{ "sql.telemetry.query_sampling.sample_rate": {}, // removed as of 22.1. - "sql.defaults.drop_enum_value.enabled": {}, - "trace.lightstep.token": {}, - "trace.datadog.agent": {}, - "trace.datadog.project": {}, - "sql.defaults.interleaved_tables.enabled": {}, - "server.declined_reservation_timeout": {}, + "sql.defaults.drop_enum_value.enabled": {}, + "trace.lightstep.token": {}, + "trace.datadog.agent": {}, + "trace.datadog.project": {}, + "sql.defaults.interleaved_tables.enabled": {}, + "sql.defaults.copy_partitioning_when_deinterleaving_table.enabled": {}, + "server.declined_reservation_timeout": {}, } // register adds a setting to the registry. diff --git a/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go b/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go index 76fa92e1f7a8..1d244903e8b5 100644 --- a/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go +++ b/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go @@ -49,10 +49,7 @@ func (sd *spanConfigDecoder) decode(kv roachpb.KeyValue) (entry roachpb.SpanConf { types := []*types.T{tbl.PublicColumns()[0].GetType()} startKeyRow := make([]rowenc.EncDatum, 1) - _, matches, _, err := rowenc.DecodeIndexKey( - keys.SystemSQLCodec, tbl, tbl.GetPrimaryIndex(), - types, startKeyRow, nil, kv.Key, - ) + _, matches, _, err := rowenc.DecodeIndexKey(keys.SystemSQLCodec, types, startKeyRow, nil /* colDirs */, kv.Key) if err != nil { return roachpb.SpanConfigEntry{}, errors.Wrapf(err, "failed to decode key: %v", kv.Key) } diff --git a/pkg/sql/alter_primary_key.go b/pkg/sql/alter_primary_key.go index 9f69555ca123..08128dd507db 100644 --- a/pkg/sql/alter_primary_key.go +++ b/pkg/sql/alter_primary_key.go @@ -12,13 +12,10 @@ package sql import ( "context" - "strings" - "github.com/cockroachdb/cockroach/pkg/config/zonepb" "github.com/cockroachdb/cockroach/pkg/server/telemetry" "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" @@ -27,7 +24,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" "github.com/cockroachdb/errors" - "github.com/gogo/protobuf/proto" ) // alterPrimaryKeyLocalitySwap contains metadata on a locality swap for @@ -72,13 +68,8 @@ func (p *planner) AlterPrimaryKey( } if alterPKNode.Interleave != nil { - interleaveIgnored, err := interleavedTableDeprecationAction(p.RunParams(ctx)) - if err != nil { - return err - } - if interleaveIgnored { - alterPKNode.Interleave = nil - } + p.BufferClientNotice(ctx, interleavedTableDisabledMigrationError) + alterPKNode.Interleave = nil } if alterPKNode.Sharded != nil { @@ -147,35 +138,6 @@ func (p *planner) AlterPrimaryKey( } } - // Disable primary key changes on tables that are interleaved parents. - if tableDesc.GetPrimaryIndex().NumInterleavedBy() != 0 { - var sb strings.Builder - sb.WriteString("[") - comma := ", " - for i := 0; i < tableDesc.GetPrimaryIndex().NumInterleavedBy(); i++ { - interleaveTableID := tableDesc.GetPrimaryIndex().GetInterleavedBy(i).Table - if i != 0 { - sb.WriteString(comma) - } - childTable, err := p.Descriptors().GetImmutableTableByID( - ctx, - p.Txn(), - interleaveTableID, - tree.ObjectLookupFlags{}, - ) - if err != nil { - return err - } - sb.WriteString(childTable.GetName()) - } - sb.WriteString("]") - return errors.Newf( - "cannot change primary key of table %s because table(s) %s are interleaved into it", - tableDesc.Name, - sb.String(), - ) - } - // Validate if the end result is the same as the current // primary index, which would mean nothing needs to be modified // here. @@ -276,15 +238,6 @@ func (p *planner) AlterPrimaryKey( return err } - if alterPKNode.Interleave != nil { - if err := p.addInterleave(ctx, tableDesc, newPrimaryIndexDesc, alterPKNode.Interleave); err != nil { - return err - } - if err := p.finalizeInterleave(ctx, tableDesc, newPrimaryIndexDesc); err != nil { - return err - } - } - var allowedNewColumnNames []tree.Name var err error // isNewPartitionAllBy is set if a new PARTITION ALL BY statement is introduced. @@ -362,12 +315,6 @@ func (p *planner) AlterPrimaryKey( if err != nil { return err } - } else { - if err := maybeCopyPartitioningWhenDeinterleaving( - ctx, p, tableDesc, newPrimaryIndexDesc, - ); err != nil { - return err - } } if partitionAllBy != nil { @@ -399,9 +346,6 @@ func (p *planner) AlterPrimaryKey( newUniqueIdx.KeySuffixColumnIDs = nil newUniqueIdx.CompositeColumnIDs = nil newUniqueIdx.KeyColumnIDs = nil - // Make the copy of the old primary index not-interleaved. This decision - // can be revisited based on user experience. - newUniqueIdx.Interleave = descpb.InterleaveDescriptor{} // Set correct version and encoding type. newUniqueIdx.Version = descpb.StrictIndexColumnIDGuaranteesVersion newUniqueIdx.EncodingType = descpb.SecondaryIndexEncoding @@ -516,15 +460,6 @@ func (p *planner) AlterPrimaryKey( if err := addIndexMutationWithSpecificPrimaryKey(ctx, tableDesc, &newIndex, newPrimaryIndexDesc); err != nil { return err } - // If the index that we are rewriting is interleaved, we need to setup the rewritten - // index to be interleaved as well. Since we cloned the index, the interleave descriptor - // on the new index is already set up. So, we just need to add the backreference from the - // parent to this new index. - if len(newIndex.Interleave.Ancestors) != 0 { - if err := p.finalizeInterleave(ctx, tableDesc, &newIndex); err != nil { - return err - } - } oldIndexIDs = append(oldIndexIDs, idx.GetID()) newIndexIDs = append(newIndexIDs, newIndex.ID) @@ -572,94 +507,6 @@ func (p *planner) AlterPrimaryKey( return nil } -func maybeCopyPartitioningWhenDeinterleaving( - ctx context.Context, - p *planner, - tableDesc *tabledesc.Mutable, - newPrimaryIndexDesc *descpb.IndexDescriptor, -) error { - if tableDesc.GetPrimaryIndex().NumInterleaveAncestors() == 0 || - !p.SessionData().CopyPartitioningWhenDeinterleavingTable || - len(newPrimaryIndexDesc.Interleave.Ancestors) > 0 { - return nil - } - - // The old primary key was interleaved in a parent and the new one is not. - // In this case, we need to clone out the old primary key's partitioning - // and zone configs and apply them to the new primary index. We do this - // if the old primary index and the new primary index have the exact same - // columns. That also allows us to side-step discussions of what to do - // about partitioning for any newly created unique index we might create - // below. - - root := tableDesc.GetPrimaryIndex().GetInterleaveAncestor(0) - interleaveRoot, err := p.Descriptors().GetImmutableTableByID(ctx, p.txn, root.TableID, tree.ObjectLookupFlags{ - CommonLookupFlags: tree.CommonLookupFlags{ - Required: true, - AvoidCached: true, - }, - DesiredObjectKind: tree.TableObject, - }) - if err != nil { - return errors.Wrap(err, "looking up interleaved root") - } - rootIndex, err := interleaveRoot.FindIndexWithID(root.IndexID) - if err != nil { - return errors.Wrap(err, "looking up interleaved root index") - } - - // If the new primary key does not have the interleave root as a prefix, - // do not copy the interleave. - if rootKeys := rootIndex.IndexDesc().KeyColumnIDs; !descpb.ColumnIDs.Equals( - rootKeys, newPrimaryIndexDesc.KeyColumnIDs[:len(rootKeys)], - ) { - return nil - } - - // The parent is not partitioned, return. - if rootIndex.GetPartitioning().NumColumns() == 0 { - return nil - } - newPrimaryIndexDesc.Partitioning = *rootIndex.GetPartitioning().DeepCopy().PartitioningDesc() - rootCfg, err := getZoneConfigRaw(ctx, p.txn, p.execCfg.Codec, p.execCfg.Settings, root.TableID) - if err != nil { - return errors.Wrapf(err, "retrieving zone config for table %s [%d]", - interleaveRoot.GetName(), interleaveRoot.GetID()) - } - tableCfg, err := getZoneConfigRaw(ctx, p.txn, p.execCfg.Codec, p.execCfg.Settings, tableDesc.GetID()) - if err != nil { - return errors.Wrapf(err, "retrieving zone config for table %s [%d]", - tableDesc.GetName(), tableDesc.GetID()) - } - // Initialize the zone config for the child. We expect it to be nil because - // the table was an interleaved child and we did not allow such children to - // have zone configs. It may be a subzone placeholder because other indexes - // might be partitioned and have zone configs. - if tableCfg == nil { - // Marking NumReplicas as 0 indicates that this zone config is a - // subzone placeholder. We assume that the value in copying out the - // partitioning is to copy out the configuration as it applies to the - // partitions of the primary index. - tableCfg = &zonepb.ZoneConfig{ - NumReplicas: proto.Int(0), - } - } else if !tableCfg.IsSubzonePlaceholder() { - return errors.AssertionFailedf("child table %s [%d] of interleave was not a subzone placeholder", - tableDesc.GetName(), tableDesc.GetID()) - } - - for _, s := range rootCfg.Subzones { - if s.IndexID == uint32(root.IndexID) { - s.IndexID = uint32(newPrimaryIndexDesc.ID) - tableCfg.Subzones = append(tableCfg.Subzones, s) - } - } - _, err = writeZoneConfig( - ctx, p.txn, tableDesc.GetID(), tableDesc, tableCfg, p.execCfg, true, - ) - return err -} - // Given the current table descriptor and the new primary keys // index descriptor this function determines if the two are // equivalent and if any index creation operations are needed @@ -674,8 +521,7 @@ func (p *planner) shouldCreateIndexes( // Validate if basic properties between the two match. if oldPK.NumKeyColumns() != len(alterPKNode.Columns) || - oldPK.IsSharded() != (alterPKNode.Sharded != nil) || - oldPK.IsInterleaved() != (alterPKNode.Interleave != nil) { + oldPK.IsSharded() != (alterPKNode.Sharded != nil) { return true, nil } @@ -690,30 +536,6 @@ func (p *planner) shouldCreateIndexes( } } - // Validate if interleaving properties match, - // specifically the parent table, and the index - // involved. - if alterPKNode.Interleave != nil { - _, parentTable, err := resolver.ResolveExistingTableObject( - ctx, p, &alterPKNode.Interleave.Parent, tree.ObjectLookupFlagsWithRequiredTableKind(tree.ResolveRequireTableDesc), - ) - if err != nil { - return true, err - } - - if oldPK.NumInterleaveAncestors() == 0 { - return true, nil - } - if oldPK.GetInterleaveAncestor(oldPK.NumInterleaveAncestors()-1).TableID != - parentTable.GetID() { - return true, nil - } - if oldPK.GetInterleaveAncestor(oldPK.NumInterleaveAncestors()-1).IndexID != - parentTable.GetPrimaryIndexID() { - return true, nil - } - } - // If the old primary key is dropped, then recreation // is required. if oldPK.IsDisabled() { diff --git a/pkg/sql/alter_table_locality.go b/pkg/sql/alter_table_locality.go index 6515ea9bcfc3..262e40757c6b 100644 --- a/pkg/sql/alter_table_locality.go +++ b/pkg/sql/alter_table_locality.go @@ -237,10 +237,6 @@ func (n *alterTableSetLocalityNode) alterTableLocalityToRegionalByRow( primaryIndexColIdxStart = int(n.tableDesc.PrimaryIndex.Partitioning.NumImplicitColumns) } - if n.tableDesc.IsInterleaved() { - return interleaveOnRegionalByRowError() - } - for _, idx := range n.tableDesc.AllIndexes() { if idx.IsSharded() { return pgerror.Newf( diff --git a/pkg/sql/backfill.go b/pkg/sql/backfill.go index a24ea4ce3d5a..8f4c97536bd8 100644 --- a/pkg/sql/backfill.go +++ b/pkg/sql/backfill.go @@ -292,9 +292,7 @@ func (sc *SchemaChanger) runBackfill(ctx context.Context) error { if col := m.AsColumn(); col != nil { needColumnBackfill = catalog.ColumnNeedsBackfill(col) } else if idx := m.AsIndex(); idx != nil { - if !canClearRangeForDrop(idx) { - droppedIndexes = append(droppedIndexes, idx) - } + // no-op } else if c := m.AsConstraint(); c != nil { constraintsToDrop = append(constraintsToDrop, c) } else if m.AsPrimaryKeySwap() != nil || m.AsComputedColumnSwap() != nil || m.AsMaterializedViewRefresh() != nil { @@ -769,74 +767,6 @@ func (sc *SchemaChanger) getTableVersion( return tableDesc, nil } -// TruncateInterleavedIndexes truncates the input set of indexes from the given -// table. It is used in the schema change GC job to delete interleaved index -// data as part of a TRUNCATE statement. Note that we cannot use -// SchemaChanger.truncateIndexes instead because that accesses the most recent -// version of the table when deleting. In this case, we need to use the version -// of the table before truncation, which is passed in. -func TruncateInterleavedIndexes( - ctx context.Context, - execCfg *ExecutorConfig, - table catalog.TableDescriptor, - indexIDs []descpb.IndexID, -) error { - log.Infof(ctx, "truncating %d interleaved indexes", len(indexIDs)) - chunkSize := int64(indexTruncateChunkSize) - alloc := &rowenc.DatumAlloc{} - codec, db := execCfg.Codec, execCfg.DB - zoneConfigIndexIDList := make([]uint32, len(indexIDs)) - for i, id := range indexIDs { - zoneConfigIndexIDList[i] = uint32(id) - } - for _, id := range indexIDs { - idx, err := table.FindIndexWithID(id) - if err != nil { - return err - } - var resume roachpb.Span - for rowIdx, done := int64(0), false; !done; rowIdx += chunkSize { - log.VEventf(ctx, 2, "truncate interleaved index (%d) at row: %d, span: %s", table.GetID(), rowIdx, resume) - resumeAt := resume - // Make a new txn just to drop this chunk. - if err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { - rd := row.MakeDeleter( - codec, table, nil /* requestedCols */, &execCfg.Settings.SV, true, /* internal */ - execCfg.GetRowMetrics(true /* internal */), - ) - td := tableDeleter{rd: rd, alloc: alloc} - if err := td.init(ctx, txn, nil /* *tree.EvalContext */, &execCfg.Settings.SV); err != nil { - return err - } - resume, err := td.deleteIndex( - ctx, - idx, - resumeAt, - chunkSize, - false, /* traceKV */ - ) - done = resume.Key == nil - return err - }); err != nil { - return err - } - } - // All the data chunks have been removed. Now also removed the - // zone configs for the dropped indexes, if any. - if err := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { - freshTableDesc, err := catalogkv.MustGetTableDescByID(ctx, txn, execCfg.Codec, table.GetID()) - if err != nil { - return err - } - return RemoveIndexZoneConfigs(ctx, txn, execCfg, freshTableDesc, zoneConfigIndexIDList) - }); err != nil { - return err - } - } - log.Infof(ctx, "finished truncating interleaved indexes") - return nil -} - // truncateIndexes truncate the KV ranges corresponding to dropped indexes. // // The indexes are dropped chunk by chunk, each chunk being deleted in @@ -846,66 +776,46 @@ func (sc *SchemaChanger) truncateIndexes( ) error { log.Infof(ctx, "clearing data for %d indexes", len(dropped)) - chunkSize := sc.getChunkSize(indexTruncateChunkSize) - if sc.testingKnobs.BackfillChunkSize > 0 { - chunkSize = sc.testingKnobs.BackfillChunkSize - } alloc := &rowenc.DatumAlloc{} droppedIndexIDs := make([]uint32, len(dropped)) for i, idx := range dropped { droppedIndexIDs[i] = uint32(idx.GetID()) } for _, idx := range dropped { - var resume roachpb.Span - for rowIdx, done := int64(0), false; !done; rowIdx += chunkSize { - resumeAt := resume - if log.V(2) { - log.Infof(ctx, "drop index (%d, %d) at row: %d, span: %s", - sc.descID, sc.mutationID, rowIdx, resume) - } - - // Make a new txn just to drop this chunk. - if err := sc.txn(ctx, func( - ctx context.Context, txn *kv.Txn, tc *descs.Collection, - ) error { - if fn := sc.execCfg.DistSQLRunTestingKnobs.RunBeforeBackfillChunk; fn != nil { - if err := fn(resume); err != nil { - return err - } - } - if fn := sc.execCfg.DistSQLRunTestingKnobs.RunAfterBackfillChunk; fn != nil { - defer fn() - } + if log.V(2) { + log.Infof(ctx, "drop index (%d, %d)", + sc.descID, sc.mutationID) + } - // Retrieve a lease for this table inside the current txn. - tableDesc, err := sc.getTableVersion(ctx, txn, tc, version) - if err != nil { - return err - } - rd := row.MakeDeleter( - sc.execCfg.Codec, tableDesc, nil /* requestedCols */, &sc.settings.SV, - true /* internal */, sc.execCfg.GetRowMetrics(true /* internal */), - ) - td := tableDeleter{rd: rd, alloc: alloc} - if err := td.init(ctx, txn, nil /* *tree.EvalContext */, &sc.settings.SV); err != nil { - return err - } - if !canClearRangeForDrop(idx) { - resume, err = td.deleteIndex( - ctx, - idx, - resumeAt, - chunkSize, - false, /* traceKV */ - ) - done = resume.Key == nil + // Make a new txn just to drop this index. + if err := sc.txn(ctx, func( + ctx context.Context, txn *kv.Txn, tc *descs.Collection, + ) error { + if fn := sc.execCfg.DistSQLRunTestingKnobs.RunBeforeBackfillChunk; fn != nil { + if err := fn(roachpb.Span{}); err != nil { return err } - done = true - return td.clearIndex(ctx, idx) - }); err != nil { + } + if fn := sc.execCfg.DistSQLRunTestingKnobs.RunAfterBackfillChunk; fn != nil { + defer fn() + } + + // Retrieve a lease for this table inside the current txn. + tableDesc, err := sc.getTableVersion(ctx, txn, tc, version) + if err != nil { + return err + } + rd := row.MakeDeleter( + sc.execCfg.Codec, tableDesc, nil /* requestedCols */, &sc.settings.SV, + true /* internal */, sc.execCfg.GetRowMetrics(true /* internal */), + ) + td := tableDeleter{rd: rd, alloc: alloc} + if err := td.init(ctx, txn, nil /* *tree.EvalContext */, &sc.settings.SV); err != nil { return err } + return td.clearIndex(ctx, idx) + }); err != nil { + return err } // All the data chunks have been removed. Now also removed the @@ -2120,24 +2030,6 @@ func runSchemaChangesInTxn( if err := tableDesc.MakeMutationComplete(tableDesc.Mutations[m.MutationOrdinal()]); err != nil { return err } - - // If the mutation we processed was a primary key swap, there is some - // extra work that needs to be done. Note that we don't need to create - // a job to clean up the dropped indexes because those mutations can - // get processed in this txn on the new table. - if err := maybeRemoveInterleaveBackreference( - ctx, planner.txn, planner.Descriptors(), m, tableDesc, func( - ctx context.Context, ancestor *tabledesc.Mutable, - ) error { - return planner.writeSchemaChange(ctx, ancestor, descpb.InvalidMutationID, - fmt.Sprintf("remove interleaved backreference from table %s(%d) "+ - "for primary key swap of table %s(%d)", - ancestor.Name, ancestor.ID, tableDesc.Name, tableDesc.GetID(), - )) - }, - ); err != nil { - return err - } } // Clear all the mutations except for adding constraints. tableDesc.Mutations = make([]descpb.DescriptorMutation, len(constraintAdditionMutations)) diff --git a/pkg/sql/catalog/catalogkeys/keys.go b/pkg/sql/catalog/catalogkeys/keys.go index 9b5ef31f0c1b..f23d58f63fb4 100644 --- a/pkg/sql/catalog/catalogkeys/keys.go +++ b/pkg/sql/catalog/catalogkeys/keys.go @@ -50,50 +50,23 @@ func IsDefaultCreatedDescriptor(descID descpb.ID) bool { // IndexKeyValDirs returns the corresponding encoding.Directions for all the // encoded values in index's "fullest" possible index key, including directions -// for table/index IDs, the interleaved sentinel and the index column values. -// For example, given -// CREATE INDEX foo ON bar (a, b DESC) INTERLEAVED IN PARENT bar (a) -// a typical index key with all values specified could be -// /51/1/42/#/51/2/1337 -// which would return the slice -// {ASC, ASC, ASC, 0, ASC, ASC, DESC} +// for table/index IDs and the index column values. func IndexKeyValDirs(index catalog.Index) []encoding.Direction { if index == nil { return nil } - dirs := make([]encoding.Direction, 0, (index.NumInterleaveAncestors()+1)*2+index.NumKeyColumns()) - - colIdx := 0 - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancs := index.GetInterleaveAncestor(i) - // Table/Index IDs are always encoded ascending. - dirs = append(dirs, encoding.Ascending, encoding.Ascending) - for i := 0; i < int(ancs.SharedPrefixLen); i++ { - d, err := index.GetKeyColumnDirection(colIdx).ToEncodingDirection() - if err != nil { - panic(err) - } - dirs = append(dirs, d) - colIdx++ - } - - // The interleaved sentinel uses the 0 value for - // encoding.Direction when pretty-printing (see - // encoding.go:prettyPrintFirstValue). - dirs = append(dirs, 0) - } + dirs := make([]encoding.Direction, 0, 2+index.NumKeyColumns()) // The index's table/index ID. dirs = append(dirs, encoding.Ascending, encoding.Ascending) - for colIdx < index.NumKeyColumns() { + for colIdx := 0; colIdx < index.NumKeyColumns(); colIdx++ { d, err := index.GetKeyColumnDirection(colIdx).ToEncodingDirection() if err != nil { panic(err) } dirs = append(dirs, d) - colIdx++ } return dirs diff --git a/pkg/sql/catalog/catconstants/constants.go b/pkg/sql/catalog/catconstants/constants.go index 48072198b398..6a76b8250095 100644 --- a/pkg/sql/catalog/catconstants/constants.go +++ b/pkg/sql/catalog/catconstants/constants.go @@ -140,7 +140,6 @@ const ( CrdbInternalZonesTableID CrdbInternalInvalidDescriptorsTableID CrdbInternalClusterDatabasePrivilegesTableID - CrdbInternalInterleaved CrdbInternalCrossDbRefrences CrdbInternalLostTableDescriptors CrdbInternalClusterInflightTracesTable diff --git a/pkg/sql/catalog/catformat/index.go b/pkg/sql/catalog/catformat/index.go index c9cd61c1f914..9c83d3853fbb 100644 --- a/pkg/sql/catalog/catformat/index.go +++ b/pkg/sql/catalog/catformat/index.go @@ -42,10 +42,9 @@ func IndexForDisplay( tableName *tree.TableName, index catalog.Index, partition string, - interleave string, semaCtx *tree.SemaContext, ) (string, error) { - return indexForDisplay(ctx, table, tableName, index.IndexDesc(), index.Primary(), partition, interleave, semaCtx) + return indexForDisplay(ctx, table, tableName, index.IndexDesc(), index.Primary(), partition, semaCtx) } func indexForDisplay( @@ -55,7 +54,6 @@ func indexForDisplay( index *descpb.IndexDescriptor, isPrimary bool, partition string, - interleave string, semaCtx *tree.SemaContext, ) (string, error) { f := tree.NewFmtCtx(tree.FmtSimple) @@ -93,7 +91,6 @@ func indexForDisplay( f.WriteByte(')') } - f.WriteString(interleave) f.WriteString(partition) if err := formatStorageConfigs(table, index, f); err != nil { diff --git a/pkg/sql/catalog/catformat/index_test.go b/pkg/sql/catalog/catformat/index_test.go index bb5027cde8a2..daef46c18a75 100644 --- a/pkg/sql/catalog/catformat/index_test.go +++ b/pkg/sql/catalog/catformat/index_test.go @@ -102,46 +102,30 @@ func TestIndexForDisplay(t *testing.T) { } testData := []struct { - index descpb.IndexDescriptor - tableName tree.TableName - partition string - interleave string - expected string + index descpb.IndexDescriptor + tableName tree.TableName + partition string + expected string }{ - {baseIndex, descpb.AnonymousTable, "", "", "INDEX baz (a ASC, b DESC)"}, - {baseIndex, tableName, "", "", "INDEX baz ON foo.public.bar (a ASC, b DESC)"}, - {uniqueIndex, descpb.AnonymousTable, "", "", "UNIQUE INDEX baz (a ASC, b DESC)"}, - {invertedIndex, descpb.AnonymousTable, "", "", "INVERTED INDEX baz (a)"}, - {storingIndex, descpb.AnonymousTable, "", "", "INDEX baz (a ASC, b DESC) STORING (c)"}, - {partialIndex, descpb.AnonymousTable, "", "", "INDEX baz (a ASC, b DESC) WHERE a > 1:::INT8"}, - {expressionIndex, descpb.AnonymousTable, "", "", "INDEX baz (a ASC, (a + b) DESC, b ASC)"}, + {baseIndex, descpb.AnonymousTable, "", "INDEX baz (a ASC, b DESC)"}, + {baseIndex, tableName, "", "INDEX baz ON foo.public.bar (a ASC, b DESC)"}, + {uniqueIndex, descpb.AnonymousTable, "", "UNIQUE INDEX baz (a ASC, b DESC)"}, + {invertedIndex, descpb.AnonymousTable, "", "INVERTED INDEX baz (a)"}, + {storingIndex, descpb.AnonymousTable, "", "INDEX baz (a ASC, b DESC) STORING (c)"}, + {partialIndex, descpb.AnonymousTable, "", "INDEX baz (a ASC, b DESC) WHERE a > 1:::INT8"}, + {expressionIndex, descpb.AnonymousTable, "", "INDEX baz (a ASC, (a + b) DESC, b ASC)"}, { partialIndex, descpb.AnonymousTable, " PARTITION BY LIST (a) (PARTITION p VALUES IN (2))", - "", "INDEX baz (a ASC, b DESC) PARTITION BY LIST (a) (PARTITION p VALUES IN (2)) WHERE a > 1:::INT8", }, - { - partialIndex, - descpb.AnonymousTable, - "", - " INTERLEAVE IN PARENT par (a)", - "INDEX baz (a ASC, b DESC) INTERLEAVE IN PARENT par (a) WHERE a > 1:::INT8", - }, - { - partialIndex, - descpb.AnonymousTable, - " PARTITION BY LIST (a) (PARTITION p VALUES IN (2))", - " INTERLEAVE IN PARENT par (a)", - "INDEX baz (a ASC, b DESC) INTERLEAVE IN PARENT par (a) PARTITION BY LIST (a) (PARTITION p VALUES IN (2)) WHERE a > 1:::INT8", - }, } for testIdx, tc := range testData { t.Run(strconv.Itoa(testIdx), func(t *testing.T) { got, err := indexForDisplay( - ctx, tableDesc, &tc.tableName, &tc.index, false /* isPrimary */, tc.partition, tc.interleave, &semaCtx, + ctx, tableDesc, &tc.tableName, &tc.index, false /* isPrimary */, tc.partition, &semaCtx, ) if err != nil { t.Fatalf("unexpected error: %s", err) diff --git a/pkg/sql/catalog/descpb/index.go b/pkg/sql/catalog/descpb/index.go index 581310725efd..f9ffae5bc784 100644 --- a/pkg/sql/catalog/descpb/index.go +++ b/pkg/sql/catalog/descpb/index.go @@ -17,11 +17,6 @@ import ( "github.com/cockroachdb/errors" ) -// IsInterleaved returns whether the index is interleaved or not. -func (desc *IndexDescriptor) IsInterleaved() bool { - return len(desc.Interleave.Ancestors) > 0 || len(desc.InterleavedBy) > 0 -} - // IsSharded returns whether the index is hash sharded or not. func (desc *IndexDescriptor) IsSharded() bool { return desc.Sharded.IsSharded diff --git a/pkg/sql/catalog/descpb/structured.pb.go b/pkg/sql/catalog/descpb/structured.pb.go index 801b08d74b96..1d8a5479c87a 100644 --- a/pkg/sql/catalog/descpb/structured.pb.go +++ b/pkg/sql/catalog/descpb/structured.pb.go @@ -1530,7 +1530,7 @@ type IndexDescriptor struct { ReferencedBy []ForeignKeyReference `protobuf:"bytes,10,rep,name=referenced_by,json=referencedBy" json:"referenced_by"` // Deprecated: Do not use. // Interleave, if it's not the zero value, describes how this index's data is // interleaved into another index's data. - Interleave InterleaveDescriptor `protobuf:"bytes,11,opt,name=interleave" json:"interleave"` + Interleave InterleaveDescriptor `protobuf:"bytes,11,opt,name=interleave" json:"interleave"` // Deprecated: Do not use. // InterleavedBy contains a reference to every table/index that is interleaved // into this one. // @@ -1542,7 +1542,7 @@ type IndexDescriptor struct { // despite the message used here, interleavings don't have to have // corresponding foreign key references (and whether they do or not is // irrelevant for this field). - InterleavedBy []ForeignKeyReference `protobuf:"bytes,12,rep,name=interleaved_by,json=interleavedBy" json:"interleaved_by"` + InterleavedBy []ForeignKeyReference `protobuf:"bytes,12,rep,name=interleaved_by,json=interleavedBy" json:"interleaved_by"` // Deprecated: Do not use. // Partitioning, if it's not the zero value, describes how this index's data // is partitioned into spans of keys each addressable by zone configs. Partitioning PartitioningDescriptor `protobuf:"bytes,15,opt,name=partitioning" json:"partitioning"` @@ -2232,9 +2232,6 @@ type TableDescriptor struct { // can be reused. This list is separate because mutations can // lie in this list for a long time (gc deadline) and should not block // the execution of other schema changes on the table. - // - // TODO(vivekmenezes): This is currently only used by the non-interleaved drop - // index case. Also use for dropped interleaved indexes and columns. GCMutations []TableDescriptor_GCDescriptorMutation `protobuf:"bytes,33,rep,name=gc_mutations,json=gcMutations" json:"gc_mutations"` CreateQuery string `protobuf:"bytes,34,opt,name=create_query,json=createQuery" json:"create_query"` // Starting in 19.2 CreateAsOfTime is initialized to zero for the first @@ -3891,360 +3888,360 @@ func init() { } var fileDescriptor_12dcc21c3bcc9571 = []byte{ - // 5640 bytes of a gzipped FileDescriptorProto + // 5636 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x3c, 0x4b, 0x73, 0x1c, 0xc7, - 0x79, 0xd8, 0x17, 0x76, 0xf7, 0xdb, 0xd7, 0xa0, 0x09, 0x92, 0x2b, 0x98, 0x02, 0xc0, 0xa5, 0x28, - 0x41, 0x94, 0x04, 0x52, 0x90, 0x6c, 0x53, 0x92, 0xed, 0x68, 0x5f, 0x20, 0x96, 0x04, 0x76, 0xa1, - 0xc1, 0x82, 0x94, 0xec, 0xc4, 0xe3, 0xc1, 0x4e, 0x63, 0x31, 0xc2, 0xec, 0xcc, 0x72, 0x66, 0x96, - 0xe0, 0xba, 0x72, 0x48, 0xf9, 0x94, 0x53, 0x92, 0x43, 0x6e, 0x29, 0x57, 0x5c, 0x29, 0x57, 0xe2, - 0x9b, 0xcb, 0x95, 0xaa, 0xa4, 0x2a, 0x87, 0x5c, 0xe3, 0xa3, 0x53, 0xa9, 0x4a, 0xf9, 0x84, 0x4a, - 0xe0, 0x4b, 0x7e, 0x40, 0x2a, 0x07, 0x5d, 0x92, 0xea, 0xd7, 0x3c, 0xf6, 0x01, 0x2d, 0x00, 0xc6, - 0x07, 0xa9, 0x30, 0x5f, 0x7f, 0xdf, 0xd7, 0xdd, 0x5f, 0x7f, 0xef, 0xee, 0x25, 0xdc, 0x71, 0x9e, - 0x1b, 0xf7, 0x3b, 0xaa, 0xab, 0x1a, 0x56, 0xf7, 0xbe, 0x86, 0x9d, 0x4e, 0xff, 0xe0, 0xbe, 0xe3, - 0xda, 0x83, 0x8e, 0x3b, 0xb0, 0xb1, 0xb6, 0xde, 0xb7, 0x2d, 0xd7, 0x42, 0xd7, 0x3b, 0x56, 0xe7, - 0xd8, 0xb6, 0xd4, 0xce, 0xd1, 0xba, 0xf3, 0xdc, 0x20, 0xff, 0x1d, 0xa8, 0x0e, 0x5e, 0x2a, 0x0e, - 0x5c, 0xdd, 0xb8, 0x7f, 0x64, 0x74, 0xee, 0xbb, 0x7a, 0x0f, 0x3b, 0xae, 0xda, 0xeb, 0x33, 0x82, - 0xa5, 0xd2, 0x04, 0xae, 0x7d, 0x5b, 0x7f, 0xa1, 0x1b, 0xb8, 0x8b, 0x39, 0xce, 0x75, 0x82, 0xe3, - 0x0e, 0xfb, 0xd8, 0x61, 0xff, 0xe7, 0xe0, 0xd7, 0xba, 0xd8, 0xba, 0xdf, 0xc5, 0x96, 0x6e, 0x6a, - 0xf8, 0xe5, 0xfd, 0x8e, 0x65, 0x1e, 0xea, 0x5d, 0x3e, 0xb4, 0xd8, 0xb5, 0xba, 0x16, 0xfd, 0xf3, - 0x3e, 0xf9, 0x8b, 0x41, 0x4b, 0x3f, 0x49, 0xc0, 0xb5, 0x4d, 0xcb, 0xc6, 0x7a, 0xd7, 0x7c, 0x82, - 0x87, 0x32, 0x3e, 0xc4, 0x36, 0x36, 0x3b, 0x18, 0xad, 0x42, 0xc2, 0x55, 0x0f, 0x0c, 0x5c, 0x8c, - 0xac, 0x46, 0xd6, 0x72, 0x15, 0xf8, 0xf5, 0xe9, 0xca, 0xdc, 0x57, 0xa7, 0x2b, 0xd1, 0x46, 0x4d, - 0x66, 0x03, 0xe8, 0x2e, 0x24, 0xe8, 0x2c, 0xc5, 0x28, 0xc5, 0x28, 0x70, 0x8c, 0x64, 0x83, 0x00, - 0x09, 0x1a, 0x1d, 0x45, 0x45, 0x88, 0x9b, 0x6a, 0x0f, 0x17, 0x63, 0xab, 0x91, 0xb5, 0x74, 0x25, - 0x4e, 0xb0, 0x64, 0x0a, 0x41, 0x4f, 0x20, 0xf5, 0x42, 0x35, 0x74, 0x4d, 0x77, 0x87, 0xc5, 0xf8, - 0x6a, 0x64, 0x2d, 0xbf, 0xf1, 0xf6, 0xfa, 0x44, 0x51, 0xad, 0x57, 0x2d, 0xd3, 0x71, 0x6d, 0x55, - 0x37, 0xdd, 0xa7, 0x9c, 0x80, 0x33, 0xf2, 0x18, 0xa0, 0x07, 0xb0, 0xe0, 0x1c, 0xa9, 0x36, 0xd6, - 0x94, 0xbe, 0x8d, 0x0f, 0xf5, 0x97, 0x8a, 0x81, 0xcd, 0x62, 0x62, 0x35, 0xb2, 0x96, 0xe0, 0xa8, - 0x05, 0x36, 0xbc, 0x4b, 0x47, 0xb7, 0xb1, 0x89, 0xda, 0x90, 0xb6, 0x4c, 0x45, 0xc3, 0x06, 0x76, - 0x71, 0x71, 0x9e, 0xce, 0xff, 0xfe, 0x94, 0xf9, 0x27, 0x08, 0x68, 0xbd, 0xdc, 0x71, 0x75, 0xcb, - 0x14, 0xeb, 0xb0, 0xcc, 0x1a, 0x65, 0xc4, 0xb9, 0x0e, 0xfa, 0x9a, 0xea, 0xe2, 0x62, 0xf2, 0xca, - 0x5c, 0xf7, 0x29, 0x23, 0xb4, 0x0d, 0x89, 0x9e, 0xea, 0x76, 0x8e, 0x8a, 0x29, 0xca, 0xf1, 0xc1, - 0x05, 0x38, 0xee, 0x10, 0x3a, 0xce, 0x90, 0x31, 0x29, 0x3d, 0x83, 0x79, 0x36, 0x0f, 0xca, 0x41, - 0xba, 0xd9, 0x52, 0xca, 0xd5, 0x76, 0xa3, 0xd5, 0x94, 0xe6, 0x50, 0x16, 0x52, 0x72, 0x7d, 0xaf, - 0x2d, 0x37, 0xaa, 0x6d, 0x29, 0x42, 0xbe, 0xf6, 0xea, 0x6d, 0xa5, 0xb9, 0xbf, 0xbd, 0x2d, 0x45, - 0x51, 0x01, 0x32, 0xe4, 0xab, 0x56, 0xdf, 0x2c, 0xef, 0x6f, 0xb7, 0xa5, 0x18, 0xca, 0x40, 0xb2, - 0x5a, 0xde, 0xab, 0x96, 0x6b, 0x75, 0x29, 0xbe, 0x14, 0xff, 0xc5, 0xcf, 0x97, 0xe7, 0x4a, 0x0f, - 0x20, 0x41, 0xa7, 0x43, 0x00, 0xf3, 0x7b, 0x8d, 0x9d, 0xdd, 0xed, 0xba, 0x34, 0x87, 0x52, 0x10, - 0xdf, 0x24, 0x2c, 0x22, 0x84, 0x62, 0xb7, 0x2c, 0xb7, 0x1b, 0xe5, 0x6d, 0x29, 0xca, 0x28, 0x3e, - 0x8e, 0xff, 0xd7, 0xcf, 0x56, 0x22, 0xa5, 0x7f, 0x4d, 0xc0, 0xa2, 0xbf, 0x76, 0xff, 0xb4, 0x51, - 0x15, 0x0a, 0x96, 0xad, 0x77, 0x75, 0x53, 0xa1, 0x3a, 0xa7, 0xe8, 0x1a, 0xd7, 0xc7, 0x6f, 0x90, - 0xfd, 0x9c, 0x9d, 0xae, 0xe4, 0x5a, 0x74, 0xb8, 0x4d, 0x46, 0x1b, 0x35, 0xae, 0xa0, 0x39, 0x2b, - 0x00, 0xd4, 0xd0, 0x13, 0x58, 0xe0, 0x4c, 0x3a, 0x96, 0x31, 0xe8, 0x99, 0x8a, 0xae, 0x39, 0xc5, - 0xe8, 0x6a, 0x6c, 0x2d, 0x57, 0x59, 0x39, 0x3b, 0x5d, 0x29, 0x30, 0x16, 0x55, 0x3a, 0xd6, 0xa8, - 0x39, 0x5f, 0x9d, 0xae, 0xa4, 0xc4, 0x87, 0xcc, 0xa7, 0xe7, 0xdf, 0x9a, 0x83, 0x9e, 0xc1, 0x75, - 0x5b, 0xc8, 0x56, 0x0b, 0x32, 0x8c, 0x51, 0x86, 0x77, 0xce, 0x4e, 0x57, 0xae, 0x79, 0xc2, 0xd7, - 0x26, 0x33, 0xbd, 0x66, 0x8f, 0x22, 0x68, 0x0e, 0x6a, 0x41, 0x00, 0xec, 0x6f, 0x37, 0x4e, 0xb7, - 0xbb, 0xc2, 0xb7, 0xbb, 0xe0, 0xb3, 0x0e, 0x6f, 0x79, 0xc1, 0x1e, 0x19, 0xd0, 0x3c, 0xc3, 0x4b, - 0x9c, 0x6b, 0x78, 0xf3, 0x57, 0x35, 0xbc, 0x90, 0x19, 0x25, 0xff, 0x5f, 0xcc, 0x28, 0xf5, 0xca, - 0xcd, 0x28, 0xfd, 0x0a, 0xcc, 0x88, 0xe9, 0xee, 0xe3, 0x78, 0x0a, 0xa4, 0xcc, 0xe3, 0x78, 0x2a, - 0x23, 0x65, 0x1f, 0xc7, 0x53, 0x59, 0x29, 0xf7, 0x38, 0x9e, 0xca, 0x49, 0xf9, 0xd2, 0x5f, 0x45, - 0xe1, 0xd6, 0xbe, 0xa9, 0x3f, 0x1f, 0xe0, 0x67, 0xba, 0x7b, 0x64, 0x0d, 0x5c, 0xea, 0x17, 0x03, - 0xba, 0xfd, 0x00, 0x52, 0x23, 0x4a, 0x7d, 0x9d, 0x9f, 0x72, 0x32, 0x7c, 0xb6, 0x49, 0x97, 0x9f, - 0xe8, 0x43, 0x80, 0x31, 0x0d, 0x7e, 0xed, 0xec, 0x74, 0x25, 0x3d, 0x59, 0xcd, 0xd2, 0x1d, 0x4f, - 0xb9, 0x7e, 0x4f, 0x4e, 0xb8, 0x04, 0xe9, 0xbe, 0x8d, 0x35, 0xbd, 0x43, 0x4e, 0x2d, 0xa8, 0x77, - 0x3e, 0x98, 0x5b, 0xfc, 0xdf, 0x26, 0x41, 0x62, 0x0b, 0xad, 0x61, 0xa7, 0x63, 0xeb, 0x7d, 0xd7, - 0xb2, 0xbd, 0x55, 0x46, 0xc6, 0x56, 0xf9, 0x26, 0x44, 0x75, 0x8d, 0x07, 0x9a, 0x1b, 0x5c, 0x4a, - 0x51, 0x2a, 0x20, 0x7f, 0xbb, 0x51, 0x5d, 0x43, 0xeb, 0x10, 0x27, 0xd1, 0x90, 0xee, 0x33, 0xb3, - 0xb1, 0x34, 0xba, 0x13, 0xdc, 0x5b, 0x67, 0xc1, 0xb2, 0x2d, 0x53, 0x3c, 0xb4, 0x0a, 0x29, 0x73, - 0x60, 0x18, 0x34, 0xd0, 0x91, 0xdd, 0xa7, 0xc4, 0x96, 0x04, 0x14, 0xdd, 0x86, 0xac, 0x86, 0x0f, - 0xd5, 0x81, 0xe1, 0x2a, 0xf8, 0x65, 0xdf, 0x66, 0xbb, 0x92, 0x33, 0x1c, 0x56, 0x7f, 0xd9, 0xb7, - 0xd1, 0x1b, 0x90, 0xf7, 0x74, 0x95, 0x21, 0x21, 0x8a, 0x94, 0x15, 0x7a, 0x47, 0xb1, 0x6e, 0xc1, - 0xfc, 0x91, 0xae, 0x69, 0xd8, 0xa4, 0x26, 0x27, 0x26, 0xe2, 0x30, 0xb4, 0x06, 0x59, 0xdd, 0x54, - 0x3b, 0x1d, 0xec, 0x38, 0x3a, 0x59, 0xcc, 0x42, 0x00, 0x27, 0x34, 0x82, 0x9e, 0xc3, 0x52, 0x17, - 0x9b, 0xd8, 0x56, 0x5d, 0xac, 0x29, 0xaa, 0xa3, 0xe8, 0x1a, 0x36, 0x5d, 0xdd, 0x1d, 0x2a, 0x74, - 0xe3, 0xd7, 0xe8, 0x11, 0xae, 0x4f, 0x39, 0xc2, 0x47, 0x82, 0xb0, 0xec, 0x34, 0x38, 0x59, 0x7b, - 0xd8, 0xc7, 0x7c, 0x9e, 0x9b, 0xdd, 0xc9, 0xc3, 0x68, 0x17, 0xee, 0x4e, 0x9e, 0xd2, 0xc1, 0xcf, - 0x07, 0xc4, 0x3a, 0x14, 0xab, 0x4f, 0xec, 0xad, 0xb8, 0x48, 0xf7, 0x7d, 0x7b, 0x02, 0x9f, 0x3d, - 0x8e, 0xd9, 0xa2, 0x88, 0x68, 0x03, 0x16, 0x06, 0x0e, 0x76, 0x7c, 0x06, 0x44, 0xa1, 0x81, 0x2a, - 0xf4, 0x3c, 0xd7, 0xfa, 0x02, 0x41, 0x10, 0x64, 0x44, 0x87, 0x37, 0x60, 0xc1, 0x3a, 0x31, 0x47, - 0x68, 0xb2, 0x61, 0x1a, 0x82, 0x10, 0xa4, 0xb9, 0x0d, 0xd9, 0x8e, 0xd5, 0xeb, 0x0f, 0xc4, 0xc1, - 0x64, 0xd8, 0xe9, 0x71, 0x18, 0x3d, 0x97, 0x65, 0x48, 0xbe, 0xd0, 0x6d, 0x77, 0xa0, 0x1a, 0x45, - 0x29, 0x20, 0x74, 0x01, 0x44, 0x9f, 0x82, 0xd4, 0xef, 0x2a, 0xaa, 0xeb, 0xda, 0xfa, 0x01, 0xe1, - 0x63, 0x0e, 0x7a, 0xc5, 0x5c, 0x48, 0x11, 0xf3, 0xbb, 0x8f, 0xca, 0x62, 0xb8, 0x39, 0xe8, 0xc9, - 0xf9, 0x7e, 0x37, 0xf8, 0x8d, 0x36, 0xe1, 0x75, 0xd5, 0x70, 0xb1, 0x2d, 0xa2, 0x05, 0x39, 0x28, - 0x45, 0x37, 0x95, 0xbe, 0x6d, 0x75, 0x6d, 0xec, 0x38, 0xc5, 0x7c, 0x60, 0xde, 0xd7, 0x28, 0x2a, - 0x53, 0x6a, 0x22, 0xfc, 0x86, 0xb9, 0xcb, 0xd1, 0xd0, 0x0f, 0x00, 0x39, 0x43, 0xc7, 0xc5, 0x3d, - 0xc1, 0xe8, 0x58, 0x37, 0xb5, 0x62, 0x81, 0x9e, 0xf8, 0x5b, 0x53, 0x4e, 0x7c, 0x8f, 0x12, 0x30, - 0x76, 0x4f, 0x74, 0x53, 0xe3, 0xb3, 0x48, 0xce, 0x08, 0xdc, 0x73, 0x66, 0x29, 0x29, 0xfd, 0x38, - 0x9e, 0x4a, 0x4b, 0xf0, 0x38, 0x9e, 0x4a, 0x4a, 0xa9, 0xd2, 0x9f, 0x45, 0xe1, 0x06, 0x43, 0xdb, - 0x54, 0x7b, 0xba, 0x31, 0xbc, 0xaa, 0xb9, 0x32, 0x2e, 0xdc, 0x5c, 0xe9, 0xf1, 0xd0, 0xad, 0x10, - 0x32, 0x16, 0x43, 0xe9, 0xf1, 0x10, 0x58, 0x93, 0x80, 0x46, 0x7c, 0x5e, 0xfc, 0x02, 0x3e, 0xaf, - 0x05, 0x0b, 0xc2, 0x72, 0x3d, 0x0e, 0xd4, 0x7c, 0x73, 0x95, 0x3b, 0x7c, 0x4d, 0x85, 0x1a, 0x43, - 0x10, 0xe4, 0xe1, 0xd0, 0xaf, 0x85, 0x06, 0xb9, 0x88, 0x4a, 0xff, 0x18, 0x85, 0xc5, 0x86, 0xe9, - 0x62, 0xdb, 0xc0, 0xea, 0x0b, 0x1c, 0x10, 0xc7, 0xe7, 0x90, 0x56, 0xcd, 0x0e, 0x76, 0x5c, 0xcb, - 0x76, 0x8a, 0x91, 0xd5, 0xd8, 0x5a, 0x66, 0xe3, 0xc3, 0x29, 0xa7, 0x32, 0x89, 0x7e, 0xbd, 0xcc, - 0x89, 0x85, 0xcb, 0xf4, 0x98, 0x2d, 0xfd, 0x73, 0x04, 0x52, 0x62, 0xf4, 0x12, 0x61, 0xe3, 0x9b, - 0x90, 0xa2, 0xa9, 0xb8, 0xe2, 0x9d, 0xc9, 0x92, 0xa0, 0xe0, 0xb9, 0x7a, 0x30, 0x6d, 0x4f, 0x52, - 0xdc, 0x86, 0x86, 0xaa, 0x93, 0x32, 0xea, 0x18, 0xa5, 0xbf, 0x29, 0xe4, 0xb7, 0x17, 0xce, 0xa9, - 0xc7, 0x92, 0x6c, 0x26, 0x33, 0x2e, 0xb9, 0x7f, 0x88, 0xc0, 0x02, 0x21, 0xd0, 0xb0, 0x16, 0x10, - 0xdb, 0x1d, 0x00, 0xdd, 0x51, 0x1c, 0x06, 0xa7, 0x3b, 0x12, 0xa6, 0x90, 0xd6, 0x1d, 0x8e, 0xee, - 0xa9, 0x5a, 0x74, 0x4c, 0xd5, 0x3e, 0x82, 0x1c, 0xa5, 0x55, 0x0e, 0x06, 0x9d, 0x63, 0xec, 0x3a, - 0x74, 0x85, 0x89, 0xca, 0x22, 0x5f, 0x61, 0x96, 0x72, 0xa8, 0xb0, 0x31, 0x39, 0xeb, 0x04, 0xbe, - 0xc6, 0xb4, 0x2f, 0x3e, 0xa6, 0x7d, 0x7c, 0xe1, 0xbf, 0x8c, 0xc3, 0x8d, 0x5d, 0xd5, 0x76, 0x75, - 0xe2, 0xbb, 0x74, 0xb3, 0x1b, 0x58, 0xfd, 0x5d, 0xc8, 0x98, 0x03, 0x61, 0x90, 0x0e, 0x3f, 0x10, - 0xb6, 0x3e, 0x30, 0x07, 0xdc, 0xc0, 0x1c, 0xf4, 0x2d, 0x58, 0x24, 0x68, 0x7a, 0xaf, 0x6f, 0xe8, - 0x1d, 0xdd, 0xf5, 0xf0, 0xe3, 0x01, 0x7c, 0x64, 0x0e, 0x7a, 0x0d, 0x8e, 0x20, 0xe8, 0xb6, 0x21, - 0x6e, 0xe8, 0x8e, 0x4b, 0x63, 0x7d, 0x66, 0x63, 0x63, 0x8a, 0x3a, 0x4d, 0x5e, 0xdb, 0xfa, 0xb6, - 0xee, 0xb8, 0x42, 0x56, 0x84, 0x0b, 0x6a, 0x41, 0xc2, 0x56, 0xcd, 0x2e, 0xa6, 0x76, 0x96, 0xd9, - 0xf8, 0xe0, 0x62, 0xec, 0x64, 0x42, 0x2a, 0x32, 0x20, 0xca, 0x67, 0xe9, 0xa7, 0x11, 0x88, 0x93, - 0x59, 0xce, 0x71, 0x05, 0x37, 0x60, 0xfe, 0x85, 0x6a, 0x0c, 0x30, 0xcb, 0x57, 0xb2, 0x32, 0xff, - 0x42, 0x7f, 0x04, 0x05, 0x67, 0x70, 0xd0, 0x0f, 0x4c, 0xc5, 0x83, 0xf6, 0x7b, 0x17, 0x5a, 0x95, - 0x57, 0xdc, 0x85, 0x79, 0xb1, 0x83, 0x5b, 0x7a, 0x0e, 0x09, 0xba, 0xea, 0x73, 0xd6, 0x77, 0x17, - 0xf2, 0x87, 0xb6, 0xd5, 0x53, 0x74, 0xb3, 0x63, 0x0c, 0x1c, 0xfd, 0x05, 0xcb, 0x1d, 0xb2, 0x72, - 0x8e, 0x40, 0x1b, 0x02, 0x48, 0x74, 0xc5, 0xb5, 0x14, 0xfc, 0x52, 0x20, 0x45, 0x29, 0x52, 0xc6, - 0xb5, 0xea, 0x02, 0x14, 0x52, 0xf5, 0x7f, 0xca, 0x42, 0x81, 0x1a, 0xd4, 0x4c, 0xee, 0xf2, 0x6e, - 0xc0, 0x5d, 0x5e, 0x0f, 0xb9, 0x4b, 0xcf, 0x2a, 0x89, 0xb7, 0xbc, 0x05, 0xf3, 0x03, 0x9a, 0x50, - 0xd2, 0x25, 0x7a, 0x19, 0x04, 0x83, 0xa1, 0x87, 0x90, 0x7c, 0x81, 0x6d, 0x87, 0x84, 0x61, 0x44, - 0x39, 0x2d, 0xf3, 0x82, 0xfc, 0xc6, 0xc8, 0x42, 0x9e, 0x32, 0x2c, 0x59, 0xa0, 0xa3, 0x35, 0x90, - 0x8e, 0xf1, 0x50, 0x99, 0x60, 0x0b, 0xf9, 0x63, 0x52, 0x8d, 0xf9, 0xce, 0x58, 0x83, 0xeb, 0x01, - 0x4c, 0x4d, 0xb7, 0x31, 0xcd, 0xb3, 0x9d, 0x62, 0x6a, 0x35, 0x76, 0x4e, 0x3e, 0x3d, 0xb2, 0x80, - 0xf5, 0x9a, 0x20, 0x94, 0xaf, 0x79, 0x13, 0x78, 0x30, 0x07, 0xbd, 0x0b, 0x88, 0x78, 0x3a, 0x1c, - 0x5e, 0x51, 0x82, 0xae, 0x48, 0xa2, 0x23, 0xc1, 0x35, 0x55, 0x20, 0x1f, 0x58, 0x13, 0x09, 0x12, - 0xf3, 0x34, 0x48, 0xdc, 0x22, 0xd6, 0xff, 0x44, 0xb0, 0x1f, 0x8d, 0x13, 0x59, 0x6f, 0x62, 0x12, - 0x2a, 0xf6, 0xd9, 0xbe, 0x9c, 0xc1, 0x21, 0xf1, 0x73, 0x01, 0x56, 0x49, 0xca, 0xaa, 0x74, 0x76, - 0xba, 0x82, 0x9e, 0xe0, 0xe1, 0x1e, 0x1d, 0x9f, 0xcc, 0x10, 0x1d, 0x8f, 0x8c, 0x6b, 0x0e, 0xda, - 0x02, 0x29, 0xb4, 0x11, 0xc2, 0x31, 0x4f, 0x39, 0x2e, 0x93, 0xb4, 0x61, 0xcf, 0xdf, 0xca, 0x28, - 0xb7, 0x7c, 0x60, 0x9b, 0x84, 0x53, 0x1b, 0x16, 0x49, 0xce, 0x62, 0x39, 0xba, 0x1b, 0xe2, 0x96, - 0xf3, 0xd7, 0x57, 0x15, 0xe3, 0x53, 0xd6, 0xd7, 0x19, 0x19, 0xd7, 0x1c, 0xb4, 0x07, 0x99, 0x43, - 0x56, 0xea, 0x28, 0xc7, 0x78, 0x48, 0x8b, 0xa2, 0xcc, 0xc6, 0xbd, 0xd9, 0x8b, 0xa2, 0xca, 0x3c, - 0x51, 0xb1, 0x62, 0x44, 0x86, 0x43, 0x6f, 0x10, 0x3d, 0x83, 0x5c, 0xa0, 0x8e, 0x3d, 0x18, 0xd2, - 0xb4, 0xee, 0x72, 0x6c, 0xb3, 0x3e, 0xa3, 0xca, 0x10, 0x7d, 0x06, 0xa0, 0x7b, 0x71, 0x93, 0x66, - 0x72, 0x99, 0x8d, 0x77, 0x2e, 0x10, 0x60, 0x85, 0x5b, 0xf6, 0x99, 0xa0, 0x67, 0x90, 0xf7, 0xbf, - 0xe8, 0x62, 0xb3, 0x17, 0x5e, 0x2c, 0xe3, 0x9a, 0x0b, 0xf0, 0xa9, 0x10, 0x21, 0x64, 0x43, 0xae, - 0xad, 0x70, 0x79, 0xd7, 0x16, 0x62, 0x84, 0xea, 0xbc, 0xc0, 0x91, 0x68, 0xd6, 0xf7, 0xce, 0x8c, - 0x06, 0x17, 0x48, 0xf2, 0x59, 0xdd, 0xf3, 0x01, 0xa0, 0x8e, 0x8d, 0x69, 0x3e, 0x8f, 0x5f, 0xb2, - 0x90, 0x63, 0x0c, 0x43, 0x45, 0xc7, 0x02, 0x1f, 0xaf, 0x7b, 0xc3, 0x68, 0x0b, 0x72, 0xd8, 0xec, - 0x58, 0x9a, 0x6e, 0x76, 0xfd, 0x62, 0x83, 0x27, 0x53, 0x5f, 0x9d, 0xae, 0x7c, 0x63, 0x64, 0xd6, - 0x3a, 0xc7, 0x25, 0x93, 0xcb, 0x59, 0x1c, 0xf8, 0x42, 0x5b, 0x90, 0x14, 0x01, 0x7f, 0x91, 0x4a, - 0x66, 0x6d, 0x5a, 0xfa, 0x3a, 0x9a, 0x2e, 0x88, 0xec, 0x9c, 0x93, 0x93, 0x02, 0x4e, 0xd3, 0x1d, - 0x92, 0xe8, 0x68, 0xc5, 0xeb, 0xc1, 0x02, 0x4e, 0x40, 0x51, 0x15, 0xa0, 0x8b, 0x2d, 0x85, 0xb5, - 0x42, 0x8b, 0x37, 0xe8, 0x74, 0xcb, 0x81, 0xe9, 0xba, 0xd8, 0x5a, 0x17, 0x0d, 0x53, 0x52, 0xe3, - 0x1e, 0xea, 0x5d, 0x91, 0x7f, 0x74, 0xb1, 0xc5, 0x00, 0xe1, 0xc2, 0xf6, 0xe6, 0xc4, 0xc2, 0xb6, - 0xb4, 0x0c, 0x69, 0xcf, 0x89, 0xa1, 0x24, 0xc4, 0xca, 0x7b, 0x55, 0xd6, 0xfd, 0xaa, 0xd5, 0xf7, - 0xaa, 0x52, 0xa4, 0x74, 0x1b, 0xe2, 0x74, 0xf3, 0x19, 0x48, 0x6e, 0xb6, 0xe4, 0x67, 0x65, 0xb9, - 0xc6, 0x3a, 0x6e, 0x8d, 0xe6, 0xd3, 0xba, 0xdc, 0xae, 0xd7, 0x24, 0x11, 0x3c, 0x4e, 0xe3, 0x80, - 0xfc, 0x62, 0xbb, 0x6d, 0xf1, 0xe6, 0x45, 0x17, 0x0a, 0x1d, 0x0f, 0xca, 0x0e, 0x20, 0xb2, 0x1a, - 0x5d, 0xcb, 0x6f, 0x3c, 0xfc, 0xda, 0x82, 0x5d, 0xf0, 0x08, 0x82, 0x7c, 0x95, 0xc8, 0x77, 0x42, - 0xd0, 0x40, 0xb2, 0x15, 0x1d, 0x09, 0x54, 0x32, 0x24, 0x3a, 0x47, 0xb8, 0x73, 0xcc, 0x43, 0xf5, - 0xb7, 0xa6, 0x4c, 0x4c, 0xf3, 0xd0, 0x80, 0xfa, 0x55, 0x09, 0x8d, 0x3f, 0xb5, 0xc8, 0x21, 0x28, - 0x2b, 0x24, 0x87, 0x9d, 0x50, 0xfc, 0x5c, 0xbb, 0x9e, 0xd4, 0x24, 0x14, 0x76, 0x1d, 0xf0, 0x41, - 0x0f, 0xa1, 0x60, 0x5a, 0xae, 0x42, 0x8a, 0x78, 0xee, 0x2d, 0x69, 0xd1, 0x9d, 0xab, 0x48, 0x5c, - 0x57, 0x7d, 0xbf, 0x98, 0x33, 0x2d, 0xb7, 0x39, 0x30, 0x0c, 0x06, 0x40, 0x7f, 0x12, 0x81, 0x15, - 0x16, 0x50, 0x95, 0x13, 0xd6, 0xb6, 0x51, 0x58, 0xee, 0xec, 0xcb, 0x88, 0x36, 0xb9, 0xa6, 0x67, - 0x4f, 0xe7, 0xf5, 0x7c, 0xf8, 0x52, 0x6f, 0x0d, 0xce, 0xc1, 0x29, 0xb5, 0x21, 0x1f, 0x3e, 0x26, - 0x94, 0x86, 0x44, 0x75, 0xab, 0x5e, 0x7d, 0x22, 0xcd, 0xa1, 0x02, 0x64, 0x36, 0x5b, 0x72, 0xbd, - 0xf1, 0xa8, 0xa9, 0x3c, 0xa9, 0x7f, 0xc1, 0x9a, 0xb4, 0xcd, 0x96, 0xd7, 0xa4, 0x2d, 0xc2, 0xe2, - 0x7e, 0xb3, 0xf1, 0xd9, 0x7e, 0x5d, 0x79, 0xd6, 0x68, 0x6f, 0xb5, 0xf6, 0xdb, 0x4a, 0xa3, 0x59, - 0xab, 0x7f, 0x2e, 0xc5, 0xbc, 0xfa, 0x2e, 0x21, 0xcd, 0x97, 0xfe, 0x6d, 0x1e, 0xf2, 0xbb, 0xb6, - 0xde, 0x53, 0xed, 0x21, 0x89, 0x6a, 0x27, 0x6a, 0x1f, 0x7d, 0x0a, 0x8b, 0x96, 0x41, 0x32, 0x7d, - 0x0a, 0x55, 0xbc, 0x7a, 0x21, 0x3e, 0xb9, 0xb7, 0xbf, 0x60, 0x19, 0x1a, 0xe7, 0xd0, 0xe0, 0xe5, - 0xc2, 0xa7, 0xb0, 0x68, 0xe2, 0x93, 0x71, 0x0e, 0x91, 0x29, 0x1c, 0x4c, 0x7c, 0x32, 0xc2, 0xe1, - 0x5d, 0xc8, 0x90, 0x35, 0x50, 0x4a, 0x2c, 0xfa, 0x5b, 0x99, 0x20, 0x11, 0x58, 0x86, 0xd6, 0x60, - 0xc3, 0x04, 0x9b, 0xcc, 0x27, 0xb0, 0x63, 0x13, 0xb0, 0x4d, 0x7c, 0x22, 0xb0, 0x3f, 0x82, 0x1b, - 0xe3, 0xab, 0x1b, 0x6b, 0x8f, 0x5e, 0x1b, 0x59, 0x14, 0xc9, 0x30, 0xd0, 0x97, 0xb0, 0x68, 0x58, - 0x1d, 0xd5, 0xd0, 0xdd, 0x21, 0xf7, 0x22, 0x8a, 0x73, 0xa2, 0xf6, 0xa9, 0x46, 0x65, 0xa6, 0x1a, - 0x5f, 0x58, 0xbe, 0xeb, 0xdb, 0x9c, 0x03, 0xf3, 0x27, 0x04, 0x24, 0x23, 0x63, 0x0c, 0xb6, 0xf4, - 0xf7, 0x31, 0x40, 0xe3, 0xa8, 0xe8, 0x18, 0xae, 0x11, 0xc9, 0x8c, 0x2c, 0x83, 0x8a, 0x36, 0xb3, - 0xf1, 0xcd, 0x19, 0xad, 0x30, 0xcc, 0x57, 0xb8, 0x79, 0xcb, 0xd0, 0xc2, 0x03, 0x64, 0x32, 0x22, - 0xaa, 0xd1, 0xc9, 0xa2, 0xaf, 0x60, 0x32, 0x13, 0x9f, 0x8c, 0x4c, 0xa6, 0xc3, 0xeb, 0x64, 0x32, - 0x1b, 0x77, 0x75, 0xcb, 0x54, 0x0d, 0xe5, 0x60, 0xa8, 0xd8, 0xd6, 0x49, 0xa0, 0x60, 0x67, 0x05, - 0xe7, 0xda, 0xd9, 0xe9, 0x4a, 0xb1, 0x89, 0x4f, 0x64, 0x8e, 0x57, 0x19, 0xca, 0xd6, 0xc9, 0xc4, - 0xaa, 0xbd, 0x68, 0x4e, 0xc6, 0xd2, 0x90, 0x0c, 0x6f, 0x9d, 0x33, 0x55, 0xa8, 0xc9, 0x17, 0x67, - 0x7d, 0xac, 0xc9, 0xac, 0x6a, 0x7e, 0xeb, 0x2f, 0x94, 0xf3, 0xff, 0x32, 0x02, 0x34, 0x09, 0x1b, - 0xb8, 0xa2, 0xad, 0x4f, 0xcf, 0xee, 0x43, 0xc8, 0x91, 0x69, 0xfd, 0x1d, 0x45, 0xa6, 0x78, 0x22, - 0xa2, 0xce, 0xde, 0x62, 0x3f, 0x84, 0x1c, 0x39, 0x71, 0x9f, 0x2a, 0x3a, 0x8d, 0xca, 0x32, 0xbc, - 0x4b, 0x04, 0xf4, 0x16, 0x64, 0x75, 0x93, 0xa4, 0xf5, 0xbc, 0xdd, 0x15, 0x6c, 0xf7, 0x66, 0xf8, - 0x88, 0xbf, 0xee, 0xd2, 0xaf, 0xa2, 0x70, 0x73, 0x47, 0x75, 0xb1, 0xad, 0xab, 0x86, 0xfe, 0x63, - 0xac, 0x3d, 0xd5, 0xc9, 0x86, 0x0f, 0x6d, 0xec, 0x1c, 0xa1, 0xcf, 0x61, 0x61, 0xcc, 0x60, 0xb8, - 0xc2, 0xbd, 0x39, 0x5b, 0xd6, 0x21, 0x4a, 0xb3, 0x11, 0x9b, 0x42, 0x3b, 0x61, 0xc3, 0x65, 0xa5, - 0xed, 0xc5, 0x78, 0x06, 0x2d, 0xfb, 0x21, 0x24, 0x54, 0x47, 0xb1, 0x0e, 0x79, 0x4c, 0x7a, 0x3d, - 0xc0, 0x68, 0xe0, 0xea, 0xc6, 0xfa, 0x91, 0xd1, 0x59, 0x6f, 0x8b, 0x0b, 0x56, 0x11, 0xcd, 0x54, - 0xa7, 0x75, 0x88, 0xde, 0x83, 0x82, 0x73, 0x64, 0x0d, 0x0c, 0x4d, 0x39, 0x50, 0x3b, 0xc7, 0x87, - 0xba, 0x61, 0x84, 0x7a, 0xc0, 0x79, 0x36, 0x58, 0xe1, 0x63, 0x5c, 0x66, 0x7f, 0x9e, 0x04, 0xe4, - 0xaf, 0x67, 0x67, 0xe0, 0xaa, 0x34, 0xde, 0x97, 0x61, 0x9e, 0x07, 0x1a, 0x26, 0xa3, 0xb7, 0xa6, - 0xc6, 0xe4, 0x70, 0xcf, 0x7b, 0x6b, 0x4e, 0xe6, 0x84, 0xe8, 0x7b, 0xc1, 0xfb, 0xd4, 0x99, 0x25, - 0xb2, 0x35, 0x27, 0x2e, 0x5a, 0x9f, 0x00, 0x04, 0x82, 0x54, 0x8a, 0x32, 0x79, 0x7b, 0xe6, 0xd4, - 0x60, 0x6b, 0x4e, 0x0e, 0x90, 0xa3, 0x16, 0xe4, 0xfb, 0x21, 0x0f, 0xc6, 0xab, 0x83, 0xbb, 0x33, - 0xb9, 0xbb, 0xad, 0x39, 0x79, 0x84, 0x1c, 0xfd, 0x00, 0x50, 0x67, 0xcc, 0x38, 0x8a, 0xf0, 0x35, - 0xab, 0x1c, 0x25, 0xd8, 0x9a, 0x93, 0x27, 0xb0, 0x41, 0x5f, 0xc2, 0xcd, 0xde, 0x64, 0x3d, 0xe6, - 0x75, 0xc2, 0xb4, 0x86, 0xf8, 0x14, 0xed, 0xdf, 0x9a, 0x93, 0xa7, 0x31, 0x44, 0x4f, 0x20, 0xe1, - 0xb8, 0x24, 0x0d, 0x8c, 0xd1, 0x14, 0xfc, 0xfe, 0x14, 0xce, 0xe3, 0x3a, 0xb2, 0xbe, 0x47, 0xc8, - 0x44, 0xf2, 0x43, 0x79, 0xa0, 0x67, 0x90, 0xf6, 0xaa, 0x68, 0x7e, 0xfd, 0xf2, 0xc1, 0xec, 0x0c, - 0xbd, 0x74, 0x53, 0x24, 0xa3, 0x1e, 0x2f, 0x54, 0x86, 0x4c, 0x8f, 0xa3, 0xf9, 0x6d, 0xcf, 0x55, - 0xde, 0x5b, 0x00, 0xc1, 0x81, 0xfa, 0xce, 0xc0, 0x97, 0x0c, 0x82, 0xa8, 0x41, 0x53, 0x6b, 0xdb, - 0x32, 0x0c, 0x62, 0x1b, 0x34, 0xe5, 0xf1, 0x52, 0x6b, 0x01, 0x2d, 0x7d, 0x0a, 0x09, 0xba, 0x27, - 0x92, 0xd2, 0xee, 0x37, 0x9f, 0x34, 0x5b, 0xcf, 0x9a, 0x2c, 0x45, 0xa9, 0xd5, 0xb7, 0xeb, 0xed, - 0xba, 0xd2, 0x6a, 0x6e, 0x93, 0x14, 0xe5, 0x35, 0xb8, 0xce, 0x01, 0xe5, 0x66, 0x4d, 0x79, 0x26, - 0x37, 0xc4, 0x50, 0xb4, 0xb4, 0x16, 0xcc, 0x99, 0x53, 0x10, 0x6f, 0xb6, 0x9a, 0x75, 0x69, 0x8e, - 0x66, 0xcf, 0xb5, 0x9a, 0x14, 0xa1, 0xd9, 0xb3, 0xdc, 0xda, 0x95, 0xa2, 0xcc, 0xfa, 0x2a, 0x59, - 0x00, 0xcd, 0x93, 0xc3, 0xe3, 0x78, 0x6a, 0x5e, 0x4a, 0x96, 0xfe, 0x2e, 0x02, 0x29, 0x12, 0xa8, - 0x1b, 0xe6, 0xa1, 0x85, 0x3e, 0x80, 0x74, 0x5f, 0xb5, 0xb1, 0xe9, 0xfa, 0x9e, 0x56, 0x34, 0xa0, - 0x53, 0xbb, 0x74, 0xc0, 0xeb, 0x8f, 0xa6, 0x18, 0x62, 0x43, 0x43, 0x9b, 0x20, 0x71, 0x22, 0xa7, - 0x73, 0x84, 0x7b, 0xaa, 0x1f, 0x77, 0x6e, 0x79, 0x2d, 0x7e, 0x3a, 0xbe, 0x47, 0x87, 0x3d, 0x0e, - 0xf9, 0x7e, 0x10, 0x7a, 0x4e, 0x97, 0x92, 0xfb, 0x8e, 0xbf, 0x7c, 0x1b, 0x0a, 0x23, 0x81, 0xf2, - 0x9c, 0xae, 0xd0, 0x2a, 0xed, 0x0a, 0xc5, 0x7c, 0xbf, 0xef, 0x75, 0x85, 0xa2, 0xbc, 0x21, 0xf4, - 0x81, 0xdf, 0xf2, 0x21, 0x07, 0x1c, 0xaf, 0xbc, 0xc6, 0xc3, 0xc3, 0xc2, 0x39, 0xdd, 0x9e, 0x5d, - 0x58, 0xe8, 0x59, 0x9a, 0x7e, 0x48, 0x8a, 0x16, 0xa2, 0x1d, 0xae, 0xde, 0xc3, 0x3c, 0xa5, 0x9d, - 0xc9, 0x77, 0x4a, 0x41, 0x6a, 0x32, 0x88, 0xb6, 0x21, 0xaf, 0x11, 0xaf, 0x41, 0xea, 0x42, 0xd6, - 0xab, 0xb9, 0x4e, 0x7d, 0xfa, 0xca, 0x14, 0x4d, 0x16, 0x87, 0x25, 0x4a, 0x67, 0x41, 0xcc, 0xfa, - 0x39, 0xa1, 0x13, 0x8c, 0xcf, 0x78, 0x82, 0x07, 0xb0, 0x34, 0x30, 0xf1, 0xcb, 0xbe, 0xe5, 0x60, - 0x4d, 0x19, 0x3b, 0xcb, 0x35, 0xca, 0xe5, 0x2e, 0xe7, 0x72, 0x73, 0x5f, 0x60, 0x4e, 0x3c, 0xd4, - 0x9b, 0x83, 0x89, 0xc3, 0x1a, 0x7a, 0x04, 0x49, 0xd1, 0xb6, 0x4d, 0xd1, 0xfd, 0xcd, 0xea, 0xe3, - 0x45, 0xcd, 0xca, 0xa9, 0xd1, 0x26, 0xe4, 0x4d, 0xfc, 0x32, 0x78, 0x2b, 0x91, 0x0e, 0x99, 0x67, - 0xb6, 0x89, 0x5f, 0x4e, 0xbe, 0x92, 0xc8, 0x9a, 0xfe, 0x88, 0x86, 0x5a, 0x90, 0x3a, 0x54, 0x7b, - 0xba, 0xa1, 0x63, 0xa7, 0x78, 0x83, 0xae, 0xe8, 0xbd, 0x73, 0x57, 0x34, 0x7a, 0x81, 0x23, 0xec, - 0x59, 0x30, 0xf1, 0x16, 0x46, 0x01, 0x43, 0xb2, 0xb0, 0x9b, 0xe3, 0x0b, 0x13, 0x17, 0x38, 0xa1, - 0xcb, 0x1c, 0xba, 0x30, 0xfe, 0xa5, 0xa1, 0xcf, 0x20, 0x17, 0xce, 0x1b, 0xe0, 0x12, 0x79, 0x43, - 0xb6, 0x1f, 0x4c, 0x1a, 0x36, 0x21, 0x29, 0x12, 0x86, 0xcc, 0x25, 0x12, 0x06, 0x41, 0x8c, 0x2a, - 0x24, 0x1b, 0x7b, 0xe9, 0xfa, 0xe5, 0x49, 0xd6, 0xef, 0x95, 0x9e, 0x9d, 0xae, 0x64, 0xc8, 0x0e, - 0x27, 0x5c, 0x8a, 0x64, 0x4c, 0x0f, 0xae, 0xa1, 0xc7, 0x00, 0xde, 0x6b, 0x2c, 0x87, 0xde, 0x05, - 0x4e, 0xef, 0x18, 0xed, 0x0a, 0x44, 0x7f, 0x49, 0x72, 0x80, 0x1a, 0xed, 0x40, 0x5a, 0xb8, 0x5c, - 0xd6, 0x1b, 0x9c, 0x1e, 0x0d, 0xc7, 0x03, 0x80, 0x70, 0xfb, 0x1e, 0x07, 0x52, 0xa0, 0x1b, 0x58, - 0x75, 0x30, 0x6f, 0x38, 0x3d, 0x9c, 0x31, 0x5b, 0x67, 0x3a, 0x5e, 0x3d, 0x52, 0xcd, 0x2e, 0xde, - 0x26, 0xf4, 0x95, 0x68, 0x31, 0x22, 0x33, 0x56, 0xa8, 0x09, 0x12, 0x15, 0x59, 0x30, 0x9e, 0x48, - 0x54, 0x6a, 0x6f, 0x08, 0xef, 0x48, 0xa4, 0x36, 0x35, 0xa6, 0x50, 0x9d, 0xda, 0xf1, 0xe3, 0xca, - 0x77, 0x20, 0x7f, 0x68, 0xd9, 0x3d, 0xd5, 0x55, 0x84, 0xf3, 0x5a, 0xf0, 0x3b, 0xdf, 0x5f, 0x9d, - 0xae, 0xe4, 0x36, 0xe9, 0xa8, 0x70, 0x5c, 0xb9, 0xc3, 0xe0, 0x27, 0xaa, 0x88, 0xf0, 0xcb, 0x6e, - 0xba, 0xdf, 0xfc, 0x5a, 0x61, 0x4d, 0x88, 0xba, 0xef, 0x40, 0xde, 0x3a, 0x3c, 0x34, 0x74, 0x13, - 0x2b, 0x36, 0x56, 0x1d, 0xcb, 0x2c, 0xbe, 0x19, 0xf0, 0xbe, 0x39, 0x3e, 0x26, 0xd3, 0x21, 0xd4, - 0x84, 0x79, 0xda, 0xa8, 0x70, 0x8a, 0x8b, 0xf4, 0x78, 0x2e, 0xd9, 0xf4, 0x90, 0x39, 0x17, 0x74, - 0x07, 0xe0, 0x85, 0x8e, 0x4f, 0x94, 0xe7, 0x03, 0x6c, 0x0f, 0x8b, 0xc5, 0x60, 0x2f, 0x89, 0xc0, - 0x3f, 0x23, 0x60, 0xf4, 0x2d, 0x58, 0xd4, 0x1d, 0x25, 0x98, 0x82, 0x28, 0x64, 0xb0, 0xf8, 0x76, - 0x20, 0x0e, 0x23, 0xdd, 0x19, 0x4d, 0x5f, 0xd0, 0xfb, 0x90, 0xd6, 0x70, 0x1f, 0x9b, 0x9a, 0xd3, - 0x32, 0x8b, 0xaf, 0xd1, 0x92, 0xf8, 0xda, 0xd9, 0xe9, 0x4a, 0xba, 0x26, 0x80, 0xdc, 0xc9, 0xf9, - 0x58, 0xe8, 0x53, 0xc8, 0x7b, 0x1f, 0xed, 0x61, 0x1f, 0x3b, 0xc5, 0xf7, 0x28, 0x5d, 0x91, 0x1c, - 0x6c, 0x2d, 0x34, 0x22, 0xc2, 0x5e, 0x18, 0x1f, 0x7d, 0x09, 0x59, 0x06, 0xc1, 0x5a, 0xcb, 0xac, - 0x0c, 0x8b, 0x4b, 0x54, 0x4e, 0x0f, 0x66, 0x94, 0x93, 0xdf, 0x49, 0xf5, 0xee, 0xec, 0x6a, 0x01, - 0x6e, 0x72, 0x88, 0x37, 0xfa, 0x43, 0xc8, 0x0a, 0x3d, 0x7c, 0x6c, 0x1d, 0x38, 0xc5, 0x6f, 0x9c, - 0x7b, 0x31, 0x36, 0x3a, 0xd7, 0x8e, 0x4f, 0x2a, 0xbc, 0x4c, 0x90, 0x1b, 0x6a, 0x03, 0x29, 0x1f, - 0x45, 0xe4, 0xe8, 0x50, 0x7b, 0x50, 0xbe, 0xb4, 0x0e, 0x88, 0xca, 0xaf, 0xaf, 0x46, 0xd6, 0x62, - 0x5e, 0x42, 0xb0, 0xd8, 0xc4, 0x27, 0x41, 0xab, 0x79, 0x6c, 0x1d, 0x34, 0x6a, 0xf2, 0xa2, 0x39, - 0x0e, 0xd5, 0xd0, 0xe7, 0x90, 0x0b, 0x3e, 0x94, 0x70, 0x8a, 0xb7, 0xce, 0x6d, 0x20, 0x8d, 0x19, - 0xa7, 0xff, 0x74, 0xc2, 0x91, 0xb3, 0x4e, 0xe0, 0x0b, 0xdd, 0x86, 0xb4, 0x66, 0x5b, 0x7d, 0x16, - 0xc3, 0x5f, 0xa7, 0x0b, 0x14, 0xed, 0x4f, 0xdb, 0xea, 0xd3, 0xe0, 0xac, 0x40, 0xde, 0xc6, 0x7d, - 0x43, 0xed, 0xe0, 0x1e, 0x09, 0x8a, 0xd6, 0x61, 0x71, 0x99, 0xce, 0xbe, 0x31, 0xf3, 0xf1, 0x78, - 0xc4, 0xc2, 0x3e, 0x02, 0xfc, 0x5a, 0x87, 0x68, 0x1f, 0x40, 0x1d, 0x68, 0xba, 0xab, 0xf4, 0x2c, - 0x0d, 0x17, 0x57, 0xce, 0x7d, 0x58, 0x35, 0xca, 0xbc, 0x4c, 0x08, 0x77, 0x2c, 0x0d, 0x7b, 0x77, - 0xde, 0x02, 0x80, 0xde, 0x87, 0x0c, 0xdd, 0x1a, 0x97, 0xfe, 0x2a, 0xdd, 0xdc, 0x02, 0x97, 0x7e, - 0xba, 0x66, 0x5b, 0x7d, 0x26, 0x72, 0x2a, 0x00, 0x26, 0x67, 0x07, 0xb2, 0xdd, 0x8e, 0xe2, 0xbb, - 0xd3, 0xdb, 0x54, 0x37, 0x3e, 0x99, 0x71, 0x2d, 0x8f, 0xaa, 0x13, 0x1c, 0xec, 0x35, 0x11, 0x17, - 0x1e, 0x55, 0x05, 0xcc, 0x91, 0x33, 0xdd, 0x8e, 0xf7, 0x41, 0x4a, 0x6e, 0xd6, 0x29, 0xe7, 0x06, - 0x5d, 0x0a, 0x96, 0xdc, 0x6c, 0x84, 0x99, 0x74, 0x13, 0x78, 0x4b, 0x5d, 0xa1, 0xe5, 0x2a, 0x3b, - 0xb3, 0x3b, 0xb3, 0xe7, 0x5d, 0x79, 0x46, 0x5d, 0x76, 0x5a, 0x87, 0xf4, 0x60, 0x3b, 0x90, 0xb5, - 0x06, 0xee, 0x81, 0x35, 0x30, 0x35, 0xe5, 0xf0, 0xd8, 0x29, 0xbe, 0x41, 0x77, 0x7b, 0xa1, 0xc6, - 0xa9, 0xb7, 0xbb, 0x16, 0x67, 0xb4, 0xf9, 0xc4, 0x91, 0x33, 0x82, 0xeb, 0xe6, 0xb1, 0x83, 0x7e, - 0x04, 0x19, 0xdd, 0xf4, 0xe7, 0xb8, 0x7b, 0xf1, 0x39, 0x90, 0xa8, 0x39, 0x1a, 0xa6, 0x37, 0x05, - 0x70, 0x9e, 0x64, 0x86, 0x9f, 0x44, 0x60, 0xf5, 0x6b, 0x1a, 0xae, 0x4e, 0xf1, 0x9d, 0x73, 0xef, - 0xab, 0x67, 0xe8, 0xb8, 0xbe, 0x7e, 0x5e, 0xc7, 0xd5, 0x41, 0x25, 0x48, 0xbb, 0xb8, 0xd7, 0xb7, - 0x6c, 0xd5, 0x1e, 0x16, 0xdf, 0x0a, 0x3e, 0x41, 0xf0, 0xc0, 0xe8, 0x87, 0x50, 0x18, 0x6d, 0x89, - 0xdd, 0xbb, 0x42, 0x4b, 0x4c, 0xce, 0x87, 0xdb, 0x7f, 0x68, 0x9d, 0x16, 0x21, 0xec, 0xa6, 0x47, - 0x51, 0x0d, 0x43, 0x39, 0x18, 0x16, 0xdf, 0x0d, 0xb6, 0x23, 0xbc, 0xd1, 0xb2, 0x61, 0x54, 0x86, - 0x4b, 0xbf, 0x88, 0xc0, 0xc2, 0x58, 0xdc, 0x46, 0x3f, 0x84, 0xa4, 0x69, 0x69, 0x81, 0xc7, 0x21, - 0x75, 0x2e, 0xff, 0xf9, 0xa6, 0xa5, 0xb1, 0xb7, 0x21, 0x1f, 0x74, 0x75, 0xf7, 0x68, 0x70, 0xb0, - 0xde, 0xb1, 0x7a, 0xf7, 0xbd, 0x95, 0x6b, 0x07, 0xfe, 0xdf, 0xf7, 0xfb, 0xc7, 0xdd, 0xfb, 0xf4, - 0xaf, 0xfe, 0xc1, 0x3a, 0x23, 0x93, 0xe7, 0x09, 0xd7, 0x86, 0x86, 0xde, 0x83, 0x02, 0x7e, 0xd9, - 0xd7, 0xed, 0x40, 0xed, 0x10, 0x0d, 0xf8, 0x9d, 0xbc, 0x3f, 0x48, 0x94, 0x94, 0x5f, 0xc3, 0xff, - 0x2a, 0x0a, 0x85, 0x91, 0x70, 0x48, 0xea, 0x1e, 0xda, 0xa2, 0x0a, 0xd5, 0x3d, 0x04, 0x72, 0xce, - 0x5b, 0x8f, 0xe0, 0x5b, 0xc5, 0xd8, 0x55, 0xdf, 0x2a, 0x86, 0x1f, 0x16, 0x25, 0x2e, 0xf0, 0xb0, - 0xe8, 0x23, 0xb8, 0xa1, 0x3b, 0x8a, 0x69, 0x99, 0xe2, 0x82, 0xc1, 0x6b, 0xba, 0x04, 0x5f, 0xf6, - 0x5d, 0xd3, 0x9d, 0xa6, 0x65, 0xb2, 0xab, 0x05, 0x6f, 0xd7, 0xfe, 0x23, 0xc0, 0xe4, 0xf8, 0x23, - 0x40, 0xaf, 0x47, 0x1f, 0x97, 0x12, 0x4b, 0xff, 0x12, 0x81, 0x74, 0xf0, 0x35, 0x7e, 0x34, 0xdc, - 0x39, 0x1c, 0xab, 0x05, 0x2f, 0xf9, 0xc8, 0x27, 0x2c, 0x85, 0xd8, 0x05, 0xa4, 0x70, 0x1b, 0x12, - 0x07, 0x43, 0x51, 0xa3, 0xa5, 0x2a, 0x59, 0x3e, 0x5b, 0xbc, 0x42, 0xea, 0x81, 0xf8, 0xc1, 0x50, - 0x3c, 0x98, 0x5a, 0xfa, 0x63, 0xc8, 0x04, 0xe2, 0xee, 0x68, 0x67, 0x22, 0x72, 0x89, 0xce, 0xc4, - 0x1b, 0x30, 0xcf, 0xc3, 0x02, 0xd3, 0xbd, 0x1c, 0xa7, 0x4e, 0xb0, 0x90, 0x90, 0xf8, 0x92, 0x84, - 0x03, 0x3e, 0xfb, 0x7f, 0xc7, 0x20, 0x1b, 0x8c, 0xa0, 0xc4, 0xd6, 0x75, 0xb3, 0x63, 0xd3, 0xf0, - 0x45, 0x67, 0x8f, 0x79, 0xcf, 0x8d, 0x04, 0x98, 0xc4, 0xd5, 0x9e, 0x6e, 0x2a, 0xf4, 0xa9, 0x4a, - 0x48, 0xbf, 0x53, 0x3d, 0xdd, 0x7c, 0x4a, 0xa0, 0x14, 0x45, 0x7d, 0xc9, 0x51, 0x62, 0x21, 0x14, - 0xf5, 0x25, 0x43, 0x59, 0xa2, 0xa9, 0xaa, 0xed, 0x52, 0x09, 0xc5, 0x02, 0x29, 0xa8, 0xed, 0x06, - 0x5f, 0x1d, 0x26, 0x26, 0xbd, 0x3a, 0x34, 0x21, 0xef, 0xe7, 0x0c, 0x27, 0x26, 0xb6, 0xf9, 0x75, - 0x43, 0xf9, 0x12, 0x49, 0x83, 0xff, 0x41, 0x18, 0x89, 0x28, 0xee, 0x04, 0x81, 0x24, 0x2b, 0xed, - 0xa8, 0x9d, 0x23, 0xac, 0x38, 0xfa, 0x8f, 0x59, 0x3b, 0xc0, 0x13, 0x0b, 0x85, 0xef, 0xe9, 0x3f, - 0xc6, 0x4b, 0x7f, 0x13, 0x81, 0x5c, 0x88, 0x17, 0x6a, 0x40, 0x81, 0xae, 0x6e, 0xac, 0xbd, 0x7d, - 0xdb, 0x7b, 0x9f, 0x4f, 0x86, 0x27, 0x16, 0xb3, 0x39, 0x2b, 0x30, 0xa4, 0x91, 0x3c, 0x94, 0xb1, - 0xf2, 0x5e, 0xb7, 0x85, 0xd5, 0x38, 0x4b, 0x39, 0x85, 0x9f, 0xb8, 0x65, 0x2d, 0x1f, 0xa6, 0x05, - 0x9b, 0xf1, 0x4b, 0x26, 0x64, 0x02, 0x99, 0xcb, 0x0c, 0xf6, 0xf3, 0x6d, 0x88, 0x7b, 0xde, 0x6c, - 0xd6, 0x2e, 0xb2, 0xeb, 0xbb, 0xb8, 0x9f, 0x45, 0x60, 0x71, 0x52, 0x06, 0x11, 0xb2, 0x4b, 0xa6, - 0x6d, 0x33, 0xd9, 0xe5, 0x9d, 0x60, 0x66, 0xc7, 0x34, 0x50, 0xbc, 0x8a, 0xf0, 0x73, 0xbb, 0x37, - 0x3d, 0x3b, 0x60, 0x0a, 0x58, 0x08, 0xd9, 0x01, 0xa9, 0xe0, 0x82, 0x96, 0xf0, 0xef, 0x31, 0xc8, - 0x8f, 0xdc, 0xbe, 0x3c, 0x85, 0xf9, 0xae, 0x61, 0x1d, 0xa8, 0x06, 0xef, 0x5a, 0x7f, 0xe7, 0x52, - 0xa1, 0x6c, 0xfd, 0x11, 0xe5, 0xb1, 0x35, 0x27, 0x73, 0x6e, 0xc8, 0x81, 0x85, 0xe0, 0x35, 0x0b, - 0xfb, 0x21, 0x11, 0x93, 0x6c, 0xfd, 0x72, 0x53, 0xf8, 0xf7, 0x30, 0x14, 0x71, 0x6b, 0x4e, 0x2e, - 0xd8, 0x61, 0x10, 0xea, 0x41, 0x61, 0xe4, 0x6e, 0x87, 0x5f, 0x09, 0x54, 0xaf, 0x3a, 0xa5, 0x6c, - 0x9d, 0x6c, 0xd1, 0xbc, 0x37, 0x00, 0x58, 0xfa, 0x03, 0x28, 0x8c, 0x2c, 0x8a, 0x9c, 0x07, 0xc3, - 0xe1, 0x51, 0x2d, 0x4f, 0x7c, 0x18, 0x43, 0x6a, 0xaa, 0x3d, 0x2c, 0xf3, 0x51, 0x7e, 0x1e, 0x77, - 0x21, 0x17, 0x9a, 0x02, 0xe5, 0x21, 0xaa, 0xb2, 0x27, 0x84, 0x69, 0x39, 0xaa, 0xf2, 0xc7, 0x87, - 0x4b, 0x79, 0x98, 0x67, 0xf2, 0x0d, 0xea, 0x77, 0x05, 0x20, 0x25, 0xf2, 0x87, 0xd2, 0x1a, 0xa4, - 0xbd, 0x44, 0x1a, 0x65, 0x21, 0x55, 0x6b, 0xec, 0x95, 0x2b, 0xdb, 0xf5, 0x9a, 0x34, 0x87, 0x72, - 0x90, 0x96, 0xeb, 0xe5, 0x1a, 0xed, 0xb9, 0x4a, 0x91, 0x8f, 0x53, 0x7f, 0xfa, 0xb3, 0x95, 0x08, - 0x0f, 0x32, 0xf3, 0x52, 0xf2, 0x71, 0x3c, 0x85, 0xa4, 0x6b, 0xa5, 0xff, 0x4d, 0x03, 0xaa, 0xa9, - 0xae, 0x4a, 0x84, 0x72, 0x81, 0xce, 0x64, 0xf4, 0x1c, 0x6b, 0x9a, 0xd8, 0x64, 0x8c, 0x5f, 0xa5, - 0xc9, 0x78, 0xa9, 0x5e, 0xe7, 0x78, 0x67, 0x72, 0xfe, 0x0a, 0x9d, 0xc9, 0x70, 0xdf, 0x27, 0x76, - 0xa5, 0xbe, 0xcf, 0x53, 0x48, 0xb2, 0x2a, 0x93, 0xbd, 0x31, 0x9b, 0xde, 0x56, 0x18, 0x3f, 0x18, - 0xde, 0xad, 0x71, 0xea, 0xa6, 0x6b, 0x0f, 0xbd, 0xf7, 0x30, 0x0c, 0xe6, 0xb7, 0x47, 0x52, 0xaf, - 0xb2, 0x3d, 0x92, 0x9e, 0xde, 0x1e, 0xf9, 0x01, 0x70, 0xbb, 0x10, 0x49, 0x31, 0x9c, 0xfb, 0x34, - 0x64, 0xc2, 0x76, 0x98, 0x11, 0xf0, 0xac, 0x38, 0x6b, 0x07, 0xbe, 0xd0, 0x8f, 0x00, 0x89, 0x7b, - 0xd9, 0x80, 0xe4, 0xd9, 0x95, 0xce, 0xfb, 0x53, 0xb7, 0x46, 0x09, 0x26, 0x1d, 0x80, 0x78, 0x0f, - 0xee, 0x8d, 0x39, 0x4b, 0x6d, 0x00, 0xde, 0xe0, 0x35, 0x0f, 0xad, 0x19, 0xc2, 0xc4, 0x32, 0x24, - 0x89, 0xfb, 0xed, 0x63, 0xa6, 0xff, 0x5e, 0xdc, 0xe6, 0x40, 0x6e, 0xb3, 0x7d, 0xc8, 0x06, 0x0f, - 0x09, 0x49, 0x10, 0x3b, 0xc6, 0x43, 0x6e, 0xda, 0xe4, 0x4f, 0xf4, 0x18, 0x12, 0x7e, 0x76, 0x31, - 0xfd, 0xa1, 0xf8, 0xd4, 0xd3, 0x27, 0xcb, 0x95, 0x19, 0x8b, 0x8f, 0xa3, 0x0f, 0x69, 0x8a, 0x9d, - 0x0d, 0x0a, 0x12, 0x35, 0x21, 0xe7, 0x0c, 0xec, 0x17, 0xfa, 0x0b, 0xd5, 0x50, 0xba, 0x96, 0x6a, - 0xd0, 0x89, 0xf2, 0x1b, 0x77, 0xa6, 0x3d, 0xb4, 0xe2, 0xb8, 0x8f, 0x2c, 0xd5, 0x10, 0xad, 0x11, - 0x27, 0x00, 0x43, 0x1f, 0x79, 0x17, 0x82, 0xfc, 0x06, 0x9d, 0x5f, 0x2e, 0x23, 0x6e, 0x86, 0x41, - 0x3f, 0x27, 0xba, 0xbf, 0x0c, 0x44, 0x22, 0x3b, 0x57, 0x11, 0x4c, 0x1f, 0x41, 0x8b, 0xb6, 0xbe, - 0x17, 0xd9, 0x19, 0x5e, 0xdd, 0x1c, 0xf4, 0xfc, 0xc8, 0x6e, 0xfb, 0x30, 0x0d, 0x6d, 0x41, 0xda, - 0x8b, 0xe5, 0xd4, 0xfc, 0xf3, 0x1b, 0x6f, 0x9c, 0x23, 0xb1, 0xdd, 0x91, 0x8e, 0x85, 0x4f, 0xec, - 0xa5, 0xd8, 0x11, 0x29, 0xea, 0x7b, 0xc3, 0xd2, 0xff, 0x64, 0x21, 0xdf, 0x1e, 0xf6, 0x27, 0x79, - 0xbf, 0xd8, 0x14, 0xef, 0x17, 0x9f, 0xed, 0x5e, 0x26, 0x7d, 0xb5, 0x7b, 0x19, 0x78, 0xb5, 0xf7, - 0x32, 0x99, 0x57, 0xe6, 0xfd, 0xf2, 0x57, 0xf2, 0x7e, 0xaf, 0xec, 0x96, 0x2e, 0x7a, 0x89, 0x5b, - 0xba, 0xef, 0x42, 0x4e, 0xb5, 0x6d, 0x75, 0xc8, 0x7f, 0x87, 0xa3, 0x51, 0x57, 0x99, 0x63, 0x67, - 0x74, 0x76, 0xba, 0x92, 0x29, 0x93, 0x41, 0xfa, 0xd3, 0x1b, 0xc1, 0x21, 0xa3, 0x7a, 0x20, 0xcd, - 0xf7, 0xb0, 0xb9, 0x57, 0xe9, 0x61, 0x0b, 0xd3, 0x3d, 0x6c, 0x0d, 0xe2, 0xf4, 0x87, 0x3e, 0x4c, - 0xef, 0xa7, 0x89, 0x3c, 0xac, 0xbe, 0xeb, 0x81, 0xdf, 0xfa, 0x50, 0x6a, 0xf4, 0x23, 0x58, 0x12, - 0xaf, 0x69, 0x89, 0x3e, 0xf8, 0xb7, 0xa8, 0x81, 0x9f, 0x51, 0x95, 0xce, 0x4e, 0x57, 0x8a, 0xb2, - 0x8f, 0xe5, 0xf3, 0x63, 0x75, 0x20, 0x91, 0x45, 0xd1, 0x9e, 0x38, 0xae, 0x39, 0xe8, 0x0b, 0xc8, - 0x52, 0xfb, 0xee, 0xe1, 0xde, 0x01, 0xb6, 0x45, 0xa8, 0x7d, 0x30, 0xdb, 0x7a, 0x89, 0xa1, 0xef, - 0x50, 0x42, 0xd1, 0x3b, 0xc3, 0x1e, 0xc4, 0x41, 0x0f, 0x20, 0xa1, 0x1a, 0x3a, 0x8d, 0x95, 0x5f, - 0xf7, 0xbb, 0x3e, 0x86, 0xc8, 0x5e, 0x21, 0x07, 0xc3, 0x92, 0x74, 0x7e, 0xd7, 0x33, 0xbc, 0x9a, - 0xe9, 0x21, 0x69, 0xe9, 0xa7, 0x31, 0x00, 0x7f, 0xb1, 0xe8, 0xdb, 0x70, 0xb3, 0x7f, 0x34, 0x74, - 0xf4, 0x8e, 0x6a, 0x28, 0x36, 0xee, 0xdb, 0xd8, 0xc1, 0x26, 0xcb, 0xfc, 0xa9, 0x5e, 0x67, 0xe5, - 0x1b, 0x62, 0x58, 0x0e, 0x8d, 0xa2, 0x4f, 0xe0, 0x86, 0x61, 0x75, 0x27, 0xd1, 0x05, 0xfb, 0x1e, - 0xd7, 0x39, 0xce, 0x08, 0xb1, 0x4a, 0xaa, 0xb5, 0xbe, 0x7a, 0xa0, 0x1b, 0x7e, 0x2b, 0xe4, 0x93, - 0x8b, 0x0a, 0x7a, 0xbd, 0xea, 0xb1, 0x10, 0xcf, 0x6a, 0x7c, 0xa6, 0xe8, 0x87, 0xe3, 0x2f, 0x13, - 0x3e, 0xbe, 0xf0, 0x0c, 0xd3, 0x1f, 0x28, 0x94, 0xde, 0x00, 0xf0, 0xe7, 0xa7, 0x17, 0xfe, 0xdb, - 0xdb, 0x7e, 0xc2, 0xca, 0x9f, 0x0e, 0x94, 0xee, 0x7d, 0xcd, 0xfb, 0x00, 0x80, 0x79, 0xb9, 0xbe, - 0xd3, 0x7a, 0x5a, 0x17, 0x2f, 0x04, 0x96, 0x5a, 0x23, 0x71, 0x70, 0x3c, 0x6e, 0x45, 0x66, 0x8c, - 0x5b, 0xfc, 0xd2, 0xfe, 0x33, 0x88, 0x13, 0x63, 0x22, 0xb3, 0xd7, 0x9b, 0xfb, 0x3b, 0xd2, 0x1c, - 0x4a, 0x43, 0xa2, 0xbc, 0xdd, 0x28, 0xef, 0x49, 0x11, 0xb4, 0x08, 0xd2, 0xce, 0xfe, 0x76, 0xbb, - 0x21, 0xd7, 0x1f, 0x35, 0x5a, 0x4d, 0x85, 0x22, 0x44, 0xd1, 0x32, 0x2c, 0xb5, 0x49, 0x06, 0xae, - 0x34, 0x76, 0x76, 0xb7, 0x1b, 0xd5, 0x46, 0x5b, 0x91, 0xeb, 0xd5, 0x96, 0x5c, 0x53, 0xda, 0x5f, - 0xec, 0xd6, 0xa5, 0x58, 0x20, 0xf0, 0xfc, 0x75, 0x1c, 0x24, 0xe6, 0x98, 0x26, 0x84, 0x9e, 0xe8, - 0x25, 0x9e, 0x04, 0xfc, 0xde, 0xf3, 0xbf, 0x89, 0x61, 0x2b, 0xf1, 0x8a, 0x32, 0xfd, 0xf9, 0x2b, - 0x64, 0xfa, 0xc9, 0x57, 0xf5, 0x06, 0x61, 0xd6, 0xf8, 0x14, 0x0e, 0x90, 0xf1, 0xab, 0x04, 0xc8, - 0x80, 0x86, 0xfc, 0x3c, 0x0a, 0x10, 0xd0, 0x8d, 0xef, 0x05, 0xff, 0x59, 0x8e, 0xe9, 0xb7, 0xe0, - 0x23, 0xa5, 0xed, 0xd6, 0x9c, 0xf8, 0x47, 0x3b, 0x1e, 0x41, 0x4a, 0xe3, 0x39, 0x25, 0x4f, 0x3d, - 0xdf, 0x9e, 0x39, 0xf5, 0xdc, 0x9a, 0x93, 0x3d, 0x62, 0xf4, 0x49, 0xe8, 0x97, 0xd6, 0x77, 0x67, - 0x72, 0x0d, 0x5b, 0xe2, 0xe7, 0x07, 0x65, 0x98, 0x67, 0x31, 0x9c, 0x8b, 0x69, 0xea, 0xaf, 0x57, - 0x47, 0x4c, 0x63, 0x6b, 0x4e, 0xe6, 0x84, 0xbc, 0x0c, 0x4e, 0x42, 0x62, 0x60, 0xea, 0x96, 0x79, - 0x4f, 0x0e, 0x3e, 0x99, 0x17, 0x3d, 0x5f, 0xe2, 0x4d, 0xe8, 0xdf, 0xaa, 0x8b, 0x35, 0xf6, 0x32, - 0x69, 0xdf, 0x7c, 0xe1, 0x01, 0x22, 0x28, 0x0f, 0xc0, 0xc7, 0x75, 0xb3, 0x2b, 0x45, 0x69, 0xf1, - 0x4c, 0x12, 0x79, 0xf2, 0x15, 0xbb, 0xf7, 0x5d, 0x90, 0x46, 0x7f, 0x3e, 0x1b, 0xf0, 0x41, 0x0b, - 0x90, 0xdb, 0x79, 0x5a, 0xad, 0xb6, 0x1b, 0x3b, 0xf5, 0xbd, 0x76, 0x79, 0x67, 0x97, 0xbd, 0xc5, - 0xa6, 0x76, 0xdf, 0x6a, 0xd4, 0xa4, 0xe8, 0xbd, 0x03, 0xb8, 0x39, 0xe5, 0xf7, 0xd6, 0xe8, 0x26, - 0x5c, 0x6b, 0xb6, 0xda, 0x4a, 0xa3, 0x56, 0x6f, 0xb6, 0x1b, 0xed, 0x2f, 0x94, 0x6a, 0x6b, 0x7b, - 0x7f, 0xa7, 0x29, 0xcd, 0x11, 0x7f, 0xf2, 0xa8, 0xde, 0xac, 0xcb, 0xe5, 0x76, 0xbd, 0xa6, 0x94, - 0xb7, 0x9f, 0x95, 0xbf, 0x20, 0x5e, 0xa6, 0x08, 0x8b, 0x3e, 0xb4, 0xf2, 0x85, 0xf7, 0x6f, 0x70, - 0x44, 0xef, 0x7d, 0x17, 0x0a, 0x23, 0xa6, 0x4c, 0x5c, 0xe2, 0xee, 0x7e, 0x65, 0xbb, 0x51, 0x9d, - 0xf8, 0x8e, 0x0a, 0x65, 0x20, 0xd9, 0xda, 0xdc, 0xdc, 0x6e, 0x34, 0xeb, 0x52, 0xec, 0xde, 0x87, - 0x90, 0x0d, 0x26, 0xfe, 0x48, 0x82, 0xec, 0xf7, 0x5b, 0xcd, 0xba, 0xb2, 0x59, 0x6e, 0x6c, 0xef, - 0xcb, 0x64, 0x97, 0x08, 0xf2, 0xdc, 0xb7, 0x09, 0x58, 0xe4, 0xde, 0xbb, 0x90, 0x0b, 0x65, 0xd9, - 0x84, 0xa7, 0x58, 0xd2, 0x1c, 0x91, 0xa9, 0xf8, 0x37, 0x44, 0xea, 0x35, 0x29, 0x52, 0x59, 0xfb, - 0xf5, 0x7f, 0x2e, 0xcf, 0xfd, 0xfa, 0x6c, 0x39, 0xf2, 0x9b, 0xb3, 0xe5, 0xc8, 0x6f, 0xcf, 0x96, - 0x23, 0xff, 0x71, 0xb6, 0x1c, 0xf9, 0x8b, 0xdf, 0x2d, 0xcf, 0xfd, 0xe6, 0x77, 0xcb, 0x73, 0xbf, - 0xfd, 0xdd, 0xf2, 0xdc, 0xf7, 0xe7, 0xd9, 0x3f, 0x71, 0xf3, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, - 0x1e, 0xc5, 0x86, 0xa6, 0x4d, 0x47, 0x00, 0x00, + 0x79, 0xd8, 0xf7, 0xee, 0xb7, 0xaf, 0x41, 0x13, 0x24, 0x57, 0xb0, 0x04, 0x80, 0x4b, 0x51, 0x82, + 0x28, 0x09, 0xa4, 0x20, 0xd9, 0xa6, 0x24, 0xdb, 0xd1, 0xbe, 0x40, 0x2c, 0x09, 0xec, 0x42, 0x83, + 0x05, 0x29, 0xda, 0x89, 0xc7, 0x83, 0x9d, 0xc6, 0x62, 0x84, 0xd9, 0x99, 0xe5, 0xcc, 0x2c, 0xc1, + 0x75, 0xe5, 0x90, 0xf2, 0x29, 0xa7, 0x24, 0x87, 0xdc, 0x52, 0xae, 0xb8, 0x52, 0xae, 0xc4, 0x37, + 0x97, 0x2b, 0x55, 0xc9, 0xcd, 0xd7, 0xf8, 0xe8, 0x54, 0xaa, 0x52, 0x3e, 0xa1, 0x12, 0xf8, 0x92, + 0x1f, 0x90, 0xca, 0x41, 0x97, 0xa4, 0xfa, 0x35, 0x8f, 0x7d, 0x40, 0x4b, 0x90, 0xf1, 0x41, 0x2a, + 0xcc, 0xd7, 0xdf, 0xf7, 0xf5, 0xd7, 0xdd, 0xdf, 0xbb, 0x7b, 0x09, 0x37, 0x9d, 0xa7, 0xc6, 0x9d, + 0xae, 0xea, 0xaa, 0x86, 0xd5, 0xbb, 0xa3, 0x61, 0xa7, 0x3b, 0x38, 0xbc, 0xe3, 0xb8, 0xf6, 0xb0, + 0xeb, 0x0e, 0x6d, 0xac, 0x6d, 0x0c, 0x6c, 0xcb, 0xb5, 0xd0, 0xd5, 0xae, 0xd5, 0x3d, 0xb1, 0x2d, + 0xb5, 0x7b, 0xbc, 0xe1, 0x3c, 0x35, 0xc8, 0x7f, 0x87, 0xaa, 0x83, 0x97, 0x4b, 0x43, 0x57, 0x37, + 0xee, 0x1c, 0x1b, 0xdd, 0x3b, 0xae, 0xde, 0xc7, 0x8e, 0xab, 0xf6, 0x07, 0x8c, 0x60, 0xb9, 0x3c, + 0x85, 0xeb, 0xc0, 0xd6, 0x9f, 0xe9, 0x06, 0xee, 0x61, 0x8e, 0x73, 0x95, 0xe0, 0xb8, 0xa3, 0x01, + 0x76, 0xd8, 0xff, 0x39, 0xf8, 0xb5, 0x1e, 0xb6, 0xee, 0xf4, 0xb0, 0xa5, 0x9b, 0x1a, 0x7e, 0x7e, + 0xa7, 0x6b, 0x99, 0x47, 0x7a, 0x8f, 0x0f, 0x2d, 0xf5, 0xac, 0x9e, 0x45, 0xff, 0xbc, 0x43, 0xfe, + 0x62, 0xd0, 0xf2, 0x4f, 0x12, 0x70, 0x65, 0xcb, 0xb2, 0xb1, 0xde, 0x33, 0x1f, 0xe2, 0x91, 0x8c, + 0x8f, 0xb0, 0x8d, 0xcd, 0x2e, 0x46, 0x6b, 0x90, 0x70, 0xd5, 0x43, 0x03, 0x97, 0x22, 0x6b, 0x91, + 0xf5, 0x7c, 0x15, 0x7e, 0x73, 0xb6, 0xba, 0xf0, 0xd5, 0xd9, 0x6a, 0xb4, 0x59, 0x97, 0xd9, 0x00, + 0xba, 0x05, 0x09, 0x3a, 0x4b, 0x29, 0x4a, 0x31, 0x8a, 0x1c, 0x23, 0xd5, 0x24, 0x40, 0x82, 0x46, + 0x47, 0x51, 0x09, 0xe2, 0xa6, 0xda, 0xc7, 0xa5, 0xd8, 0x5a, 0x64, 0x3d, 0x53, 0x8d, 0x13, 0x2c, + 0x99, 0x42, 0xd0, 0x43, 0x48, 0x3f, 0x53, 0x0d, 0x5d, 0xd3, 0xdd, 0x51, 0x29, 0xbe, 0x16, 0x59, + 0x2f, 0x6c, 0xbe, 0xb3, 0x31, 0x75, 0xab, 0x36, 0x6a, 0x96, 0xe9, 0xb8, 0xb6, 0xaa, 0x9b, 0xee, + 0x23, 0x4e, 0xc0, 0x19, 0x79, 0x0c, 0xd0, 0x5d, 0x58, 0x74, 0x8e, 0x55, 0x1b, 0x6b, 0xca, 0xc0, + 0xc6, 0x47, 0xfa, 0x73, 0xc5, 0xc0, 0x66, 0x29, 0xb1, 0x16, 0x59, 0x4f, 0x70, 0xd4, 0x22, 0x1b, + 0xde, 0xa3, 0xa3, 0x3b, 0xd8, 0x44, 0x1d, 0xc8, 0x58, 0xa6, 0xa2, 0x61, 0x03, 0xbb, 0xb8, 0x94, + 0xa4, 0xf3, 0x7f, 0x30, 0x63, 0xfe, 0x29, 0x1b, 0xb4, 0x51, 0xe9, 0xba, 0xba, 0x65, 0x0a, 0x39, + 0x2c, 0xb3, 0x4e, 0x19, 0x71, 0xae, 0xc3, 0x81, 0xa6, 0xba, 0xb8, 0x94, 0x7a, 0x69, 0xae, 0x07, + 0x94, 0x11, 0xda, 0x81, 0x44, 0x5f, 0x75, 0xbb, 0xc7, 0xa5, 0x34, 0xe5, 0x78, 0xf7, 0x05, 0x38, + 0xee, 0x12, 0x3a, 0xce, 0x90, 0x31, 0x29, 0x3f, 0x86, 0x24, 0x9b, 0x07, 0xe5, 0x21, 0xd3, 0x6a, + 0x2b, 0x95, 0x5a, 0xa7, 0xd9, 0x6e, 0x49, 0x0b, 0x28, 0x07, 0x69, 0xb9, 0xb1, 0xdf, 0x91, 0x9b, + 0xb5, 0x8e, 0x14, 0x21, 0x5f, 0xfb, 0x8d, 0x8e, 0xd2, 0x3a, 0xd8, 0xd9, 0x91, 0xa2, 0xa8, 0x08, + 0x59, 0xf2, 0x55, 0x6f, 0x6c, 0x55, 0x0e, 0x76, 0x3a, 0x52, 0x0c, 0x65, 0x21, 0x55, 0xab, 0xec, + 0xd7, 0x2a, 0xf5, 0x86, 0x14, 0x5f, 0x8e, 0xff, 0xe2, 0xe7, 0x2b, 0x0b, 0xe5, 0xbb, 0x90, 0xa0, + 0xd3, 0x21, 0x80, 0xe4, 0x7e, 0x73, 0x77, 0x6f, 0xa7, 0x21, 0x2d, 0xa0, 0x34, 0xc4, 0xb7, 0x08, + 0x8b, 0x08, 0xa1, 0xd8, 0xab, 0xc8, 0x9d, 0x66, 0x65, 0x47, 0x8a, 0x32, 0x8a, 0x4f, 0xe2, 0xff, + 0xf5, 0xb3, 0xd5, 0x48, 0xf9, 0x5f, 0x13, 0xb0, 0xe4, 0xcb, 0xee, 0x9f, 0x36, 0xaa, 0x41, 0xd1, + 0xb2, 0xf5, 0x9e, 0x6e, 0x2a, 0x54, 0xe7, 0x14, 0x5d, 0xe3, 0xfa, 0xf8, 0x0d, 0xb2, 0x9e, 0xf3, + 0xb3, 0xd5, 0x7c, 0x9b, 0x0e, 0x77, 0xc8, 0x68, 0xb3, 0xce, 0x15, 0x34, 0x6f, 0x05, 0x80, 0x1a, + 0x7a, 0x08, 0x8b, 0x9c, 0x49, 0xd7, 0x32, 0x86, 0x7d, 0x53, 0xd1, 0x35, 0xa7, 0x14, 0x5d, 0x8b, + 0xad, 0xe7, 0xab, 0xab, 0xe7, 0x67, 0xab, 0x45, 0xc6, 0xa2, 0x46, 0xc7, 0x9a, 0x75, 0xe7, 0xab, + 0xb3, 0xd5, 0xb4, 0xf8, 0x90, 0xf9, 0xf4, 0xfc, 0x5b, 0x73, 0xd0, 0x63, 0xb8, 0x6a, 0x8b, 0xbd, + 0xd5, 0x82, 0x0c, 0x63, 0x94, 0xe1, 0xcd, 0xf3, 0xb3, 0xd5, 0x2b, 0xde, 0xe6, 0x6b, 0xd3, 0x99, + 0x5e, 0xb1, 0xc7, 0x11, 0x34, 0x07, 0xb5, 0x21, 0x00, 0xf6, 0x97, 0x1b, 0xa7, 0xcb, 0x5d, 0xe5, + 0xcb, 0x5d, 0xf4, 0x59, 0x87, 0x97, 0xbc, 0x68, 0x8f, 0x0d, 0x68, 0x9e, 0xe1, 0x25, 0x2e, 0x34, + 0xbc, 0xe4, 0xcb, 0x1a, 0x5e, 0xc8, 0x8c, 0x52, 0xff, 0x2f, 0x66, 0x94, 0x7e, 0xe5, 0x66, 0x94, + 0x79, 0x05, 0x66, 0xc4, 0x74, 0xf7, 0x41, 0x3c, 0x0d, 0x52, 0xf6, 0x41, 0x3c, 0x9d, 0x95, 0x72, + 0x0f, 0xe2, 0xe9, 0x9c, 0x94, 0x7f, 0x10, 0x4f, 0xe7, 0xa5, 0x42, 0xf9, 0x6f, 0xa2, 0xf0, 0xfa, + 0x81, 0xa9, 0x3f, 0x1d, 0xe2, 0xc7, 0xba, 0x7b, 0x6c, 0x0d, 0x5d, 0xea, 0x17, 0x03, 0xba, 0x7d, + 0x17, 0xd2, 0x63, 0x4a, 0x7d, 0x95, 0x9f, 0x72, 0x2a, 0x7c, 0xb6, 0x29, 0x97, 0x9f, 0xe8, 0x3d, + 0x80, 0x09, 0x0d, 0x7e, 0xed, 0xfc, 0x6c, 0x35, 0x33, 0x5d, 0xcd, 0x32, 0x5d, 0x4f, 0xb9, 0xfe, + 0x40, 0x4e, 0xb8, 0x0c, 0x99, 0x81, 0x8d, 0x35, 0xbd, 0x4b, 0x4e, 0x2d, 0xa8, 0x77, 0x3e, 0x98, + 0x5b, 0xfc, 0xdf, 0xa7, 0x40, 0x62, 0x82, 0xd6, 0xb1, 0xd3, 0xb5, 0xf5, 0x81, 0x6b, 0xd9, 0x9e, + 0x94, 0x91, 0x09, 0x29, 0xdf, 0x82, 0xa8, 0xae, 0xf1, 0x40, 0x73, 0x8d, 0xef, 0x52, 0x94, 0x6e, + 0x90, 0xbf, 0xdc, 0xa8, 0xae, 0xa1, 0x0d, 0x88, 0x93, 0x68, 0x48, 0xd7, 0x99, 0xdd, 0x5c, 0x1e, + 0x5f, 0x09, 0xee, 0x6f, 0xb0, 0x60, 0xd9, 0x91, 0x29, 0x1e, 0x5a, 0x83, 0xb4, 0x39, 0x34, 0x0c, + 0x1a, 0xe8, 0xc8, 0xea, 0xd3, 0x62, 0x49, 0x02, 0x8a, 0x6e, 0x40, 0x4e, 0xc3, 0x47, 0xea, 0xd0, + 0x70, 0x15, 0xfc, 0x7c, 0x60, 0xb3, 0x55, 0xc9, 0x59, 0x0e, 0x6b, 0x3c, 0x1f, 0xd8, 0xe8, 0x4d, + 0x28, 0x78, 0xba, 0xca, 0x90, 0x10, 0x45, 0xca, 0x09, 0xbd, 0xa3, 0x58, 0xaf, 0x43, 0xf2, 0x58, + 0xd7, 0x34, 0x6c, 0x52, 0x93, 0x13, 0x13, 0x71, 0x18, 0x5a, 0x87, 0x9c, 0x6e, 0xaa, 0xdd, 0x2e, + 0x76, 0x1c, 0x9d, 0x08, 0xb3, 0x18, 0xc0, 0x09, 0x8d, 0xa0, 0xa7, 0xb0, 0xdc, 0xc3, 0x26, 0xb6, + 0x55, 0x17, 0x6b, 0x8a, 0xea, 0x28, 0xba, 0x86, 0x4d, 0x57, 0x77, 0x47, 0x0a, 0x5d, 0xf8, 0x15, + 0x7a, 0x84, 0x1b, 0x33, 0x8e, 0xf0, 0xbe, 0x20, 0xac, 0x38, 0x4d, 0x4e, 0xd6, 0x19, 0x0d, 0x30, + 0x9f, 0xe7, 0x7a, 0x6f, 0xfa, 0x30, 0xda, 0x83, 0x5b, 0xd3, 0xa7, 0x74, 0xf0, 0xd3, 0x21, 0xb1, + 0x0e, 0xc5, 0x1a, 0x10, 0x7b, 0x2b, 0x2d, 0xd1, 0x75, 0xdf, 0x98, 0xc2, 0x67, 0x9f, 0x63, 0xb6, + 0x29, 0x22, 0xda, 0x84, 0xc5, 0xa1, 0x83, 0x1d, 0x9f, 0x01, 0x51, 0x68, 0xa0, 0x0a, 0x9d, 0xe4, + 0x5a, 0x5f, 0x24, 0x08, 0x82, 0x8c, 0xe8, 0xf0, 0x26, 0x2c, 0x5a, 0xa7, 0xe6, 0x18, 0x4d, 0x2e, + 0x4c, 0x43, 0x10, 0x82, 0x34, 0x37, 0x20, 0xd7, 0xb5, 0xfa, 0x83, 0xa1, 0x38, 0x98, 0x2c, 0x3b, + 0x3d, 0x0e, 0xa3, 0xe7, 0xb2, 0x02, 0xa9, 0x67, 0xba, 0xed, 0x0e, 0x55, 0xa3, 0x24, 0x05, 0x36, + 0x5d, 0x00, 0xd1, 0x67, 0x20, 0x0d, 0x7a, 0x8a, 0xea, 0xba, 0xb6, 0x7e, 0x48, 0xf8, 0x98, 0xc3, + 0x7e, 0x29, 0x1f, 0x52, 0xc4, 0xc2, 0xde, 0xfd, 0x8a, 0x18, 0x6e, 0x0d, 0xfb, 0x72, 0x61, 0xd0, + 0x0b, 0x7e, 0xa3, 0x2d, 0x78, 0x43, 0x35, 0x5c, 0x6c, 0x8b, 0x68, 0x41, 0x0e, 0x4a, 0xd1, 0x4d, + 0x65, 0x60, 0x5b, 0x3d, 0x1b, 0x3b, 0x4e, 0xa9, 0x10, 0x98, 0xf7, 0x35, 0x8a, 0xca, 0x94, 0x9a, + 0x6c, 0x7e, 0xd3, 0xdc, 0xe3, 0x68, 0xe8, 0x07, 0x80, 0x9c, 0x91, 0xe3, 0xe2, 0xbe, 0x60, 0x74, + 0xa2, 0x9b, 0x5a, 0xa9, 0x48, 0x4f, 0xfc, 0xed, 0x19, 0x27, 0xbe, 0x4f, 0x09, 0x18, 0xbb, 0x87, + 0xba, 0xa9, 0xf1, 0x59, 0x24, 0x67, 0x0c, 0xee, 0x39, 0xb3, 0xb4, 0x94, 0x79, 0x10, 0x4f, 0x67, + 0x24, 0x78, 0x10, 0x4f, 0xa7, 0xa4, 0x74, 0xf9, 0x2f, 0xa2, 0x70, 0x8d, 0xa1, 0x6d, 0xa9, 0x7d, + 0xdd, 0x18, 0xbd, 0xac, 0xb9, 0x32, 0x2e, 0xdc, 0x5c, 0xe9, 0xf1, 0xd0, 0xa5, 0x10, 0x32, 0x16, + 0x43, 0xe9, 0xf1, 0x10, 0x58, 0x8b, 0x80, 0xc6, 0x7c, 0x5e, 0xfc, 0x05, 0x7c, 0x5e, 0x1b, 0x16, + 0x85, 0xe5, 0x7a, 0x1c, 0xa8, 0xf9, 0xe6, 0xab, 0x37, 0xb9, 0x4c, 0xc5, 0x3a, 0x43, 0x10, 0xe4, + 0xe1, 0xd0, 0xaf, 0x85, 0x06, 0xf9, 0x16, 0x95, 0xff, 0x39, 0x0a, 0x4b, 0x4d, 0xd3, 0xc5, 0xb6, + 0x81, 0xd5, 0x67, 0x38, 0xb0, 0x1d, 0x5f, 0x40, 0x46, 0x35, 0xbb, 0xd8, 0x71, 0x2d, 0xdb, 0x29, + 0x45, 0xd6, 0x62, 0xeb, 0xd9, 0xcd, 0x8f, 0x66, 0x9c, 0xca, 0x34, 0xfa, 0x8d, 0x0a, 0x27, 0x16, + 0x2e, 0xd3, 0x63, 0xb6, 0xfc, 0xeb, 0x08, 0xa4, 0xc5, 0xe8, 0x25, 0xc2, 0xc6, 0x37, 0x21, 0x4d, + 0x53, 0x71, 0xc5, 0x3b, 0x93, 0x65, 0x41, 0xc1, 0x73, 0xf5, 0x60, 0xda, 0x9e, 0xa2, 0xb8, 0x4d, + 0x0d, 0xd5, 0xa6, 0x65, 0xd4, 0x31, 0x4a, 0x7f, 0x5d, 0xec, 0xdf, 0x7e, 0x38, 0xa7, 0x9e, 0x48, + 0xb2, 0xd9, 0x9e, 0xf1, 0x9d, 0xfb, 0xa7, 0x08, 0x2c, 0x12, 0x02, 0x0d, 0x6b, 0x81, 0x6d, 0xbb, + 0x09, 0xa0, 0x3b, 0x8a, 0xc3, 0xe0, 0x74, 0x45, 0xc2, 0x14, 0x32, 0xba, 0xc3, 0xd1, 0x3d, 0x55, + 0x8b, 0x4e, 0xa8, 0xda, 0xc7, 0x90, 0xa7, 0xb4, 0xca, 0xe1, 0xb0, 0x7b, 0x82, 0x5d, 0x87, 0x4a, + 0x98, 0xa8, 0x2e, 0x71, 0x09, 0x73, 0x94, 0x43, 0x95, 0x8d, 0xc9, 0x39, 0x27, 0xf0, 0x35, 0xa1, + 0x7d, 0xf1, 0x09, 0xed, 0xe3, 0x82, 0xff, 0x32, 0x0e, 0xd7, 0xf6, 0x54, 0xdb, 0xd5, 0x89, 0xef, + 0xd2, 0xcd, 0x5e, 0x40, 0xfa, 0x5b, 0x90, 0x35, 0x87, 0xc2, 0x20, 0x1d, 0x7e, 0x20, 0x4c, 0x3e, + 0x30, 0x87, 0xdc, 0xc0, 0x1c, 0xf4, 0x2d, 0x58, 0x22, 0x68, 0x7a, 0x7f, 0x60, 0xe8, 0x5d, 0xdd, + 0xf5, 0xf0, 0xe3, 0x01, 0x7c, 0x64, 0x0e, 0xfb, 0x4d, 0x8e, 0x20, 0xe8, 0x76, 0x20, 0x6e, 0xe8, + 0x8e, 0x4b, 0x63, 0x7d, 0x76, 0x73, 0x73, 0x86, 0x3a, 0x4d, 0x97, 0x6d, 0x63, 0x47, 0x77, 0x5c, + 0xb1, 0x57, 0x84, 0x0b, 0x6a, 0x43, 0xc2, 0x56, 0xcd, 0x1e, 0xa6, 0x76, 0x96, 0xdd, 0xfc, 0xf0, + 0xc5, 0xd8, 0xc9, 0x84, 0x54, 0x64, 0x40, 0x94, 0xcf, 0xf2, 0x4f, 0x23, 0x10, 0x27, 0xb3, 0x5c, + 0xe0, 0x0a, 0xae, 0x41, 0xf2, 0x99, 0x6a, 0x0c, 0x31, 0xcb, 0x57, 0x72, 0x32, 0xff, 0x42, 0x7f, + 0x02, 0x45, 0x67, 0x78, 0x38, 0x08, 0x4c, 0xc5, 0x83, 0xf6, 0xfb, 0x2f, 0x24, 0x95, 0x57, 0xdc, + 0x85, 0x79, 0xb1, 0x83, 0x5b, 0x7e, 0x0a, 0x09, 0x2a, 0xf5, 0x05, 0xf2, 0xdd, 0x82, 0xc2, 0x91, + 0x6d, 0xf5, 0x15, 0xdd, 0xec, 0x1a, 0x43, 0x47, 0x7f, 0xc6, 0x72, 0x87, 0x9c, 0x9c, 0x27, 0xd0, + 0xa6, 0x00, 0x12, 0x5d, 0x71, 0x2d, 0x05, 0x3f, 0x17, 0x48, 0x51, 0x8a, 0x94, 0x75, 0xad, 0x86, + 0x00, 0x85, 0x54, 0xfd, 0xd7, 0x39, 0x28, 0x52, 0x83, 0x9a, 0xcb, 0x5d, 0xde, 0x0a, 0xb8, 0xcb, + 0xab, 0x21, 0x77, 0xe9, 0x59, 0x25, 0xf1, 0x96, 0xaf, 0x43, 0x72, 0x48, 0x13, 0x4a, 0x2a, 0xa2, + 0x97, 0x41, 0x30, 0x18, 0xba, 0x07, 0xa9, 0x67, 0xd8, 0x76, 0x48, 0x18, 0x46, 0x94, 0xd3, 0x0a, + 0x2f, 0xc8, 0xaf, 0x8d, 0x09, 0xf2, 0x88, 0x61, 0xc9, 0x02, 0x1d, 0xad, 0x83, 0x74, 0x82, 0x47, + 0xca, 0x14, 0x5b, 0x28, 0x9c, 0x90, 0x6a, 0xcc, 0x77, 0xc6, 0x1a, 0x5c, 0x0d, 0x60, 0x6a, 0xba, + 0x8d, 0x69, 0x9e, 0xed, 0x94, 0xd2, 0x6b, 0xb1, 0x0b, 0xf2, 0xe9, 0x31, 0x01, 0x36, 0xea, 0x82, + 0x50, 0xbe, 0xe2, 0x4d, 0xe0, 0xc1, 0x1c, 0xf4, 0x1e, 0x20, 0xe2, 0xe9, 0x70, 0x58, 0xa2, 0x04, + 0x95, 0x48, 0xa2, 0x23, 0x41, 0x99, 0xaa, 0x50, 0x08, 0xc8, 0x44, 0x82, 0x44, 0x92, 0x06, 0x89, + 0xd7, 0x89, 0xf5, 0x3f, 0x14, 0xec, 0xc7, 0xe3, 0x44, 0xce, 0x9b, 0x98, 0x84, 0x8a, 0x03, 0xb6, + 0x2e, 0x67, 0x78, 0x44, 0xfc, 0x5c, 0x80, 0x55, 0x8a, 0xb2, 0x2a, 0x9f, 0x9f, 0xad, 0xa2, 0x87, + 0x78, 0xb4, 0x4f, 0xc7, 0xa7, 0x33, 0x44, 0x27, 0x63, 0xe3, 0x9a, 0x83, 0xb6, 0x41, 0x0a, 0x2d, + 0x84, 0x70, 0x2c, 0x50, 0x8e, 0x2b, 0x24, 0x6d, 0xd8, 0xf7, 0x97, 0x32, 0xce, 0xad, 0x10, 0x58, + 0x26, 0xe1, 0xd4, 0x81, 0x25, 0x92, 0xb3, 0x58, 0x8e, 0xee, 0x86, 0xb8, 0xe5, 0x7d, 0xf9, 0x6a, + 0x62, 0x7c, 0x86, 0x7c, 0xdd, 0xb1, 0x71, 0xcd, 0x41, 0xfb, 0x90, 0x3d, 0x62, 0xa5, 0x8e, 0x72, + 0x82, 0x47, 0xb4, 0x28, 0xca, 0x6e, 0xde, 0x9e, 0xbf, 0x28, 0xaa, 0x26, 0x89, 0x8a, 0x95, 0x22, + 0x32, 0x1c, 0x79, 0x83, 0xe8, 0x31, 0xe4, 0x03, 0x75, 0xec, 0xe1, 0x88, 0xa6, 0x75, 0x97, 0x63, + 0x9b, 0xf3, 0x19, 0x55, 0x47, 0x68, 0x1f, 0x40, 0xf7, 0xe2, 0x26, 0xcd, 0xe4, 0xb2, 0x9b, 0xef, + 0xbe, 0x40, 0x80, 0xf5, 0xa5, 0xf5, 0xd9, 0xa0, 0x27, 0x50, 0xf0, 0xbf, 0xa8, 0xb8, 0xb9, 0x4b, + 0x8b, 0x9b, 0x0f, 0x70, 0xaa, 0x92, 0x8d, 0xc8, 0x85, 0xdc, 0x5b, 0xf1, 0xf2, 0xee, 0x2d, 0xc4, + 0x08, 0x35, 0x78, 0x91, 0x23, 0xd1, 0xcc, 0xef, 0xdd, 0x39, 0x8d, 0x2e, 0x90, 0xe8, 0xb3, 0xda, + 0xe7, 0x43, 0x40, 0x5d, 0x1b, 0xd3, 0x9c, 0x1e, 0x3f, 0x67, 0x61, 0xc7, 0x18, 0x85, 0x0a, 0x8f, + 0x45, 0x3e, 0xde, 0xf0, 0x86, 0xd1, 0x36, 0xe4, 0xb1, 0xd9, 0xb5, 0x34, 0xdd, 0xec, 0xf9, 0x05, + 0x07, 0x4f, 0xa8, 0xbe, 0x3a, 0x5b, 0xfd, 0xc6, 0xd8, 0xac, 0x0d, 0x8e, 0x4b, 0x26, 0x97, 0x73, + 0x38, 0xf0, 0x85, 0xb6, 0x21, 0x25, 0x82, 0xfe, 0x12, 0xdd, 0x99, 0xf5, 0x59, 0x29, 0xec, 0x78, + 0xca, 0x20, 0x32, 0x74, 0x4e, 0x4e, 0x8a, 0x38, 0x4d, 0x77, 0x48, 0xb2, 0xa3, 0x95, 0xae, 0x06, + 0x8b, 0x38, 0x01, 0x45, 0x35, 0x80, 0x1e, 0xb6, 0x14, 0xd6, 0x0e, 0x2d, 0x5d, 0xa3, 0xd3, 0xad, + 0x04, 0xa6, 0xeb, 0x61, 0x6b, 0x43, 0x34, 0x4d, 0x49, 0x9d, 0x7b, 0xa4, 0xf7, 0x44, 0x0e, 0xd2, + 0xc3, 0x16, 0x03, 0x84, 0x8b, 0xdb, 0xeb, 0x53, 0x8b, 0xdb, 0xf2, 0x0a, 0x64, 0x3c, 0x47, 0x86, + 0x52, 0x10, 0xab, 0xec, 0xd7, 0x58, 0x07, 0xac, 0xde, 0xd8, 0xaf, 0x49, 0x91, 0xf2, 0x0d, 0x88, + 0xd3, 0xc5, 0x67, 0x21, 0xb5, 0xd5, 0x96, 0x1f, 0x57, 0xe4, 0x3a, 0xeb, 0xba, 0x35, 0x5b, 0x8f, + 0x1a, 0x72, 0xa7, 0x51, 0x97, 0x44, 0x00, 0x39, 0x8b, 0x03, 0xf2, 0x0b, 0xee, 0x8e, 0xc5, 0x1b, + 0x18, 0x3d, 0x28, 0x76, 0x3d, 0x28, 0x3b, 0x80, 0xc8, 0x5a, 0x74, 0xbd, 0xb0, 0x79, 0xef, 0x6b, + 0x8b, 0x76, 0xc1, 0x23, 0x08, 0xf2, 0x55, 0xa2, 0xd0, 0x0d, 0x41, 0x03, 0x09, 0x57, 0x74, 0x2c, + 0x58, 0xc9, 0x90, 0xe8, 0x1e, 0xe3, 0xee, 0x09, 0x0f, 0xd7, 0xdf, 0x9a, 0x31, 0x31, 0xcd, 0x45, + 0x03, 0xea, 0x57, 0x23, 0x34, 0xfe, 0xd4, 0x22, 0x8f, 0xa0, 0xac, 0x90, 0x1c, 0x76, 0x44, 0xf1, + 0x0b, 0x6d, 0x7b, 0x5a, 0xa3, 0x50, 0xa4, 0x5c, 0x01, 0x3f, 0x74, 0x0f, 0x8a, 0xa6, 0xe5, 0x2a, + 0xa4, 0x90, 0xe7, 0x1e, 0x93, 0x16, 0xde, 0xf9, 0xaa, 0xc4, 0x75, 0xd5, 0xf7, 0x8d, 0x79, 0xd3, + 0x72, 0x5b, 0x43, 0xc3, 0x60, 0x00, 0xf4, 0x67, 0x11, 0x58, 0x65, 0x41, 0x55, 0x39, 0x65, 0xad, + 0x1b, 0x85, 0xe5, 0xcf, 0xfe, 0x1e, 0xd1, 0x46, 0xd7, 0xec, 0x0c, 0xea, 0xa2, 0xbe, 0x0f, 0x17, + 0xf5, 0xf5, 0xe1, 0x05, 0x38, 0xe5, 0x0e, 0x14, 0xc2, 0xc7, 0x84, 0x32, 0x90, 0xa8, 0x6d, 0x37, + 0x6a, 0x0f, 0xa5, 0x05, 0x54, 0x84, 0xec, 0x56, 0x5b, 0x6e, 0x34, 0xef, 0xb7, 0x94, 0x87, 0x8d, + 0x27, 0xac, 0x51, 0xdb, 0x6a, 0x7b, 0x8d, 0xda, 0x12, 0x2c, 0x1d, 0xb4, 0x9a, 0x9f, 0x1f, 0x34, + 0x94, 0xc7, 0xcd, 0xce, 0x76, 0xfb, 0xa0, 0xa3, 0x34, 0x5b, 0xf5, 0xc6, 0x17, 0x52, 0xcc, 0xab, + 0xf1, 0x12, 0x52, 0xb2, 0xfc, 0x6f, 0x49, 0x28, 0xec, 0xd9, 0x7a, 0x5f, 0xb5, 0x47, 0x24, 0xb2, + 0x9d, 0xaa, 0x03, 0xf4, 0x19, 0x2c, 0x59, 0x06, 0xc9, 0xf6, 0x29, 0x54, 0xf1, 0x6a, 0x86, 0xf8, + 0xf4, 0xfe, 0xfe, 0xa2, 0x65, 0x68, 0x9c, 0x43, 0x93, 0x97, 0x0c, 0x9f, 0xc1, 0x92, 0x89, 0x4f, + 0x27, 0x39, 0x44, 0x66, 0x70, 0x30, 0xf1, 0xe9, 0x18, 0x87, 0xf7, 0x20, 0x4b, 0x64, 0xa0, 0x94, + 0x58, 0xf4, 0xb8, 0xb2, 0x41, 0x22, 0xb0, 0x0c, 0xad, 0xc9, 0x86, 0x09, 0x36, 0x99, 0x4f, 0x60, + 0xc7, 0xa6, 0x60, 0x9b, 0xf8, 0x54, 0x60, 0x7f, 0x0c, 0xd7, 0x26, 0xa5, 0x9b, 0x68, 0x91, 0x5e, + 0x19, 0x13, 0x8a, 0x64, 0x19, 0xe8, 0x4b, 0x58, 0x32, 0xac, 0xae, 0x6a, 0xe8, 0xee, 0x88, 0x7b, + 0x11, 0xc5, 0x39, 0x55, 0x07, 0x54, 0xa3, 0xb2, 0x33, 0x8d, 0x2f, 0xbc, 0xbf, 0x1b, 0x3b, 0x9c, + 0x03, 0xf3, 0x27, 0x04, 0x24, 0x23, 0x63, 0x02, 0xb6, 0xfc, 0x8f, 0x31, 0x40, 0x93, 0xa8, 0xe8, + 0x04, 0xae, 0x90, 0x9d, 0x19, 0x13, 0x83, 0x6e, 0x6d, 0x76, 0xf3, 0x9b, 0x73, 0x5a, 0x61, 0x98, + 0xaf, 0x70, 0xf3, 0x96, 0xa1, 0x85, 0x07, 0xc8, 0x64, 0x64, 0xab, 0xc6, 0x27, 0x8b, 0xbe, 0x82, + 0xc9, 0x4c, 0x7c, 0x3a, 0x36, 0x99, 0x0e, 0x6f, 0x90, 0xc9, 0x6c, 0xdc, 0xd3, 0x2d, 0x53, 0x35, + 0x94, 0xc3, 0x91, 0x62, 0x5b, 0xa7, 0x81, 0xa2, 0x9d, 0x15, 0x9d, 0xeb, 0xe7, 0x67, 0xab, 0xa5, + 0x16, 0x3e, 0x95, 0x39, 0x5e, 0x75, 0x24, 0x5b, 0xa7, 0x53, 0x2b, 0xf7, 0x92, 0x39, 0x1d, 0x4b, + 0x43, 0x32, 0xbc, 0x7d, 0xc1, 0x54, 0xa1, 0x46, 0x5f, 0x9c, 0xf5, 0xb2, 0xa6, 0xb3, 0xaa, 0xfb, + 0xed, 0xbf, 0x50, 0xde, 0xff, 0xcb, 0x08, 0xd0, 0x44, 0x6c, 0xe8, 0x8a, 0xd6, 0x3e, 0x3d, 0xbb, + 0x8f, 0x20, 0x4f, 0xa6, 0xf5, 0x57, 0x14, 0x99, 0xe1, 0x89, 0x88, 0x3a, 0x7b, 0xc2, 0x7e, 0x04, + 0x79, 0x72, 0xe2, 0x3e, 0x55, 0x74, 0x16, 0x95, 0x65, 0x78, 0x17, 0x09, 0xe8, 0x6d, 0xc8, 0xe9, + 0x26, 0x49, 0xed, 0x79, 0xcb, 0x2b, 0xd8, 0xf2, 0xcd, 0xf2, 0x11, 0x5f, 0xee, 0xf2, 0xaf, 0xa2, + 0x70, 0x7d, 0x57, 0x75, 0xb1, 0xad, 0xab, 0x86, 0xfe, 0x63, 0xac, 0x3d, 0xd2, 0xc9, 0x82, 0x8f, + 0x6c, 0xec, 0x1c, 0xa3, 0x2f, 0x60, 0x71, 0xc2, 0x60, 0xb8, 0xc2, 0xbd, 0x35, 0x5f, 0xd6, 0x21, + 0xca, 0xb3, 0x31, 0x9b, 0x42, 0xbb, 0x61, 0xc3, 0x65, 0xe5, 0xed, 0x8b, 0xf1, 0x0c, 0x5a, 0xf6, + 0x3d, 0x48, 0xa8, 0x8e, 0x62, 0x1d, 0xf1, 0x98, 0xf4, 0x46, 0x80, 0xd1, 0xd0, 0xd5, 0x8d, 0x8d, + 0x63, 0xa3, 0xbb, 0xd1, 0x11, 0x97, 0xac, 0x22, 0x9a, 0xa9, 0x4e, 0xfb, 0x08, 0xbd, 0x0f, 0x45, + 0xe7, 0xd8, 0x1a, 0x1a, 0x9a, 0x72, 0xa8, 0x76, 0x4f, 0x8e, 0x74, 0xc3, 0x08, 0xf5, 0x81, 0x0b, + 0x6c, 0xb0, 0xca, 0xc7, 0xf8, 0x9e, 0xfd, 0x65, 0x0a, 0x90, 0x2f, 0xcf, 0xee, 0xd0, 0x55, 0x69, + 0xbc, 0xaf, 0x40, 0x92, 0x07, 0x1a, 0xb6, 0x47, 0x6f, 0xcf, 0x8c, 0xc9, 0xe1, 0xbe, 0xf7, 0xf6, + 0x82, 0xcc, 0x09, 0xd1, 0xf7, 0x82, 0x77, 0xaa, 0x73, 0xef, 0xc8, 0xf6, 0x82, 0xb8, 0x6c, 0x7d, + 0x08, 0x10, 0x08, 0x52, 0x69, 0xca, 0xe4, 0x9d, 0xb9, 0x53, 0x83, 0xed, 0x05, 0x39, 0x40, 0x8e, + 0xda, 0x50, 0x18, 0x84, 0x3c, 0x18, 0xaf, 0x10, 0x6e, 0xcd, 0xe5, 0xee, 0xb6, 0x17, 0xe4, 0x31, + 0x72, 0xf4, 0x03, 0x40, 0xdd, 0x09, 0xe3, 0x28, 0xc1, 0xd7, 0x48, 0x39, 0x4e, 0xb0, 0xbd, 0x20, + 0x4f, 0x61, 0x83, 0xbe, 0x84, 0xeb, 0xfd, 0xe9, 0x7a, 0xcc, 0x6b, 0x85, 0x59, 0x4d, 0xf1, 0x19, + 0xda, 0xbf, 0xbd, 0x20, 0xcf, 0x62, 0x88, 0x1e, 0x42, 0xc2, 0x71, 0x49, 0x1a, 0x18, 0xa3, 0x29, + 0xf8, 0x9d, 0x19, 0x9c, 0x27, 0x75, 0x64, 0x63, 0x9f, 0x90, 0x89, 0xe4, 0x87, 0xf2, 0x40, 0x8f, + 0x21, 0xe3, 0x55, 0xd2, 0xfc, 0x0a, 0xe6, 0xc3, 0xf9, 0x19, 0x7a, 0xe9, 0xa6, 0x48, 0x46, 0x3d, + 0x5e, 0xa8, 0x02, 0xd9, 0x3e, 0x47, 0xf3, 0x5b, 0x9f, 0x6b, 0xbc, 0xbf, 0x00, 0x82, 0x03, 0xf5, + 0x9d, 0x81, 0x2f, 0x19, 0x04, 0x51, 0x93, 0xa6, 0xd6, 0xb6, 0x65, 0x18, 0xc4, 0x36, 0x68, 0xca, + 0xe3, 0xa5, 0xd6, 0x02, 0x5a, 0xfe, 0x0c, 0x12, 0x74, 0x4d, 0x24, 0xa5, 0x3d, 0x68, 0x3d, 0x6c, + 0xb5, 0x1f, 0xb7, 0x58, 0x8a, 0x52, 0x6f, 0xec, 0x34, 0x3a, 0x0d, 0xa5, 0xdd, 0xda, 0x21, 0x29, + 0xca, 0x6b, 0x70, 0x95, 0x03, 0x2a, 0xad, 0xba, 0xf2, 0x58, 0x6e, 0x8a, 0xa1, 0x68, 0x79, 0x3d, + 0x98, 0x33, 0xa7, 0x21, 0xde, 0x6a, 0xb7, 0x1a, 0xd2, 0x02, 0xcd, 0x9e, 0xeb, 0x75, 0x29, 0x42, + 0xb3, 0x67, 0xb9, 0xbd, 0x27, 0x45, 0x99, 0xf5, 0x55, 0x73, 0x00, 0x9a, 0xb7, 0x0f, 0x0f, 0xe2, + 0xe9, 0xa4, 0x94, 0x2a, 0xff, 0x43, 0x04, 0xd2, 0x24, 0x50, 0x37, 0xcd, 0x23, 0x0b, 0x7d, 0x08, + 0x99, 0x81, 0x6a, 0x63, 0xd3, 0xf5, 0x3d, 0xad, 0x68, 0x42, 0xa7, 0xf7, 0xe8, 0x80, 0xd7, 0x23, + 0x4d, 0x33, 0xc4, 0xa6, 0x86, 0xb6, 0x40, 0xe2, 0x44, 0x4e, 0xf7, 0x18, 0xf7, 0x55, 0x3f, 0xee, + 0xbc, 0xee, 0xb5, 0xf9, 0xe9, 0xf8, 0x3e, 0x1d, 0xf6, 0x38, 0x14, 0x06, 0x41, 0xe8, 0x05, 0x9d, + 0x4a, 0xee, 0x3b, 0xfe, 0xfa, 0x1d, 0x28, 0x8e, 0x05, 0xca, 0x0b, 0x3a, 0x43, 0x6b, 0xb4, 0x33, + 0x14, 0xf3, 0xfd, 0xbe, 0xd7, 0x19, 0x8a, 0xf2, 0xa6, 0xd0, 0x87, 0x7e, 0xdb, 0x87, 0x1c, 0x70, + 0xbc, 0xfa, 0x1a, 0x0f, 0x0f, 0x8b, 0x17, 0x74, 0x7c, 0xf6, 0x60, 0xb1, 0x6f, 0x69, 0xfa, 0x11, + 0x29, 0x5a, 0x88, 0x76, 0xb8, 0x7a, 0x1f, 0xf3, 0x94, 0x76, 0x2e, 0xdf, 0x29, 0x05, 0xa9, 0xc9, + 0x20, 0xda, 0x81, 0x82, 0x46, 0xbc, 0x06, 0xa9, 0x0b, 0x59, 0xbf, 0xe6, 0x2a, 0xf5, 0xe9, 0xab, + 0x33, 0x34, 0x59, 0x1c, 0x16, 0x67, 0x98, 0x17, 0xc4, 0xac, 0xa7, 0x13, 0x3a, 0xc1, 0xf8, 0x9c, + 0x27, 0x78, 0x08, 0xcb, 0x43, 0x13, 0x3f, 0x1f, 0x58, 0x0e, 0xd6, 0x94, 0x89, 0xb3, 0x5c, 0xa7, + 0x5c, 0x6e, 0x71, 0x2e, 0xd7, 0x0f, 0x04, 0xe6, 0xd4, 0x43, 0xbd, 0x3e, 0x9c, 0x3a, 0xac, 0xa1, + 0xfb, 0x90, 0x12, 0xad, 0xdb, 0x34, 0x5d, 0xdf, 0xbc, 0x3e, 0x5e, 0xd4, 0xac, 0x9c, 0x1a, 0x6d, + 0x41, 0xc1, 0xc4, 0xcf, 0x83, 0x37, 0x13, 0x99, 0x90, 0x79, 0xe6, 0x5a, 0xf8, 0xf9, 0xf4, 0x6b, + 0x89, 0x9c, 0xe9, 0x8f, 0x68, 0xa8, 0x0d, 0xe9, 0x23, 0xb5, 0xaf, 0x1b, 0x3a, 0x76, 0x4a, 0xd7, + 0xa8, 0x44, 0xef, 0x5f, 0x28, 0xd1, 0xf8, 0x25, 0x8e, 0xb0, 0x67, 0xc1, 0xc4, 0x13, 0x8c, 0x02, + 0x46, 0x44, 0xb0, 0xeb, 0x93, 0x82, 0x89, 0x4b, 0x9c, 0xd0, 0x85, 0x0e, 0x15, 0x8c, 0x7f, 0x69, + 0xe8, 0x73, 0xc8, 0x87, 0xf3, 0x06, 0xb8, 0x44, 0xde, 0x90, 0x1b, 0x04, 0x93, 0x86, 0x2d, 0x48, + 0x89, 0x84, 0x21, 0x7b, 0x89, 0x84, 0x41, 0x10, 0xa3, 0x2a, 0xc9, 0xc6, 0x9e, 0xbb, 0x7e, 0x79, + 0x92, 0xf3, 0xfb, 0xa5, 0xe7, 0x67, 0xab, 0x59, 0xb2, 0xc2, 0x29, 0x17, 0x23, 0x59, 0xd3, 0x83, + 0x6b, 0xe8, 0x01, 0x80, 0xf7, 0x22, 0xcb, 0xa1, 0xf7, 0x81, 0xb3, 0x7b, 0x46, 0x7b, 0x02, 0xd1, + 0x17, 0x49, 0x0e, 0x50, 0xa3, 0x5d, 0xc8, 0x08, 0x97, 0xcb, 0xfa, 0x83, 0xb3, 0xa3, 0xe1, 0x64, + 0x00, 0x10, 0x6e, 0xdf, 0xe3, 0x40, 0x0a, 0x74, 0x03, 0xab, 0x0e, 0xe6, 0x0d, 0xa7, 0x7b, 0x73, + 0x66, 0xeb, 0x4c, 0xc7, 0x6b, 0xc7, 0xaa, 0xd9, 0xc3, 0x3b, 0x84, 0xbe, 0x1a, 0x2d, 0x45, 0x64, + 0xc6, 0x0a, 0xb5, 0x40, 0xa2, 0x5b, 0x16, 0x8c, 0x27, 0x12, 0xdd, 0xb5, 0x37, 0x85, 0x77, 0x24, + 0xbb, 0x36, 0x33, 0xa6, 0x50, 0x9d, 0xda, 0xf5, 0xe3, 0xca, 0x77, 0xa0, 0x70, 0x64, 0xd9, 0x7d, + 0xd5, 0x55, 0x84, 0xf3, 0x5a, 0xf4, 0xbb, 0xdf, 0x5f, 0x9d, 0xad, 0xe6, 0xb7, 0xe8, 0xa8, 0x70, + 0x5c, 0xf9, 0xa3, 0xe0, 0x27, 0xaa, 0x8a, 0xf0, 0xcb, 0x6e, 0xbb, 0xdf, 0xfa, 0xda, 0xcd, 0x9a, + 0x12, 0x75, 0xdf, 0x85, 0x82, 0x75, 0x74, 0x64, 0xe8, 0x26, 0x56, 0x6c, 0xac, 0x3a, 0x96, 0x59, + 0x7a, 0x2b, 0xe0, 0x7d, 0xf3, 0x7c, 0x4c, 0xa6, 0x43, 0xa8, 0x05, 0x49, 0xda, 0xa8, 0x70, 0x4a, + 0x4b, 0xf4, 0x78, 0x2e, 0xd9, 0xf4, 0x90, 0x39, 0x17, 0x74, 0x13, 0xe0, 0x99, 0x8e, 0x4f, 0x95, + 0xa7, 0x43, 0x6c, 0x8f, 0x4a, 0xa5, 0x60, 0x2f, 0x89, 0xc0, 0x3f, 0x27, 0x60, 0xf4, 0x2d, 0x58, + 0xd2, 0x1d, 0x25, 0x98, 0x82, 0x28, 0x64, 0xb0, 0xf4, 0x4e, 0x20, 0x0e, 0x23, 0xdd, 0x19, 0x4f, + 0x5f, 0xd0, 0x07, 0x90, 0xd1, 0xf0, 0x00, 0x9b, 0x9a, 0xd3, 0x36, 0x4b, 0xaf, 0xd1, 0x92, 0xf8, + 0xca, 0xf9, 0xd9, 0x6a, 0xa6, 0x2e, 0x80, 0xdc, 0xc9, 0xf9, 0x58, 0xe8, 0x33, 0x28, 0x78, 0x1f, + 0x9d, 0xd1, 0x00, 0x3b, 0xa5, 0xf7, 0x29, 0x5d, 0x89, 0x1c, 0x6c, 0x3d, 0x34, 0x22, 0xc2, 0x5e, + 0x18, 0x1f, 0x7d, 0x09, 0x39, 0x06, 0xc1, 0x5a, 0xdb, 0xac, 0x8e, 0x4a, 0xcb, 0x74, 0x9f, 0xee, + 0xce, 0xb9, 0x4f, 0x7e, 0x2f, 0xd5, 0xbb, 0xb7, 0xab, 0x07, 0xb8, 0xc9, 0x21, 0xde, 0xe8, 0x8f, + 0x21, 0x27, 0xf4, 0xf0, 0x81, 0x75, 0xe8, 0x94, 0xbe, 0x71, 0xe1, 0xe5, 0xd8, 0xf8, 0x5c, 0xbb, + 0x3e, 0xa9, 0xf0, 0x32, 0x41, 0x6e, 0xa8, 0x03, 0xa4, 0x7c, 0x14, 0x91, 0xa3, 0x4b, 0xed, 0x41, + 0xf9, 0xd2, 0x3a, 0x24, 0x2a, 0xbf, 0xb1, 0x16, 0x59, 0x8f, 0x79, 0x09, 0xc1, 0x52, 0x0b, 0x9f, + 0x06, 0xad, 0xe6, 0x81, 0x75, 0xd8, 0xac, 0xcb, 0x4b, 0xe6, 0x24, 0x54, 0x43, 0x5f, 0x40, 0x3e, + 0xf8, 0x58, 0xc2, 0x29, 0xbd, 0x7e, 0x61, 0x03, 0x69, 0xc2, 0x38, 0xfd, 0xe7, 0x13, 0x8e, 0x9c, + 0x73, 0x02, 0x5f, 0xe8, 0x06, 0x64, 0x34, 0xdb, 0x1a, 0xb0, 0x18, 0xfe, 0x06, 0x15, 0x50, 0xb4, + 0x3f, 0x6d, 0x6b, 0x40, 0x83, 0xb3, 0x02, 0x05, 0x1b, 0x0f, 0x0c, 0xb5, 0x8b, 0xfb, 0x24, 0x28, + 0x5a, 0x47, 0xa5, 0x15, 0x3a, 0xfb, 0xe6, 0xdc, 0xc7, 0xe3, 0x11, 0x0b, 0xfb, 0x08, 0xf0, 0x6b, + 0x1f, 0xa1, 0x03, 0x00, 0x75, 0xa8, 0xe9, 0xae, 0xd2, 0xb7, 0x34, 0x5c, 0x5a, 0xbd, 0xf0, 0x71, + 0xd5, 0x38, 0xf3, 0x0a, 0x21, 0xdc, 0xb5, 0x34, 0xec, 0xdd, 0x7b, 0x0b, 0x00, 0xfa, 0x00, 0xb2, + 0x74, 0x69, 0x7c, 0xf7, 0xd7, 0xe8, 0xe2, 0x16, 0xf9, 0xee, 0x67, 0xea, 0xb6, 0x35, 0x60, 0x5b, + 0x4e, 0x37, 0x80, 0xed, 0xb3, 0x03, 0xb9, 0x5e, 0x57, 0xf1, 0xdd, 0xe9, 0x0d, 0xaa, 0x1b, 0x9f, + 0xce, 0x29, 0xcb, 0xfd, 0xda, 0x14, 0x07, 0x7b, 0x45, 0xc4, 0x85, 0xfb, 0x35, 0x01, 0x73, 0xe4, + 0x6c, 0xaf, 0xeb, 0x7d, 0x90, 0x92, 0x9b, 0x75, 0xca, 0xb9, 0x41, 0x97, 0x83, 0x25, 0x37, 0x1b, + 0x61, 0x26, 0xdd, 0x02, 0xde, 0x52, 0x57, 0x68, 0xb9, 0xca, 0xce, 0xec, 0xe6, 0xfc, 0x79, 0x57, + 0x81, 0x51, 0x57, 0x9c, 0xf6, 0x11, 0x3d, 0xd8, 0x2e, 0xe4, 0xac, 0xa1, 0x7b, 0x68, 0x0d, 0x4d, + 0x4d, 0x39, 0x3a, 0x71, 0x4a, 0x6f, 0xd2, 0xd5, 0xbe, 0x50, 0xe3, 0xd4, 0x5b, 0x5d, 0x9b, 0x33, + 0xda, 0x7a, 0xe8, 0xc8, 0x59, 0xc1, 0x75, 0xeb, 0xc4, 0x41, 0x3f, 0x82, 0xac, 0x6e, 0xfa, 0x73, + 0xdc, 0x7a, 0xf1, 0x39, 0x90, 0xa8, 0x39, 0x9a, 0xa6, 0x37, 0x05, 0x70, 0x9e, 0x64, 0x86, 0x9f, + 0x44, 0x60, 0xed, 0x6b, 0x1a, 0xae, 0x4e, 0xe9, 0xdd, 0x0b, 0xef, 0xac, 0xe7, 0xe8, 0xb8, 0xbe, + 0x71, 0x51, 0xc7, 0xd5, 0x41, 0x65, 0xc8, 0xb8, 0xb8, 0x3f, 0xb0, 0x6c, 0xd5, 0x1e, 0x95, 0xde, + 0x0e, 0x3e, 0x43, 0xf0, 0xc0, 0xe8, 0x87, 0x50, 0x1c, 0x6f, 0x89, 0xdd, 0x7e, 0x89, 0x96, 0x98, + 0x5c, 0x08, 0xb7, 0xff, 0xd0, 0x06, 0x2d, 0x42, 0xd8, 0x4d, 0x8f, 0xa2, 0x1a, 0x86, 0x72, 0x38, + 0x2a, 0xbd, 0x17, 0x6c, 0x47, 0x78, 0xa3, 0x15, 0xc3, 0xa8, 0x8e, 0x96, 0x7f, 0x11, 0x81, 0xc5, + 0x89, 0xb8, 0x8d, 0x7e, 0x08, 0x29, 0xd3, 0xd2, 0x02, 0x0f, 0x44, 0x1a, 0x7c, 0xff, 0x93, 0x2d, + 0x4b, 0x63, 0xef, 0x43, 0x3e, 0xec, 0xe9, 0xee, 0xf1, 0xf0, 0x70, 0xa3, 0x6b, 0xf5, 0xef, 0x78, + 0x92, 0x6b, 0x87, 0xfe, 0xdf, 0x77, 0x06, 0x27, 0xbd, 0x3b, 0xf4, 0xaf, 0xc1, 0xe1, 0x06, 0x23, + 0x93, 0x93, 0x84, 0x6b, 0x53, 0x43, 0xef, 0x43, 0x11, 0x3f, 0x1f, 0xe8, 0x76, 0xa0, 0x76, 0x88, + 0x06, 0xfc, 0x4e, 0xc1, 0x1f, 0x24, 0x4a, 0xca, 0xaf, 0xe2, 0x7f, 0x15, 0x85, 0xe2, 0x58, 0x38, + 0x24, 0x75, 0x0f, 0x6d, 0x51, 0x85, 0xea, 0x1e, 0x02, 0xb9, 0xe0, 0xbd, 0x47, 0xf0, 0xbd, 0x62, + 0xec, 0x65, 0xdf, 0x2b, 0x86, 0x1f, 0x17, 0x25, 0x5e, 0xe0, 0x71, 0xd1, 0xc7, 0x70, 0x4d, 0x77, + 0x14, 0xd3, 0x32, 0xc5, 0x05, 0x83, 0xd7, 0x74, 0x09, 0xbe, 0xee, 0xbb, 0xa2, 0x3b, 0x2d, 0xcb, + 0x64, 0x57, 0x0b, 0xde, 0xaa, 0xfd, 0x87, 0x80, 0xa9, 0xc9, 0x87, 0x80, 0x5e, 0x8f, 0x3e, 0x2e, + 0x25, 0x96, 0xff, 0x25, 0x02, 0x99, 0xe0, 0x8b, 0xfc, 0x68, 0xb8, 0x73, 0x38, 0x51, 0x0b, 0x5e, + 0xf2, 0xa1, 0x4f, 0x78, 0x17, 0x62, 0x2f, 0xb0, 0x0b, 0x37, 0x20, 0x71, 0x38, 0x12, 0x35, 0x5a, + 0xba, 0x9a, 0xe3, 0xb3, 0xc5, 0xab, 0xa4, 0x1e, 0x88, 0x1f, 0x8e, 0xc4, 0xa3, 0xa9, 0xe5, 0x3f, + 0x85, 0x6c, 0x20, 0xee, 0x8e, 0x77, 0x26, 0x22, 0x97, 0xe8, 0x4c, 0xbc, 0x09, 0x49, 0x1e, 0x16, + 0x98, 0xee, 0xe5, 0x39, 0x75, 0x82, 0x85, 0x84, 0xc4, 0x97, 0x24, 0x1c, 0xf0, 0xd9, 0xff, 0x3b, + 0x06, 0xb9, 0x60, 0x04, 0x25, 0xb6, 0xae, 0x9b, 0x5d, 0x9b, 0x86, 0x2f, 0x3a, 0x7b, 0xcc, 0x7b, + 0x72, 0x24, 0xc0, 0x24, 0xae, 0xf6, 0x75, 0x53, 0xa1, 0xcf, 0x55, 0x42, 0xfa, 0x9d, 0xee, 0xeb, + 0xe6, 0x23, 0x02, 0xa5, 0x28, 0xea, 0x73, 0x8e, 0x12, 0x0b, 0xa1, 0xa8, 0xcf, 0x19, 0xca, 0x32, + 0x4d, 0x55, 0x6d, 0x97, 0xee, 0x50, 0x2c, 0x90, 0x82, 0xda, 0x6e, 0xf0, 0xe5, 0x61, 0x62, 0xda, + 0xcb, 0x43, 0x13, 0x0a, 0x7e, 0xce, 0x70, 0x6a, 0x62, 0x9b, 0x5f, 0x37, 0x54, 0x2e, 0x91, 0x34, + 0xf8, 0x1f, 0x84, 0x91, 0x88, 0xe2, 0x4e, 0x10, 0x48, 0xb2, 0xd2, 0xae, 0xda, 0x3d, 0xc6, 0x8a, + 0xa3, 0xff, 0x98, 0xb5, 0x03, 0xbc, 0x6d, 0xa1, 0xf0, 0x7d, 0xfd, 0xc7, 0x78, 0xf9, 0xef, 0x22, + 0x90, 0x0f, 0xf1, 0x42, 0x4d, 0x28, 0x52, 0xe9, 0x26, 0xda, 0xdb, 0x37, 0xbc, 0x37, 0xfa, 0x64, + 0x78, 0x6a, 0x31, 0x9b, 0xb7, 0x02, 0x43, 0x1a, 0xc9, 0x43, 0x19, 0x2b, 0xef, 0x85, 0x5b, 0x58, + 0x8d, 0x73, 0x94, 0x53, 0xf8, 0x99, 0x5b, 0xce, 0xf2, 0x61, 0x5a, 0xb0, 0x19, 0xbf, 0x6c, 0x42, + 0x36, 0x90, 0xb9, 0xcc, 0x61, 0x3f, 0xdf, 0x86, 0xb8, 0xe7, 0xcd, 0xe6, 0xed, 0x22, 0xbb, 0xbe, + 0x8b, 0xfb, 0x59, 0x04, 0x96, 0xa6, 0x65, 0x10, 0x21, 0xbb, 0x64, 0xda, 0x36, 0x97, 0x5d, 0xde, + 0x0c, 0x66, 0x76, 0x4c, 0x03, 0xc5, 0x53, 0x03, 0x3f, 0xb7, 0x7b, 0xcb, 0xb3, 0x03, 0xa6, 0x80, + 0xc5, 0x90, 0x1d, 0x90, 0x0a, 0x2e, 0x68, 0x09, 0xff, 0x1e, 0x83, 0xc2, 0xd8, 0xed, 0xcb, 0x23, + 0x48, 0xf6, 0x0c, 0xeb, 0x50, 0x35, 0x78, 0xd7, 0xfa, 0x3b, 0x97, 0x0a, 0x65, 0x1b, 0xf7, 0x29, + 0x8f, 0xed, 0x05, 0x99, 0x73, 0x43, 0x0e, 0x2c, 0x06, 0xaf, 0x59, 0xd8, 0x8f, 0x89, 0xd8, 0xce, + 0x36, 0x2e, 0x37, 0x85, 0x7f, 0x0f, 0x43, 0x11, 0xb7, 0x17, 0xe4, 0xa2, 0x1d, 0x06, 0xa1, 0x3e, + 0x14, 0xc7, 0xee, 0x76, 0xf8, 0x95, 0x40, 0xed, 0x65, 0xa7, 0x94, 0xad, 0xd3, 0x6d, 0x9a, 0xf7, + 0x06, 0x00, 0xcb, 0x7f, 0x04, 0xc5, 0x31, 0xa1, 0xc8, 0x79, 0x30, 0x1c, 0x1e, 0xd5, 0x0a, 0xc4, + 0x87, 0x31, 0xa4, 0x96, 0xda, 0xc7, 0x32, 0x1f, 0xe5, 0xe7, 0x71, 0x0b, 0xf2, 0xa1, 0x29, 0x50, + 0x01, 0xa2, 0x2a, 0x7b, 0x46, 0x98, 0x91, 0xa3, 0x2a, 0x7f, 0x80, 0xb8, 0x5c, 0x80, 0x24, 0xdb, + 0xdf, 0xa0, 0x7e, 0x57, 0x01, 0xd2, 0x22, 0x7f, 0x28, 0xaf, 0x43, 0xc6, 0x4b, 0xa4, 0x51, 0x0e, + 0xd2, 0xf5, 0xe6, 0x7e, 0xa5, 0xba, 0xd3, 0xa8, 0x4b, 0x0b, 0x28, 0x0f, 0x19, 0xb9, 0x51, 0xa9, + 0xd3, 0x9e, 0xab, 0x14, 0xf9, 0x24, 0xfd, 0xe7, 0x3f, 0x5b, 0x8d, 0xf0, 0x20, 0x93, 0x94, 0x52, + 0x0f, 0xe2, 0x69, 0x24, 0x5d, 0x29, 0xff, 0x6f, 0x06, 0x50, 0x5d, 0x75, 0x55, 0xb2, 0x29, 0x2f, + 0xd0, 0x99, 0x8c, 0x5e, 0x60, 0x4d, 0x53, 0x9b, 0x8c, 0xf1, 0x97, 0x69, 0x32, 0x5e, 0xaa, 0xd7, + 0x39, 0xd9, 0x99, 0x4c, 0xbe, 0x44, 0x67, 0x32, 0xdc, 0xf7, 0x89, 0xbd, 0x54, 0xdf, 0xe7, 0x11, + 0xa4, 0x58, 0x95, 0xc9, 0xde, 0x99, 0xcd, 0x6e, 0x2b, 0x4c, 0x1e, 0x0c, 0xef, 0xd6, 0x38, 0x0d, + 0xd3, 0xb5, 0x47, 0xde, 0x7b, 0x18, 0x06, 0xf3, 0xdb, 0x23, 0xe9, 0x57, 0xd9, 0x1e, 0xc9, 0xcc, + 0x6e, 0x8f, 0xfc, 0x00, 0xb8, 0x5d, 0x88, 0xa4, 0x18, 0x2e, 0x7c, 0x1a, 0x32, 0x65, 0x39, 0xcc, + 0x08, 0x78, 0x56, 0x9c, 0xb3, 0x03, 0x5f, 0xe8, 0x47, 0x80, 0xc4, 0xbd, 0x6c, 0x60, 0xe7, 0xd9, + 0x95, 0xce, 0x07, 0x33, 0x97, 0x46, 0x09, 0xa6, 0x1d, 0x80, 0x78, 0x13, 0xee, 0x8d, 0x39, 0xcb, + 0x1d, 0x00, 0xde, 0xe0, 0x35, 0x8f, 0xac, 0x39, 0xc2, 0xc4, 0x0a, 0xa4, 0x88, 0xfb, 0x1d, 0x60, + 0xa6, 0xff, 0x5e, 0xdc, 0xe6, 0x40, 0x6e, 0xb3, 0x03, 0xc8, 0x05, 0x0f, 0x09, 0x49, 0x10, 0x3b, + 0xc1, 0x23, 0x6e, 0xda, 0xe4, 0x4f, 0xf4, 0x00, 0x12, 0x7e, 0x76, 0x31, 0xfb, 0xb1, 0xf8, 0xcc, + 0xd3, 0x27, 0xe2, 0xca, 0x8c, 0xc5, 0x27, 0xd1, 0x7b, 0x34, 0xc5, 0xce, 0x05, 0x37, 0x12, 0xb5, + 0x20, 0xef, 0x0c, 0xed, 0x67, 0xfa, 0x33, 0xd5, 0x50, 0x7a, 0x96, 0x6a, 0xd0, 0x89, 0x0a, 0x9b, + 0x37, 0x67, 0x3d, 0xb4, 0xe2, 0xb8, 0xf7, 0x2d, 0xd5, 0x10, 0xad, 0x11, 0x27, 0x00, 0x43, 0x1f, + 0x7b, 0x17, 0x82, 0xfc, 0x06, 0x9d, 0x5f, 0x2e, 0x23, 0x6e, 0x86, 0x41, 0x3f, 0x27, 0xba, 0xbf, + 0x0c, 0x44, 0x22, 0x3b, 0x57, 0x11, 0x4c, 0x1f, 0x42, 0x8b, 0xb6, 0xbe, 0x17, 0xd9, 0x19, 0x5e, + 0xc3, 0x1c, 0xf6, 0xfd, 0xc8, 0x6e, 0xfb, 0x30, 0x0d, 0x6d, 0x43, 0xc6, 0x8b, 0xe5, 0xd4, 0xfc, + 0x0b, 0x9b, 0x6f, 0x5e, 0xb0, 0x63, 0x7b, 0x63, 0x1d, 0x0b, 0x9f, 0xd8, 0x4b, 0xb1, 0x23, 0x52, + 0xd4, 0xf7, 0x86, 0xe5, 0xff, 0xc9, 0x41, 0xa1, 0x33, 0x1a, 0x4c, 0xf3, 0x7e, 0xb1, 0x19, 0xde, + 0x2f, 0x3e, 0xdf, 0xbd, 0x4c, 0xe6, 0xe5, 0xee, 0x65, 0xe0, 0xd5, 0xde, 0xcb, 0x64, 0x5f, 0x99, + 0xf7, 0x2b, 0xbc, 0x94, 0xf7, 0x7b, 0x65, 0xb7, 0x74, 0xd1, 0x4b, 0xdc, 0xd2, 0x7d, 0x17, 0xf2, + 0xaa, 0x6d, 0xab, 0x23, 0xfe, 0x5b, 0x1c, 0x8d, 0xba, 0xca, 0x3c, 0x3b, 0xa3, 0xf3, 0xb3, 0xd5, + 0x6c, 0x85, 0x0c, 0xd2, 0x9f, 0xdf, 0x08, 0x0e, 0x59, 0xd5, 0x03, 0x69, 0xbe, 0x87, 0xcd, 0xbf, + 0x4a, 0x0f, 0x5b, 0x9c, 0xed, 0x61, 0xeb, 0x10, 0xa7, 0x3f, 0xf6, 0x61, 0x7a, 0x3f, 0x6b, 0xcb, + 0xc3, 0xea, 0xbb, 0x11, 0xf8, 0xbd, 0x0f, 0xa5, 0x46, 0x3f, 0x82, 0x65, 0xf1, 0xa2, 0x96, 0xe8, + 0x83, 0x7f, 0x8b, 0x1a, 0xf8, 0x29, 0x55, 0xf9, 0xfc, 0x6c, 0xb5, 0x24, 0xfb, 0x58, 0x3e, 0x3f, + 0x56, 0x07, 0x92, 0xbd, 0x28, 0xd9, 0x53, 0xc7, 0x35, 0x07, 0x3d, 0x81, 0x1c, 0xb5, 0xef, 0x3e, + 0xee, 0x1f, 0x62, 0x5b, 0x84, 0xda, 0xbb, 0xf3, 0xc9, 0x4b, 0x0c, 0x7d, 0x97, 0x12, 0x8a, 0xde, + 0x19, 0xf6, 0x20, 0x0e, 0xba, 0x0b, 0x09, 0xd5, 0xd0, 0x69, 0xac, 0xfc, 0xba, 0xdf, 0xf6, 0x31, + 0x44, 0xf6, 0x12, 0x39, 0x18, 0x96, 0xa4, 0x8b, 0xbb, 0x9e, 0x61, 0x69, 0x66, 0x87, 0xa4, 0xe5, + 0x9f, 0xc6, 0x00, 0x7c, 0x61, 0xd1, 0xb7, 0xe1, 0xfa, 0xe0, 0x78, 0xe4, 0xe8, 0x5d, 0xd5, 0x50, + 0x6c, 0x3c, 0xb0, 0xb1, 0x83, 0x4d, 0x96, 0xf9, 0x53, 0xbd, 0xce, 0xc9, 0xd7, 0xc4, 0xb0, 0x1c, + 0x1a, 0x45, 0x9f, 0xc2, 0x35, 0xc3, 0xea, 0x4d, 0xa3, 0x0b, 0xf6, 0x3d, 0xae, 0x72, 0x9c, 0x31, + 0x62, 0x95, 0x54, 0x6b, 0x03, 0xf5, 0x50, 0x37, 0xfc, 0x56, 0xc8, 0xa7, 0x2f, 0xba, 0xd1, 0x1b, + 0x35, 0x8f, 0x85, 0x78, 0x56, 0xe3, 0x33, 0x45, 0x3f, 0x9c, 0x7c, 0x99, 0xf0, 0xc9, 0x0b, 0xcf, + 0x30, 0xfb, 0x81, 0x42, 0xf9, 0x4d, 0x00, 0x7f, 0x7e, 0x7a, 0xe1, 0xbf, 0xb3, 0xe3, 0x27, 0xac, + 0xfc, 0xe9, 0x40, 0xf9, 0xf6, 0xd7, 0xbc, 0x0f, 0x00, 0x48, 0xca, 0x8d, 0xdd, 0xf6, 0xa3, 0x86, + 0x78, 0x21, 0xb0, 0xdc, 0x1e, 0x8b, 0x83, 0x93, 0x71, 0x2b, 0x32, 0x67, 0xdc, 0xe2, 0x97, 0xf6, + 0x9f, 0x43, 0x9c, 0x18, 0x13, 0x99, 0xbd, 0xd1, 0x3a, 0xd8, 0x95, 0x16, 0x50, 0x06, 0x12, 0x95, + 0x9d, 0x66, 0x65, 0x5f, 0x8a, 0xa0, 0x25, 0x90, 0x76, 0x0f, 0x76, 0x3a, 0x4d, 0xb9, 0x71, 0xbf, + 0xd9, 0x6e, 0x29, 0x14, 0x21, 0x8a, 0x56, 0x60, 0xb9, 0x43, 0x32, 0x70, 0xa5, 0xb9, 0xbb, 0xb7, + 0xd3, 0xac, 0x35, 0x3b, 0x8a, 0xdc, 0xa8, 0xb5, 0xe5, 0xba, 0xd2, 0x79, 0xb2, 0xd7, 0x90, 0x62, + 0x81, 0xc0, 0xf3, 0xb7, 0x71, 0x90, 0x98, 0x63, 0x9a, 0x12, 0x7a, 0xa2, 0x97, 0x78, 0x12, 0xf0, + 0x07, 0xcf, 0xff, 0xa6, 0x86, 0xad, 0xc4, 0x2b, 0xca, 0xf4, 0x93, 0x2f, 0x91, 0xe9, 0xa7, 0x5e, + 0xd5, 0x1b, 0x84, 0x79, 0xe3, 0x53, 0x38, 0x40, 0xc6, 0x5f, 0x26, 0x40, 0x06, 0x34, 0xe4, 0xe7, + 0x51, 0x80, 0x80, 0x6e, 0x7c, 0x2f, 0xf8, 0x4f, 0x73, 0xcc, 0xbe, 0x05, 0x1f, 0x2b, 0x6d, 0xb7, + 0x17, 0xc4, 0x3f, 0xdc, 0x71, 0x1f, 0xd2, 0x1a, 0xcf, 0x29, 0x79, 0xea, 0xf9, 0xce, 0xdc, 0xa9, + 0xe7, 0xf6, 0x82, 0xec, 0x11, 0xa3, 0x4f, 0x43, 0xbf, 0xb6, 0xbe, 0x35, 0x97, 0x6b, 0xd8, 0x16, + 0x3f, 0x3f, 0xa8, 0x40, 0x92, 0xc5, 0x70, 0xbe, 0x4d, 0x33, 0x7f, 0xc1, 0x3a, 0x66, 0x1a, 0xdb, + 0x0b, 0x32, 0x27, 0xe4, 0x65, 0x70, 0x0a, 0x12, 0x43, 0x53, 0xb7, 0xcc, 0xdb, 0x72, 0xf0, 0xc9, + 0xbc, 0xe8, 0xf9, 0x12, 0x6f, 0x42, 0xff, 0x56, 0x5d, 0xac, 0xb1, 0x97, 0x49, 0x07, 0xe6, 0x33, + 0x0f, 0x10, 0x41, 0x05, 0x00, 0x3e, 0xae, 0x9b, 0x3d, 0x29, 0x4a, 0x8b, 0x67, 0x92, 0xc8, 0x93, + 0xaf, 0xd8, 0xed, 0xef, 0x82, 0x34, 0xfe, 0x13, 0xda, 0x80, 0x0f, 0x5a, 0x84, 0xfc, 0xee, 0xa3, + 0x5a, 0xad, 0xd3, 0xdc, 0x6d, 0xec, 0x77, 0x2a, 0xbb, 0x7b, 0xec, 0x2d, 0x36, 0xb5, 0xfb, 0x76, + 0xb3, 0x2e, 0x45, 0x6f, 0x1f, 0xc2, 0xf5, 0x19, 0xbf, 0xb9, 0x46, 0xd7, 0xe1, 0x4a, 0xab, 0xdd, + 0x51, 0x9a, 0xf5, 0x46, 0xab, 0xd3, 0xec, 0x3c, 0x51, 0x6a, 0xed, 0x9d, 0x83, 0xdd, 0x96, 0xb4, + 0x40, 0xfc, 0xc9, 0xfd, 0x46, 0xab, 0x21, 0x57, 0x3a, 0x8d, 0xba, 0x52, 0xd9, 0x79, 0x5c, 0x79, + 0x42, 0xbc, 0x4c, 0x09, 0x96, 0x7c, 0x68, 0xf5, 0x89, 0xf7, 0xef, 0x70, 0x44, 0x6f, 0x7f, 0x17, + 0x8a, 0x63, 0xa6, 0x4c, 0x5c, 0xe2, 0xde, 0x41, 0x75, 0xa7, 0x59, 0x9b, 0xfa, 0x8e, 0x0a, 0x65, + 0x21, 0xd5, 0xde, 0xda, 0xda, 0x69, 0xb6, 0x1a, 0x52, 0xec, 0xf6, 0x47, 0x90, 0x0b, 0x26, 0xfe, + 0x48, 0x82, 0xdc, 0xf7, 0xdb, 0xad, 0x86, 0xb2, 0x55, 0x69, 0xee, 0x1c, 0xc8, 0x64, 0x95, 0x08, + 0x0a, 0xdc, 0xb7, 0x09, 0x58, 0xe4, 0xf6, 0x7b, 0x90, 0x0f, 0x65, 0xd9, 0x84, 0xa7, 0x10, 0x69, + 0x81, 0xec, 0xa9, 0xf8, 0x77, 0x44, 0x1a, 0x75, 0x29, 0x52, 0x5d, 0xff, 0xcd, 0x7f, 0xae, 0x2c, + 0xfc, 0xe6, 0x7c, 0x25, 0xf2, 0xdb, 0xf3, 0x95, 0xc8, 0xef, 0xce, 0x57, 0x22, 0xff, 0x71, 0xbe, + 0x12, 0xf9, 0xab, 0xdf, 0xaf, 0x2c, 0xfc, 0xf6, 0xf7, 0x2b, 0x0b, 0xbf, 0xfb, 0xfd, 0xca, 0xc2, + 0xf7, 0x93, 0xec, 0x9f, 0xb9, 0xf9, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x32, 0xee, 0xe4, 0x7b, + 0x51, 0x47, 0x00, 0x00, } func (this *ForeignKeyReference) Equal(that interface{}) bool { diff --git a/pkg/sql/catalog/descpb/structured.proto b/pkg/sql/catalog/descpb/structured.proto index b3ea3d449cfa..1d366df7c550 100644 --- a/pkg/sql/catalog/descpb/structured.proto +++ b/pkg/sql/catalog/descpb/structured.proto @@ -513,7 +513,7 @@ message IndexDescriptor { // Interleave, if it's not the zero value, describes how this index's data is // interleaved into another index's data. - optional InterleaveDescriptor interleave = 11 [(gogoproto.nullable) = false]; + optional InterleaveDescriptor interleave = 11 [(gogoproto.nullable) = false, deprecated = true]; // InterleavedBy contains a reference to every table/index that is interleaved // into this one. @@ -526,7 +526,7 @@ message IndexDescriptor { // despite the message used here, interleavings don't have to have // corresponding foreign key references (and whether they do or not is // irrelevant for this field). - repeated ForeignKeyReference interleaved_by = 12 [(gogoproto.nullable) = false]; + repeated ForeignKeyReference interleaved_by = 12 [(gogoproto.nullable) = false, deprecated = true]; // Partitioning, if it's not the zero value, describes how this index's data // is partitioned into spans of keys each addressable by zone configs. @@ -1157,9 +1157,6 @@ message TableDescriptor { // can be reused. This list is separate because mutations can // lie in this list for a long time (gc deadline) and should not block // the execution of other schema changes on the table. - // - // TODO(vivekmenezes): This is currently only used by the non-interleaved drop - // index case. Also use for dropped interleaved indexes and columns. repeated GCDescriptorMutation gc_mutations = 33 [(gogoproto.nullable) = false, (gogoproto.customname) = "GCMutations"]; diff --git a/pkg/sql/catalog/descriptor.go b/pkg/sql/catalog/descriptor.go index 84e62a206a91..bf9ea0e4f57d 100644 --- a/pkg/sql/catalog/descriptor.go +++ b/pkg/sql/catalog/descriptor.go @@ -263,9 +263,6 @@ type TableDescriptor interface { // Sequences count as physical tables because their values are stored in // the KV layer. IsPhysicalTable() bool - // IsInterleaved returns true if any part of this this table is interleaved with - // another table's data. - IsInterleaved() bool // MaterializedView returns whether or not this TableDescriptor is a // MaterializedView. MaterializedView() bool diff --git a/pkg/sql/catalog/table_elements.go b/pkg/sql/catalog/table_elements.go index 7e4bdea464a0..03b46ea106e6 100644 --- a/pkg/sql/catalog/table_elements.go +++ b/pkg/sql/catalog/table_elements.go @@ -119,7 +119,6 @@ type Index interface { GetID() descpb.IndexID GetName() string - IsInterleaved() bool IsPartial() bool IsUnique() bool IsDisabled() bool @@ -141,12 +140,6 @@ type Index interface { ExplicitColumnStartIdx() int - NumInterleaveAncestors() int - GetInterleaveAncestor(ancestorOrdinal int) descpb.InterleaveDescriptor_Ancestor - - NumInterleavedBy() int - GetInterleavedBy(interleavedByOrdinal int) descpb.ForeignKeyReference - NumKeyColumns() int GetKeyColumnID(columnOrdinal int) descpb.ColumnID GetKeyColumnName(columnOrdinal int) string diff --git a/pkg/sql/catalog/tabledesc/index.go b/pkg/sql/catalog/tabledesc/index.go index 40082b8dae66..72ce0818760e 100644 --- a/pkg/sql/catalog/tabledesc/index.go +++ b/pkg/sql/catalog/tabledesc/index.go @@ -72,11 +72,6 @@ func (w index) GetName() string { return w.desc.Name } -// IsInterleaved returns true iff the index is interleaved. -func (w index) IsInterleaved() bool { - return w.desc.IsInterleaved() -} - // IsPartial returns true iff the index is a partial index. func (w index) IsPartial() bool { return w.desc.IsPartial() @@ -234,29 +229,6 @@ func (w index) GetEncodingType() descpb.IndexDescriptorEncodingType { return w.desc.EncodingType } -// NumInterleaveAncestors returns the number of interleave ancestors as per the -// index descriptor. -func (w index) NumInterleaveAncestors() int { - return len(w.desc.Interleave.Ancestors) -} - -// GetInterleaveAncestor returns the ancestorOrdinal-th interleave ancestor. -func (w index) GetInterleaveAncestor(ancestorOrdinal int) descpb.InterleaveDescriptor_Ancestor { - return w.desc.Interleave.Ancestors[ancestorOrdinal] -} - -// NumInterleavedBy returns the number of tables/indexes that are interleaved -// into this index. -func (w index) NumInterleavedBy() int { - return len(w.desc.InterleavedBy) -} - -// GetInterleavedBy returns the interleavedByOrdinal-th table/index that is -// interleaved into this index. -func (w index) GetInterleavedBy(interleavedByOrdinal int) descpb.ForeignKeyReference { - return w.desc.InterleavedBy[interleavedByOrdinal] -} - // NumKeyColumns returns the number of columns in the index key. func (w index) NumKeyColumns() int { return len(w.desc.KeyColumnIDs) diff --git a/pkg/sql/catalog/tabledesc/index_test.go b/pkg/sql/catalog/tabledesc/index_test.go index c36d0c4917b6..c42226885584 100644 --- a/pkg/sql/catalog/tabledesc/index_test.go +++ b/pkg/sql/catalog/tabledesc/index_test.go @@ -233,8 +233,6 @@ func TestIndexInterface(t *testing.T) { // Check index methods on features not tested here. for _, idx := range indexes { - require.False(t, idx.IsInterleaved(), - errMsgFmt, "IsInterleaved", idx.GetName()) require.False(t, idx.IsDisabled(), errMsgFmt, "IsDisabled", idx.GetName()) require.False(t, idx.IsCreatedExplicitly(), @@ -246,10 +244,6 @@ func TestIndexInterface(t *testing.T) { require.Equal(t, descpb.IndexDescriptorVersion(0x3), idx.GetVersion(), errMsgFmt, "GetVersion", idx.GetName()) } - require.Equal(t, 0, idx.NumInterleaveAncestors(), - errMsgFmt, "NumInterleaveAncestors", idx.GetName()) - require.Equal(t, 0, idx.NumInterleavedBy(), - errMsgFmt, "NumInterleavedBy", idx.GetName()) require.False(t, idx.HasOldStoredColumns(), errMsgFmt, "HasOldStoredColumns", idx.GetName()) require.Equalf(t, 0, idx.NumCompositeColumns(), diff --git a/pkg/sql/catalog/tabledesc/safe_format.go b/pkg/sql/catalog/tabledesc/safe_format.go index af01d73e57d4..9482ac0f110b 100644 --- a/pkg/sql/catalog/tabledesc/safe_format.go +++ b/pkg/sql/catalog/tabledesc/safe_format.go @@ -129,28 +129,6 @@ func formatSafeIndex( if idx.Predicate != "" { w.Printf(", Partial: true") } - if !idx.Interleave.Equal(&descpb.InterleaveDescriptor{}) { - w.Printf(", InterleaveParents: [") - for i := range idx.Interleave.Ancestors { - a := &idx.Interleave.Ancestors[i] - if i > 0 { - w.Printf(", ") - } - w.Printf("{TableID: %d, IndexID: %d}", a.TableID, a.IndexID) - } - w.Printf("]") - } - if len(idx.InterleavedBy) > 0 { - w.Printf(", InterleaveChildren: [") - for i := range idx.InterleavedBy { - a := &idx.InterleavedBy[i] - if i > 0 { - w.Printf(", ") - } - w.Printf("{TableID: %d, IndexID: %d}", a.Table, a.Index) - } - w.Printf("]") - } w.Printf(", KeyColumns: [") for i := range idx.KeyColumnIDs { if i > 0 { diff --git a/pkg/sql/catalog/tabledesc/structured.go b/pkg/sql/catalog/tabledesc/structured.go index a66c9cc4c031..a6551ee33460 100644 --- a/pkg/sql/catalog/tabledesc/structured.go +++ b/pkg/sql/catalog/tabledesc/structured.go @@ -1537,13 +1537,6 @@ func (desc *wrapper) FindFKByName(name string) (*descpb.ForeignKeyConstraint, er return nil, fmt.Errorf("fk %q does not exist", name) } -// IsInterleaved implements the TableDescriptor interface. -func (desc *wrapper) IsInterleaved() bool { - return nil != catalog.FindNonDropIndex(desc, func(idx catalog.Index) bool { - return idx.IsInterleaved() - }) -} - // IsPrimaryIndexDefaultRowID returns whether or not the table's primary // index is the default primary key on the hidden rowid column. func (desc *wrapper) IsPrimaryIndexDefaultRowID() bool { @@ -2153,8 +2146,6 @@ func (desc *wrapper) IndexSpan(codec keys.SQLCodec, indexID descpb.IndexID) roac // TableSpan implements the TableDescriptor interface. func (desc *wrapper) TableSpan(codec keys.SQLCodec) roachpb.Span { - // TODO(jordan): Why does IndexSpan consider interleaves but TableSpan does - // not? Should it? prefix := codec.TablePrefix(uint32(desc.ID)) return roachpb.Span{Key: prefix, EndKey: prefix.PrefixEnd()} } @@ -2268,14 +2259,6 @@ func (desc *wrapper) FindAllReferences() (map[descpb.ID]struct{}, error) { fk := &desc.InboundFKs[i] refs[fk.OriginTableID] = struct{}{} } - for _, index := range desc.NonDropIndexes() { - for i := 0; i < index.NumInterleaveAncestors(); i++ { - refs[index.GetInterleaveAncestor(i).TableID] = struct{}{} - } - for i := 0; i < index.NumInterleavedBy(); i++ { - refs[index.GetInterleavedBy(i).Table] = struct{}{} - } - } for _, c := range desc.NonDropColumns() { for i := 0; i < c.NumUsesSequences(); i++ { diff --git a/pkg/sql/catalog/tabledesc/validate.go b/pkg/sql/catalog/tabledesc/validate.go index 05cdd2665ebc..81000dc0ed93 100644 --- a/pkg/sql/catalog/tabledesc/validate.go +++ b/pkg/sql/catalog/tabledesc/validate.go @@ -51,21 +51,13 @@ func (desc *wrapper) GetReferencedDescIDs() (catalog.DescriptorIDSet, error) { if desc.GetParentSchemaID() != keys.PublicSchemaID { ids.Add(desc.GetParentSchemaID()) } - // Collect referenced table IDs in foreign keys and interleaves. + // Collect referenced table IDs in foreign keys. for _, fk := range desc.OutboundFKs { ids.Add(fk.ReferencedTableID) } for _, fk := range desc.InboundFKs { ids.Add(fk.OriginTableID) } - for _, idx := range desc.NonDropIndexes() { - for i := 0; i < idx.NumInterleaveAncestors(); i++ { - ids.Add(idx.GetInterleaveAncestor(i).TableID) - } - for i := 0; i < idx.NumInterleavedBy(); i++ { - ids.Add(idx.GetInterleavedBy(i).Table) - } - } // Collect user defined type Oids and sequence references in columns. for _, col := range desc.DeletableColumns() { children, err := typedesc.GetTypeDescriptorClosure(col.GetType()) @@ -177,82 +169,6 @@ func (desc *wrapper) ValidateCrossReferences( } } } - - // Check interleaves. - for _, indexI := range desc.NonDropIndexes() { - vea.Report(desc.validateIndexInterleave(indexI, vdg)) - } - // TODO(dan): Also validate SharedPrefixLen in the interleaves. -} - -func (desc *wrapper) validateIndexInterleave( - indexI catalog.Index, vdg catalog.ValidationDescGetter, -) error { - // Check interleaves. - if indexI.NumInterleaveAncestors() > 0 { - // Only check the most recent ancestor, the rest of them don't point - // back. - ancestor := indexI.GetInterleaveAncestor(indexI.NumInterleaveAncestors() - 1) - targetTable, err := vdg.GetTableDescriptor(ancestor.TableID) - if err != nil { - return errors.Wrapf(err, - "invalid interleave: missing table=%d index=%d", ancestor.TableID, errors.Safe(ancestor.IndexID)) - } - targetIndex, err := targetTable.FindIndexWithID(ancestor.IndexID) - if err != nil { - return errors.Wrapf(err, - "invalid interleave: missing table=%s index=%d", targetTable.GetName(), errors.Safe(ancestor.IndexID)) - } - - found := false - for j := 0; j < targetIndex.NumInterleavedBy(); j++ { - backref := targetIndex.GetInterleavedBy(j) - if backref.Table == desc.ID && backref.Index == indexI.GetID() { - found = true - break - } - } - if !found { - return errors.AssertionFailedf( - "missing interleave back reference to %q@%q from %q@%q", - desc.Name, indexI.GetName(), targetTable.GetName(), targetIndex.GetName()) - } - } - - interleaveBackrefs := make(map[descpb.ForeignKeyReference]struct{}) - for j := 0; j < indexI.NumInterleavedBy(); j++ { - backref := indexI.GetInterleavedBy(j) - if _, ok := interleaveBackrefs[backref]; ok { - return errors.AssertionFailedf("duplicated interleave backreference %+v", backref) - } - interleaveBackrefs[backref] = struct{}{} - targetTable, err := vdg.GetTableDescriptor(backref.Table) - if err != nil { - return errors.Wrapf(err, - "invalid interleave backreference table=%d index=%d", - backref.Table, backref.Index) - } - targetIndex, err := targetTable.FindIndexWithID(backref.Index) - if err != nil { - return errors.Wrapf(err, - "invalid interleave backreference table=%s index=%d", - targetTable.GetName(), backref.Index) - } - if targetIndex.NumInterleaveAncestors() == 0 { - return errors.AssertionFailedf( - "broken interleave backward reference from %q@%q to %q@%q", - desc.Name, indexI.GetName(), targetTable.GetName(), targetIndex.GetName()) - } - // The last ancestor is required to be a backreference. - ancestor := targetIndex.GetInterleaveAncestor(targetIndex.NumInterleaveAncestors() - 1) - if ancestor.TableID != desc.ID || ancestor.IndexID != indexI.GetID() { - return errors.AssertionFailedf( - "broken interleave backward reference from %q@%q to %q@%q", - desc.Name, indexI.GetName(), targetTable.GetName(), targetIndex.GetName()) - } - } - - return nil } func (desc *wrapper) validateOutboundFK( @@ -855,6 +771,10 @@ func (desc *wrapper) validateTableIndexes(columnNames map[string]descpb.ColumnID return errors.AssertionFailedf("index %q contains deprecated foreign key representation", idx.GetName()) } + if len(idx.IndexDesc().Interleave.Ancestors) > 0 || len(idx.IndexDesc().InterleavedBy) > 0 { + return errors.Newf("index is interleaved") + } + if _, indexNameExists := indexNames[idx.GetName()]; indexNameExists { for i := range desc.Indexes { if desc.Indexes[i].Name == idx.GetName() { @@ -1054,16 +974,6 @@ func (desc *wrapper) validatePartitioningDescriptor( return nil } - // TODO(dan): The sqlccl.GenerateSubzoneSpans logic is easier if we disallow - // setting zone configs on indexes that are interleaved into another index. - // InterleavedBy is fine, so using the root of the interleave hierarchy will - // work. It is expected that this is sufficient for real-world use cases. - // Revisit this restriction if that expectation is wrong. - if idx.NumInterleaveAncestors() > 0 { - return errors.Errorf("cannot set a zone config for interleaved index %s; "+ - "set it on the root of the interleaved hierarchy instead", idx.GetName()) - } - // We don't need real prefixes in the DecodePartitionTuple calls because we're // only using it to look for collisions and the prefix would be the same for // all of them. Faking them out with DNull allows us to make O(list partition) diff --git a/pkg/sql/catalog/tabledesc/validate_test.go b/pkg/sql/catalog/tabledesc/validate_test.go index 007ee20d38bb..a23d7a42955b 100644 --- a/pkg/sql/catalog/tabledesc/validate_test.go +++ b/pkg/sql/catalog/tabledesc/validate_test.go @@ -1422,129 +1422,7 @@ func TestValidateCrossTableReferences(t *testing.T) { FormatVersion: descpb.InterleavedFormatVersion, }}, }, - // Interleaves - { // 6 - err: `invalid interleave: missing table=52 index=2: referenced table ID 52: descriptor not found`, - desc: descpb.TableDescriptor{ - Name: "foo", - ID: 51, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - FormatVersion: descpb.InterleavedFormatVersion, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 1, - Interleave: descpb.InterleaveDescriptor{Ancestors: []descpb.InterleaveDescriptor_Ancestor{ - {TableID: 52, IndexID: 2}, - }}, - }, - }, - otherDescs: nil, - }, - { // 7 - err: `invalid interleave: missing table=baz index=2: index-id "2" does not exist`, - desc: descpb.TableDescriptor{ - Name: "foo", - ID: 51, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - FormatVersion: descpb.InterleavedFormatVersion, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 1, - Interleave: descpb.InterleaveDescriptor{Ancestors: []descpb.InterleaveDescriptor_Ancestor{ - {TableID: 52, IndexID: 2}, - }}, - }, - }, - otherDescs: []descpb.TableDescriptor{{ - ID: 52, - Name: "baz", - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - }}, - }, - { // 8 - err: `missing interleave back reference to "foo"@"bar" from "baz"@"qux"`, - desc: descpb.TableDescriptor{ - Name: "foo", - ID: 51, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 1, - Name: "bar", - Interleave: descpb.InterleaveDescriptor{Ancestors: []descpb.InterleaveDescriptor_Ancestor{ - {TableID: 52, IndexID: 2}, - }}, - }, - }, - otherDescs: []descpb.TableDescriptor{{ - ID: 52, - Name: "baz", - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 2, - Name: "qux", - }, - }}, - }, - { // 9 - err: `invalid interleave backreference table=52 index=2: referenced table ID 52: descriptor not found`, - desc: descpb.TableDescriptor{ - Name: "foo", - ID: 51, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 1, - InterleavedBy: []descpb.ForeignKeyReference{{Table: 52, Index: 2}}, - }, - }, - }, - { // 10 - err: `invalid interleave backreference table=baz index=2: index-id "2" does not exist`, - desc: descpb.TableDescriptor{ - Name: "foo", - ID: 51, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 1, - InterleavedBy: []descpb.ForeignKeyReference{{Table: 52, Index: 2}}, - }, - }, - otherDescs: []descpb.TableDescriptor{{ - ID: 52, - Name: "baz", - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - }}, - }, - { // 11 - err: `broken interleave backward reference from "foo"@"bar" to "baz"@"qux"`, - desc: descpb.TableDescriptor{ - Name: "foo", - ID: 51, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 1, - Name: "bar", - InterleavedBy: []descpb.ForeignKeyReference{{Table: 52, Index: 2}}, - }, - }, - otherDescs: []descpb.TableDescriptor{{ - Name: "baz", - ID: 52, - ParentID: 1, - UnexposedParentSchemaID: keys.PublicSchemaID, - PrimaryIndex: descpb.IndexDescriptor{ - ID: 2, - Name: "qux", - }, - }}, - }, - { // 12 + { // 4 err: `referenced type ID 500: descriptor not found`, desc: descpb.TableDescriptor{ Name: "foo", @@ -1567,7 +1445,7 @@ func TestValidateCrossTableReferences(t *testing.T) { }, }, // Add some expressions with invalid type references. - { // 13 + { // 5 err: `referenced type ID 500: descriptor not found`, desc: descpb.TableDescriptor{ Name: "foo", @@ -1590,7 +1468,7 @@ func TestValidateCrossTableReferences(t *testing.T) { }, }, }, - { // 14 + { // 6 err: `referenced type ID 500: descriptor not found`, desc: descpb.TableDescriptor{ Name: "foo", @@ -1613,7 +1491,7 @@ func TestValidateCrossTableReferences(t *testing.T) { }, }, }, - { // 15 + { // 7 err: `referenced type ID 500: descriptor not found`, desc: descpb.TableDescriptor{ Name: "foo", @@ -1627,7 +1505,7 @@ func TestValidateCrossTableReferences(t *testing.T) { }, }, }, - { // 16 + { // 8 err: `referenced type ID 500: descriptor not found`, desc: descpb.TableDescriptor{ Name: "foo", @@ -1651,7 +1529,7 @@ func TestValidateCrossTableReferences(t *testing.T) { }, }, // Temporary tables. - { // 17 + { // 9 err: "", desc: descpb.TableDescriptor{ Name: "foo", diff --git a/pkg/sql/colfetcher/cfetcher.go b/pkg/sql/colfetcher/cfetcher.go index 4daf8c2cf475..faf179f000cb 100644 --- a/pkg/sql/colfetcher/cfetcher.go +++ b/pkg/sql/colfetcher/cfetcher.go @@ -1382,15 +1382,13 @@ func (rf *cFetcher) KeyToDesc(key roachpb.Key) (catalog.TableDescriptor, bool) { } nIndexCols := rf.table.index.NumKeyColumns() + rf.table.index.NumKeySuffixColumns() tableKeyVals := make([]rowenc.EncDatum, nIndexCols) - _, ok, _, err := rowenc.DecodeIndexKeyWithoutTableIDIndexIDPrefix( - rf.table.desc, - rf.table.index, + _, _, err := rowenc.DecodeKeyVals( rf.table.keyValTypes, tableKeyVals, rf.table.indexColumnDirs, key[rf.table.knownPrefixLength:], ) - if !ok || err != nil { + if err != nil { return nil, false } return rf.table.desc, true diff --git a/pkg/sql/crdb_internal.go b/pkg/sql/crdb_internal.go index 758ff5030bab..6f330120db36 100644 --- a/pkg/sql/crdb_internal.go +++ b/pkg/sql/crdb_internal.go @@ -42,7 +42,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catconstants" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/catformat" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catprivilege" "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" @@ -151,7 +150,6 @@ var crdbInternal = virtualSchema{ catconstants.CrdbInternalZonesTableID: crdbInternalZonesTable, catconstants.CrdbInternalInvalidDescriptorsTableID: crdbInternalInvalidDescriptorsTable, catconstants.CrdbInternalClusterDatabasePrivilegesTableID: crdbInternalClusterDatabasePrivilegesTable, - catconstants.CrdbInternalInterleaved: crdbInternalInterleaved, catconstants.CrdbInternalCrossDbRefrences: crdbInternalCrossDbReferences, catconstants.CrdbInternalLostTableDescriptors: crdbLostTableDescriptors, catconstants.CrdbInternalClusterInflightTracesTable: crdbInternalClusterInflightTracesTable, @@ -2384,8 +2382,7 @@ CREATE TABLE crdb_internal.create_statements ( if err != nil { return err } - if err := showAlterStatementWithInterleave(ctx, &name, contextName, lookup, table.PublicNonPrimaryIndexes(), table, alterStmts, - validateStmts, &p.semaCtx); err != nil { + if err := showAlterStatement(ctx, &name, contextName, lookup, table, alterStmts, validateStmts); err != nil { return err } displayOptions.FKDisplayMode = IncludeFkClausesInCreate @@ -2422,18 +2419,16 @@ CREATE TABLE crdb_internal.create_statements ( ) }) -func showAlterStatementWithInterleave( +func showAlterStatement( ctx context.Context, tn *tree.TableName, contextName string, lCtx simpleSchemaResolver, - allIdx []catalog.Index, table catalog.TableDescriptor, alterStmts *tree.DArray, validateStmts *tree.DArray, - semaCtx *tree.SemaContext, ) error { - if err := table.ForeachOutboundFK(func(fk *descpb.ForeignKeyConstraint) error { + return table.ForeachOutboundFK(func(fk *descpb.ForeignKeyConstraint) error { f := tree.NewFmtCtx(tree.FmtSimple) f.WriteString("ALTER TABLE ") f.FormatNode(tn) @@ -2464,87 +2459,7 @@ func showAlterStatementWithInterleave( f.FormatNameP(&fk.Name) return validateStmts.Append(tree.NewDString(f.CloseAndGetString())) - }); err != nil { - return err - } - - for _, idx := range allIdx { - // Create CREATE INDEX commands for INTERLEAVE tables. These commands - // are included in the ALTER TABLE statements. - if idx.NumInterleaveAncestors() > 0 { - f := tree.NewFmtCtx(tree.FmtSimple) - parentTableID := idx.GetInterleaveAncestor(idx.NumInterleaveAncestors() - 1).TableID - var err error - var parentName tree.TableName - if lCtx != nil { - parentName, err = getParentAsTableName(lCtx, parentTableID, contextName) - if err != nil { - return err - } - } else { - parentName = tree.MakeTableNameWithSchema(tree.Name(""), tree.PublicSchemaName, tree.Name(fmt.Sprintf("[%d as parent]", parentTableID))) - parentName.ExplicitCatalog = false - parentName.ExplicitSchema = false - } - - var tableName tree.TableName - if lCtx != nil { - tableName, err = getTableNameFromTableDescriptor(lCtx, table, contextName) - if err != nil { - return err - } - } else { - tableName = tree.MakeTableNameWithSchema(tree.Name(""), tree.PublicSchemaName, tree.Name(fmt.Sprintf("[%d as parent]", table.GetID()))) - tableName.ExplicitCatalog = false - tableName.ExplicitSchema = false - } - var sharedPrefixLen int - for i := 0; i < idx.NumInterleaveAncestors(); i++ { - sharedPrefixLen += int(idx.GetInterleaveAncestor(i).SharedPrefixLen) - } - // Write the CREATE INDEX statements. - if err := showCreateIndexWithInterleave(ctx, f, table, idx, tableName, parentName, sharedPrefixLen, semaCtx); err != nil { - return err - } - if err := alterStmts.Append(tree.NewDString(f.CloseAndGetString())); err != nil { - return err - } - } - } - return nil -} - -func showCreateIndexWithInterleave( - ctx context.Context, - f *tree.FmtCtx, - table catalog.TableDescriptor, - idx catalog.Index, - tableName tree.TableName, - parentName tree.TableName, - sharedPrefixLen int, - semaCtx *tree.SemaContext, -) error { - f.WriteString("CREATE ") - idxStr, err := catformat.IndexForDisplay( - ctx, table, &tableName, idx, "" /* partition */, "" /* interleave */, semaCtx, - ) - if err != nil { - return err - } - f.WriteString(idxStr) - f.WriteString(" INTERLEAVE IN PARENT ") - parentName.Format(f) - f.WriteString(" (") - // Get all of the columns and write them. - comma := "" - for i := 0; i < sharedPrefixLen; i++ { - name := idx.GetKeyColumnName(i) - f.WriteString(comma) - f.FormatNameP(&name) - comma = ", " - } - f.WriteString(")") - return nil + }) } // crdbInternalTableColumnsTable exposes the column descriptors. @@ -2800,7 +2715,6 @@ CREATE TABLE crdb_internal.backward_dependencies ( fkDep := tree.NewDString("fk") viewDep := tree.NewDString("view") sequenceDep := tree.NewDString("sequence") - interleaveDep := tree.NewDString("interleave") return forEachTableDescAllWithTableLookup(ctx, p, dbContext, hideVirtual, func( db catalog.DatabaseDescriptor, _ string, table catalog.TableDescriptor, tableLookup tableLookupFn, @@ -2808,25 +2722,6 @@ CREATE TABLE crdb_internal.backward_dependencies ( tableID := tree.NewDInt(tree.DInt(table.GetID())) tableName := tree.NewDString(table.GetName()) - reportIdxDeps := func(idx catalog.Index) error { - for i := 0; i < idx.NumInterleaveAncestors(); i++ { - interleaveParent := idx.GetInterleaveAncestor(i) - if err := addRow( - tableID, tableName, - tree.NewDInt(tree.DInt(idx.GetID())), - tree.DNull, - tree.NewDInt(tree.DInt(interleaveParent.TableID)), - interleaveDep, - tree.NewDInt(tree.DInt(interleaveParent.IndexID)), - tree.DNull, - tree.NewDString(fmt.Sprintf("SharedPrefixLen: %d", - interleaveParent.SharedPrefixLen)), - ); err != nil { - return err - } - } - return nil - } if err := table.ForeachOutboundFK(func(fk *descpb.ForeignKeyConstraint) error { refTbl, err := tableLookup.getTableByID(fk.ReferencedTableID) if err != nil { @@ -2854,11 +2749,6 @@ CREATE TABLE crdb_internal.backward_dependencies ( return err } - // Record the backward references of the primary index. - if err := catalog.ForEachIndex(table, catalog.IndexOpts{}, reportIdxDeps); err != nil { - return err - } - // Record the view dependencies. for _, tIdx := range table.GetDependsOn() { if err := addRow( @@ -2959,31 +2849,12 @@ CREATE TABLE crdb_internal.forward_dependencies ( populate: func(ctx context.Context, p *planner, dbContext catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error { fkDep := tree.NewDString("fk") viewDep := tree.NewDString("view") - interleaveDep := tree.NewDString("interleave") sequenceDep := tree.NewDString("sequence") return forEachTableDescAll(ctx, p, dbContext, hideVirtual, /* virtual tables have no backward/forward dependencies*/ func(db catalog.DatabaseDescriptor, _ string, table catalog.TableDescriptor) error { tableID := tree.NewDInt(tree.DInt(table.GetID())) tableName := tree.NewDString(table.GetName()) - reportIdxDeps := func(idx catalog.Index) error { - for i := 0; i < idx.NumInterleavedBy(); i++ { - interleaveRef := idx.GetInterleavedBy(i) - if err := addRow( - tableID, tableName, - tree.NewDInt(tree.DInt(idx.GetID())), - tree.NewDInt(tree.DInt(interleaveRef.Table)), - interleaveDep, - tree.NewDInt(tree.DInt(interleaveRef.Index)), - tree.DNull, - tree.NewDString(fmt.Sprintf("SharedPrefixLen: %d", - interleaveRef.SharedPrefixLen)), - ); err != nil { - return err - } - } - return nil - } if err := table.ForeachInboundFK(func(fk *descpb.ForeignKeyConstraint) error { return addRow( tableID, tableName, @@ -2998,10 +2869,6 @@ CREATE TABLE crdb_internal.forward_dependencies ( return err } - // Record the backward references of the primary index. - if err := catalog.ForEachIndex(table, catalog.IndexOpts{}, reportIdxDeps); err != nil { - return err - } reportDependedOnBy := func( dep *descpb.TableDescriptor_Reference, depTypeString *tree.DString, ) error { @@ -4619,67 +4486,6 @@ CREATE TABLE crdb_internal.cluster_database_privileges ( }, } -var crdbInternalInterleaved = virtualSchemaTable{ - comment: `virtual table with interleaved table information`, - schema: ` -CREATE TABLE crdb_internal.interleaved ( - database_name - STRING NOT NULL, - schema_name - STRING NOT NULL, - table_name - STRING NOT NULL, - index_name - STRING NOT NULL, - parent_database_name - STRING NOT NULL, - parent_schema_name - STRING NOT NULL, - parent_table_name - STRING NOT NULL -);`, - populate: func(ctx context.Context, p *planner, dbContext catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error { - return forEachTableDescAllWithTableLookup(ctx, p, dbContext, hideVirtual, - func(db catalog.DatabaseDescriptor, schemaName string, table catalog.TableDescriptor, lookupFn tableLookupFn) error { - if !table.IsInterleaved() { - return nil - } - // We want to include dropped indexes for cases where - // a revert may end up adding them back. - indexes := table.AllIndexes() - for _, index := range indexes { - if index.NumInterleaveAncestors() == 0 { - continue - } - ancestor := index.GetInterleaveAncestor(index.NumInterleaveAncestors() - 1) - parentTable, err := lookupFn.getTableByID(ancestor.TableID) - if err != nil { - return err - } - parentSchemaName, err := lookupFn.getSchemaNameByID(parentTable.GetParentSchemaID()) - if err != nil { - return err - } - parentDatabase, err := lookupFn.getDatabaseByID(parentTable.GetParentID()) - if err != nil { - return err - } - - if err := addRow(tree.NewDString(db.GetName()), - tree.NewDString(schemaName), - tree.NewDString(table.GetName()), - tree.NewDString(index.GetName()), - tree.NewDString(parentDatabase.GetName()), - tree.NewDString(parentSchemaName), - tree.NewDString(parentTable.GetName())); err != nil { - return err - } - } - return nil - }) - }, -} - var crdbInternalCrossDbReferences = virtualSchemaTable{ comment: `virtual table with cross db references`, schema: ` diff --git a/pkg/sql/create_index.go b/pkg/sql/create_index.go index 57bc03d2812f..4902d2fb40e4 100644 --- a/pkg/sql/create_index.go +++ b/pkg/sql/create_index.go @@ -13,7 +13,6 @@ package sql import ( "context" - "github.com/cockroachdb/cockroach/pkg/build" "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/docs" "github.com/cockroachdb/cockroach/pkg/geo/geoindex" @@ -601,35 +600,10 @@ func maybeCreateAndAddShardCol( return shardCol, created, err } -var interleavedTableDeprecationError = errors.WithIssueLink( - pgnotice.Newf("interleaved tables and interleaved indexes are deprecated in 20.2 and will be removed in 21.2"), - errors.IssueLink{IssueURL: build.MakeIssueURL(52009)}, -) - var interleavedTableDisabledMigrationError = pgnotice.Newf( "creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored." + " For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations") -// interleavedTableDeprecationAction either returns an error, if interleaved -// tables are disabled, or sends a notice, if they're not. Returns any error -// and if the interleaved option should be ignored. -func interleavedTableDeprecationAction(params runParams) (ignoreInterleave bool, err error) { - // Once interleave tables should be prevented, we should - // return a client notice and ignore opeartions. - if params.ExecCfg().Settings.Version.IsActive(params.ctx, clusterversion.PreventNewInterleavedTables) { - params.p.BufferClientNotice( - params.ctx, - interleavedTableDisabledMigrationError, - ) - return true, nil - } - params.p.BufferClientNotice( - params.ctx, - interleavedTableDeprecationError, - ) - return false, nil -} - func (n *createIndexNode) startExec(params runParams) error { telemetry.Inc(sqltelemetry.SchemaChangeCreateCounter("index")) foundIndex, err := n.tableDesc.FindIndexWithName(string(n.n.Name)) @@ -670,14 +644,8 @@ func (n *createIndexNode) startExec(params runParams) error { if n.n.PartitionByIndex != nil { return pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot be partitioned") } - - interleaveIgnored, err := interleavedTableDeprecationAction(params) - if err != nil { - return err - } - if interleaveIgnored { - n.n.Interleave = nil - } + params.p.BufferClientNotice(params.ctx, interleavedTableDisabledMigrationError) + n.n.Interleave = nil } indexDesc, err := MakeIndexDescriptor(params, *n.n, n.tableDesc) @@ -733,15 +701,6 @@ func (n *createIndexNode) startExec(params runParams) error { index := n.tableDesc.Mutations[mutationIdx].GetIndex() indexName := index.Name - if n.n.Interleave != nil { - if err := params.p.addInterleave(params.ctx, n.tableDesc, index, n.n.Interleave); err != nil { - return err - } - if err := params.p.finalizeInterleave(params.ctx, n.tableDesc, index); err != nil { - return err - } - } - mutationID := n.tableDesc.ClusterVersion.NextMutationID if err := params.p.writeSchemaChange( params.ctx, n.tableDesc, mutationID, tree.AsStringWithFQNames(n.n, params.Ann()), diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index 90d8f741551e..b2c7bde382c5 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -292,13 +292,8 @@ func (n *createTableNode) startExec(params runParams) error { } if n.n.Interleave != nil { telemetry.Inc(sqltelemetry.CreateInterleavedTableCounter) - interleaveIgnored, err := interleavedTableDeprecationAction(params) - if err != nil { - return err - } - if interleaveIgnored { - n.n.Interleave = nil - } + params.p.BufferClientNotice(params.ctx, interleavedTableDisabledMigrationError) + n.n.Interleave = nil } if n.n.Persistence.IsTemporary() { telemetry.Inc(sqltelemetry.CreateTempTableCounter) @@ -429,14 +424,6 @@ func (n *createTableNode) startExec(params runParams) error { } } - for _, index := range desc.NonDropIndexes() { - if index.NumInterleaveAncestors() > 0 { - if err := params.p.finalizeInterleave(params.ctx, desc, index.IndexDesc()); err != nil { - return err - } - } - } - // Install back references to types used by this table. if err := params.p.addBackRefsFromAllTypesInTable(params.ctx, desc); err != nil { return err @@ -1048,168 +1035,6 @@ func ResolveFK( return nil } -func (p *planner) addInterleave( - ctx context.Context, - desc *tabledesc.Mutable, - index *descpb.IndexDescriptor, - interleave *tree.InterleaveDef, -) error { - return addInterleave(ctx, p.txn, p, desc, index, interleave) -} - -// addInterleave marks an index as one that is interleaved in some parent data -// according to the given definition. -func addInterleave( - ctx context.Context, - txn *kv.Txn, - vt resolver.SchemaResolver, - desc *tabledesc.Mutable, - index *descpb.IndexDescriptor, - interleave *tree.InterleaveDef, -) error { - if interleave.DropBehavior != tree.DropDefault { - return unimplemented.NewWithIssuef( - 7854, "unsupported shorthand %s", interleave.DropBehavior) - } - - if desc.IsLocalityRegionalByRow() { - return interleaveOnRegionalByRowError() - } - - _, parentTable, err := resolver.ResolveExistingTableObject( - ctx, vt, &interleave.Parent, tree.ObjectLookupFlagsWithRequiredTableKind(tree.ResolveRequireTableDesc), - ) - if err != nil { - return err - } - parentIndex := parentTable.GetPrimaryIndex() - - // typeOfIndex is used to give more informative error messages. - var typeOfIndex string - if index.ID == desc.GetPrimaryIndexID() { - typeOfIndex = "primary key" - } else { - typeOfIndex = "index" - } - - if len(interleave.Fields) != parentIndex.NumKeyColumns() { - return pgerror.Newf( - pgcode.InvalidSchemaDefinition, - "declared interleaved columns (%s) must match the parent's primary index (%s)", - &interleave.Fields, - strings.Join(parentIndex.IndexDesc().KeyColumnNames, ", "), - ) - } - if len(interleave.Fields) > len(index.KeyColumnIDs) { - return pgerror.Newf( - pgcode.InvalidSchemaDefinition, - "declared interleaved columns (%s) must be a prefix of the %s columns being interleaved (%s)", - &interleave.Fields, - typeOfIndex, - strings.Join(index.KeyColumnNames, ", "), - ) - } - - for i := 0; i < parentIndex.NumKeyColumns(); i++ { - targetColID := parentIndex.GetKeyColumnID(i) - targetCol, err := parentTable.FindColumnWithID(targetColID) - if err != nil { - return err - } - col, err := desc.FindColumnWithID(index.KeyColumnIDs[i]) - if err != nil { - return err - } - if string(interleave.Fields[i]) != col.GetName() { - return pgerror.Newf( - pgcode.InvalidSchemaDefinition, - "declared interleaved columns (%s) must refer to a prefix of the %s column names being interleaved (%s)", - &interleave.Fields, - typeOfIndex, - strings.Join(index.KeyColumnNames, ", "), - ) - } - if !col.GetType().Identical(targetCol.GetType()) || index.KeyColumnDirections[i] != parentIndex.GetKeyColumnDirection(i) { - return pgerror.Newf( - pgcode.InvalidSchemaDefinition, - "declared interleaved columns (%s) must match type and sort direction of the parent's primary index (%s)", - &interleave.Fields, - strings.Join(parentIndex.IndexDesc().KeyColumnNames, ", "), - ) - } - } - - ancestorPrefix := make([]descpb.InterleaveDescriptor_Ancestor, parentIndex.NumInterleaveAncestors()) - for i := range ancestorPrefix { - ancestorPrefix[i] = parentIndex.GetInterleaveAncestor(i) - } - - intl := descpb.InterleaveDescriptor_Ancestor{ - TableID: parentTable.GetID(), - IndexID: parentIndex.GetID(), - SharedPrefixLen: uint32(parentIndex.NumKeyColumns()), - } - for _, ancestor := range ancestorPrefix { - intl.SharedPrefixLen -= ancestor.SharedPrefixLen - } - index.Interleave = descpb.InterleaveDescriptor{Ancestors: append(ancestorPrefix, intl)} - - desc.State = descpb.DescriptorState_ADD - return nil -} - -// finalizeInterleave creates backreferences from an interleaving parent to the -// child data being interleaved. -func (p *planner) finalizeInterleave( - ctx context.Context, desc *tabledesc.Mutable, index *descpb.IndexDescriptor, -) error { - // TODO(dan): This is similar to finalizeFKs. Consolidate them - if len(index.Interleave.Ancestors) == 0 { - return nil - } - // Only the last ancestor needs the backreference. - ancestor := index.Interleave.Ancestors[len(index.Interleave.Ancestors)-1] - var ancestorTable *tabledesc.Mutable - if ancestor.TableID == desc.ID { - ancestorTable = desc - } else { - var err error - ancestorTable, err = p.Descriptors().GetMutableTableVersionByID(ctx, ancestor.TableID, p.txn) - if err != nil { - return err - } - } - ancestorIndex, err := ancestorTable.FindIndexWithID(ancestor.IndexID) - if err != nil { - return err - } - ancestorIndex.IndexDesc().InterleavedBy = append(ancestorIndex.IndexDesc().InterleavedBy, - descpb.ForeignKeyReference{Table: desc.ID, Index: index.ID}) - - if err := p.writeSchemaChange( - ctx, ancestorTable, descpb.InvalidMutationID, - fmt.Sprintf( - "updating ancestor table %s(%d) for table %s(%d)", - ancestorTable.Name, ancestorTable.ID, desc.Name, desc.ID, - ), - ); err != nil { - return err - } - - if desc.State == descpb.DescriptorState_ADD { - desc.State = descpb.DescriptorState_PUBLIC - - // No job description, since this is presumably part of some larger schema change. - if err := p.writeSchemaChange( - ctx, desc, descpb.InvalidMutationID, "", - ); err != nil { - return err - } - } - - return nil -} - // CreatePartitioning returns a set of implicit columns and a new partitioning // descriptor to build an index with partitioning fields populated to align with // the tree.PartitionBy clause. @@ -1504,11 +1329,6 @@ func NewTableDesc( regionalByRowCol = n.Locality.RegionalByRowColumn } - // Check no interleaving is on the table. - if n.Interleave != nil { - return nil, interleaveOnRegionalByRowError() - } - // Check PARTITION BY is not set on anything partitionable, and also check // for the existence of the column to partition by. regionalByRowColExists := false @@ -2146,12 +1966,6 @@ func NewTableDesc( } } - if n.Interleave != nil { - if err := addInterleave(ctx, txn, vt, &desc, desc.GetPrimaryIndex().IndexDesc(), n.Interleave); err != nil { - return nil, err - } - } - if n.PartitionByTable.ContainsPartitions() || desc.PartitionAllBy { partitionBy := partitionAllBy if partitionBy == nil { @@ -2453,9 +2267,8 @@ func newTableDesc( regionConfig = &conf } - // We need to run NewTableDesc with caching disabled, because - // it needs to pull in descriptors from FK depended-on tables - // and interleaved parents using their current state in KV. + // We need to run NewTableDesc with caching disabled, because it needs to pull + // in descriptors from FK depended-on tables using their current state in KV. // See the comment at the start of NewTableDesc() and ResolveFK(). params.p.runWithOptions(resolveFlags{skipCache: true, contextDatabaseID: db.GetID()}, func() { ret, err = NewTableDesc( @@ -2933,10 +2746,6 @@ func hashShardedIndexesOnRegionalByRowError() error { return pgerror.New(pgcode.FeatureNotSupported, "hash sharded indexes are not compatible with REGIONAL BY ROW tables") } -func interleaveOnRegionalByRowError() error { - return pgerror.New(pgcode.FeatureNotSupported, "interleaved tables are not compatible with REGIONAL BY ROW tables") -} - func checkTypeIsSupported(ctx context.Context, settings *cluster.Settings, typ *types.T) error { version := settings.Version.ActiveVersionOrEmpty(ctx) if supported := types.IsTypeSupportedInVersion(version, typ); !supported { diff --git a/pkg/sql/doctor/doctor_test.go b/pkg/sql/doctor/doctor_test.go index 273949602da9..4100d9769757 100644 --- a/pkg/sql/doctor/doctor_test.go +++ b/pkg/sql/doctor/doctor_test.go @@ -453,10 +453,6 @@ func TestExamineDescriptors(t *testing.T) { desc := protoutil.Clone(validTableDesc).(*descpb.Descriptor) tbl, _, _, _ := descpb.FromDescriptor(desc) tbl.PrimaryIndex.Disabled = true - tbl.PrimaryIndex.InterleavedBy = make([]descpb.ForeignKeyReference, 1) - tbl.PrimaryIndex.InterleavedBy[0].Name = "bad_backref" - tbl.PrimaryIndex.InterleavedBy[0].Table = 500 - tbl.PrimaryIndex.InterleavedBy[0].Index = 1 return desc }())}, { @@ -471,7 +467,6 @@ func TestExamineDescriptors(t *testing.T) { {NameInfo: descpb.NameInfo{Name: "db"}, ID: 52}, }, expected: `Examining 2 descriptors and 2 namespace entries... - ParentID 52, ParentSchemaID 29: relation "t" (51): invalid interleave backreference table=500 index=1: referenced table ID 500: descriptor not found ParentID 52, ParentSchemaID 29: relation "t" (51): unimplemented: primary key dropped without subsequent addition of new primary key in same transaction `, }, diff --git a/pkg/sql/drop_index.go b/pkg/sql/drop_index.go index 512045b572ab..7bc64a0c351a 100644 --- a/pkg/sql/drop_index.go +++ b/pkg/sql/drop_index.go @@ -418,17 +418,6 @@ func (p *planner) dropIndexByName( return err } - if idx.NumInterleaveAncestors() > 0 { - if err := p.removeInterleaveBackReference(ctx, tableDesc, idx); err != nil { - return err - } - } - for i := 0; i < idx.NumInterleavedBy(); i++ { - if err := p.removeInterleave(ctx, idx.GetInterleavedBy(i)); err != nil { - return err - } - } - var droppedViews []string for _, tableRef := range tableDesc.DependedOnBy { if tableRef.IndexID == idx.GetID() { diff --git a/pkg/sql/drop_table.go b/pkg/sql/drop_table.go index ed4b49d2f00e..11a1a6cd68a3 100644 --- a/pkg/sql/drop_table.go +++ b/pkg/sql/drop_table.go @@ -31,7 +31,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" - "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/log/eventpb" "github.com/cockroachdb/cockroach/pkg/util/timeutil" @@ -87,16 +86,6 @@ func (p *planner) DropTable(ctx context.Context, n *tree.DropTable) (planNode, e } } } - for _, idx := range droppedDesc.NonDropIndexes() { - for i := 0; i < idx.NumInterleavedBy(); i++ { - ref := idx.GetInterleavedBy(i) - if _, ok := td[ref.Table]; !ok { - if err := p.canRemoveInterleave(ctx, droppedDesc.Name, ref, n.DropBehavior); err != nil { - return nil, err - } - } - } - } for _, ref := range droppedDesc.DependedOnBy { if _, ok := td[ref.ID]; !ok { if err := p.canRemoveDependentView(ctx, droppedDesc, ref, n.DropBehavior); err != nil { @@ -231,44 +220,6 @@ func (p *planner) canRemoveFKBackreference( return p.CheckPrivilege(ctx, table, privilege.CREATE) } -func (p *planner) canRemoveInterleave( - ctx context.Context, from string, ref descpb.ForeignKeyReference, behavior tree.DropBehavior, -) error { - table, err := p.Descriptors().GetMutableTableVersionByID(ctx, ref.Table, p.txn) - if err != nil { - return err - } - // TODO(dan): It's possible to DROP a table that has a child interleave, but - // some loose ends would have to be addressed. The zone would have to be - // kept and deleted when the last table in it is removed. Also, the dropped - // table's descriptor would have to be kept around in some Dropped but - // non-public state for referential integrity of the `InterleaveDescriptor` - // pointers. - if behavior != tree.DropCascade { - return unimplemented.NewWithIssuef( - 8036, "%q is interleaved by table %q", from, table.Name) - } - return p.CheckPrivilege(ctx, table, privilege.CREATE) -} - -func (p *planner) removeInterleave(ctx context.Context, ref descpb.ForeignKeyReference) error { - table, err := p.Descriptors().GetMutableTableVersionByID(ctx, ref.Table, p.txn) - if err != nil { - return err - } - if table.Dropped() { - // The referenced table is being dropped. No need to modify it further. - return nil - } - idx, err := table.FindIndexWithID(ref.Index) - if err != nil { - return err - } - idx.IndexDesc().Interleave.Ancestors = nil - // No job description, since this is presumably part of some larger schema change. - return p.writeSchemaChange(ctx, table, descpb.InvalidMutationID, "") -} - // dropTableImpl does the work of dropping a table (and everything that depends // on it if `cascade` is enabled). It returns a list of view names that were // dropped due to `cascade` behavior. droppingParent indicates whether this @@ -306,21 +257,6 @@ func (p *planner) dropTableImpl( } tableDesc.InboundFKs = nil - // Remove interleave relationships. - for _, idx := range tableDesc.NonDropIndexes() { - if idx.NumInterleaveAncestors() > 0 { - if err := p.removeInterleaveBackReference(ctx, tableDesc, idx); err != nil { - return droppedViews, err - } - } - for i := 0; i < idx.NumInterleavedBy(); i++ { - ref := idx.GetInterleavedBy(i) - if err := p.removeInterleave(ctx, ref); err != nil { - return droppedViews, err - } - } - } - // Remove sequence dependencies. for _, col := range tableDesc.PublicColumns() { if err := p.removeSequenceDependencies(ctx, tableDesc, col); err != nil { @@ -439,15 +375,9 @@ func (p *planner) initiateDropTable( ) } - // If the table is not interleaved , use the delayed GC mechanism to - // schedule usage of the more efficient ClearRange pathway. ClearRange will - // only work if the entire hierarchy of interleaved tables are dropped at - // once, as with ON DELETE CASCADE where the top-level "root" table is - // dropped. - // - // TODO(bram): If interleaved and ON DELETE CASCADE, we will be able to use - // this faster mechanism. - if tableDesc.IsTable() && !tableDesc.IsInterleaved() { + // Use the delayed GC mechanism to schedule usage of the more efficient + // ClearRange pathway. + if tableDesc.IsTable() { tableDesc.DropTime = timeutil.Now().UnixNano() } @@ -664,54 +594,6 @@ func removeFKBackReferenceFromTable( return nil } -func (p *planner) removeInterleaveBackReference( - ctx context.Context, tableDesc *tabledesc.Mutable, idx catalog.Index, -) error { - if idx.NumInterleaveAncestors() == 0 { - return nil - } - ancestor := idx.GetInterleaveAncestor(idx.NumInterleaveAncestors() - 1) - var t *tabledesc.Mutable - if ancestor.TableID == tableDesc.ID { - t = tableDesc - } else { - lookup, err := p.Descriptors().GetMutableTableVersionByID(ctx, ancestor.TableID, p.txn) - if err != nil { - return errors.Wrapf(err, "error resolving referenced table ID %d", ancestor.TableID) - } - t = lookup - } - if t.Dropped() { - // The referenced table is being dropped. No need to modify it further. - return nil - } - targetIdxI, err := t.FindIndexWithID(ancestor.IndexID) - if err != nil { - return err - } - targetIdx := targetIdxI.IndexDesc() - foundAncestor := false - for k, ref := range targetIdx.InterleavedBy { - if ref.Table == tableDesc.ID && ref.Index == idx.GetID() { - if foundAncestor { - return errors.AssertionFailedf( - "ancestor entry in %s for %s@%s found more than once", t.Name, tableDesc.Name, idx.GetName()) - } - targetIdx.InterleavedBy = append(targetIdx.InterleavedBy[:k], targetIdx.InterleavedBy[k+1:]...) - foundAncestor = true - } - } - if t != tableDesc { - return p.writeSchemaChange( - ctx, t, descpb.InvalidMutationID, - fmt.Sprintf("removing reference for interleaved table %s(%d)", - t.Name, t.ID, - ), - ) - } - return nil -} - // removeMatchingReferences removes all refs from the provided slice that // match the provided ID, returning the modified slice. func removeMatchingReferences( diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index f5b659ef981a..b99b05a2e878 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -459,12 +459,6 @@ var experimentalComputedColumnRewrites = settings.RegisterValidatedStringSetting }, ) -var copyPartitioningWhenDeinterleavingTable = settings.RegisterBoolSetting( - `sql.defaults.copy_partitioning_when_deinterleaving_table.enabled`, - `default value for enable_copying_partitioning_when_deinterleaving_table session variable`, - false, -).WithPublic() - var propagateInputOrdering = settings.RegisterBoolSetting( `sql.defaults.propagate_input_ordering.enabled`, `default value for the experimental propagate_input_ordering session variable`, @@ -2923,12 +2917,6 @@ func (m *sessionDataMutator) SetStubCatalogTablesEnabled(enabled bool) { m.data.StubCatalogTablesEnabled = enabled } -// SetCopyPartitioningWhenDeinterleavingTable sets the value for -// CopyPartitioningWhenDeinterleavingTable. -func (m *sessionDataMutator) SetCopyPartitioningWhenDeinterleavingTable(b bool) { - m.data.CopyPartitioningWhenDeinterleavingTable = b -} - func (m *sessionDataMutator) SetExperimentalComputedColumnRewrites(val string) { m.data.ExperimentalComputedColumnRewrites = val } diff --git a/pkg/sql/gcjob/BUILD.bazel b/pkg/sql/gcjob/BUILD.bazel index 0c89a304d3f4..3f3dd969ba23 100644 --- a/pkg/sql/gcjob/BUILD.bazel +++ b/pkg/sql/gcjob/BUILD.bazel @@ -31,7 +31,6 @@ go_library( "//pkg/sql/catalog/catalogkv", "//pkg/sql/catalog/descpb", "//pkg/sql/catalog/descs", - "//pkg/sql/catalog/tabledesc", "//pkg/sql/pgwire/pgcode", "//pkg/sql/pgwire/pgerror", "//pkg/sql/sem/tree", diff --git a/pkg/sql/gcjob/gc_job.go b/pkg/sql/gcjob/gc_job.go index 8589234319c0..bfb62c3bfd8d 100644 --- a/pkg/sql/gcjob/gc_job.go +++ b/pkg/sql/gcjob/gc_job.go @@ -20,7 +20,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" @@ -105,29 +104,6 @@ func (r schemaChangeGCResumer) Resume(ctx context.Context, execCtx interface{}) return err } - // If there are any interleaved indexes to drop as part of a table TRUNCATE - // operation, then drop the indexes before waiting on the GC timer. - if len(details.InterleavedIndexes) > 0 { - // Before deleting any indexes, ensure that old versions of the table - // descriptor are no longer in use. - _, err := sql.WaitToUpdateLeases(ctx, execCfg.LeaseManager, details.InterleavedTable.ID) - if err != nil { - return err - } - interleavedIndexIDs := make([]descpb.IndexID, len(details.InterleavedIndexes)) - for i := range details.InterleavedIndexes { - interleavedIndexIDs[i] = details.InterleavedIndexes[i].ID - } - if err := sql.TruncateInterleavedIndexes( - ctx, - execCfg, - tabledesc.NewBuilder(details.InterleavedTable).BuildImmutableTable(), - interleavedIndexIDs, - ); err != nil { - return err - } - } - tableDropTimes, indexDropTimes := getDropTimes(details) timer := timeutil.NewTimer() diff --git a/pkg/sql/gcjob/table_garbage_collection.go b/pkg/sql/gcjob/table_garbage_collection.go index 410da3c5073f..ffb8b163cce1 100644 --- a/pkg/sql/gcjob/table_garbage_collection.go +++ b/pkg/sql/gcjob/table_garbage_collection.go @@ -94,10 +94,7 @@ func ClearTableData( ) error { // If DropTime isn't set, assume this drop request is from a version // 1.1 server and invoke legacy code that uses DeleteRange and range GC. - // TODO(pbardea): Note that we never set the drop time for interleaved tables, - // but this check was added to be more explicit about it. This should get - // cleaned up. - if table.GetDropTime() == 0 || table.IsInterleaved() { + if table.GetDropTime() == 0 { log.Infof(ctx, "clearing data in chunks for table %d", table.GetID()) return sql.ClearTableDataInChunks(ctx, db, codec, sv, table, false /* traceKV */) } diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal b/pkg/sql/logictest/testdata/logic_test/crdb_internal index ef1e721a02b1..e6400c8798c4 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal @@ -42,7 +42,6 @@ crdb_internal gossip_network table NULL NULL NULL crdb_internal gossip_nodes table NULL NULL NULL crdb_internal index_columns table NULL NULL NULL crdb_internal index_usage_statistics table NULL NULL NULL -crdb_internal interleaved table NULL NULL NULL crdb_internal invalid_objects table NULL NULL NULL crdb_internal jobs table NULL NULL NULL crdb_internal kv_node_liveness table NULL NULL NULL diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal_tenant b/pkg/sql/logictest/testdata/logic_test/crdb_internal_tenant index b3666fe59f7f..70f8555455e5 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal_tenant +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal_tenant @@ -59,7 +59,6 @@ crdb_internal gossip_network table NULL NULL NULL crdb_internal gossip_nodes table NULL NULL NULL crdb_internal index_columns table NULL NULL NULL crdb_internal index_usage_statistics table NULL NULL NULL -crdb_internal interleaved table NULL NULL NULL crdb_internal invalid_objects table NULL NULL NULL crdb_internal jobs table NULL NULL NULL crdb_internal kv_node_liveness table NULL NULL NULL diff --git a/pkg/sql/logictest/testdata/logic_test/create_statements b/pkg/sql/logictest/testdata/logic_test/create_statements index 8ed6d493c197..642d6fa8f9b1 100644 --- a/pkg/sql/logictest/testdata/logic_test/create_statements +++ b/pkg/sql/logictest/testdata/logic_test/create_statements @@ -482,23 +482,6 @@ CREATE TABLE crdb_internal.index_usage_statistics ( total_reads INT8 NOT NULL, last_read TIMESTAMPTZ NULL ) {} {} -CREATE TABLE crdb_internal.interleaved ( - database_name STRING NOT NULL, - schema_name STRING NOT NULL, - table_name STRING NOT NULL, - index_name STRING NOT NULL, - parent_database_name STRING NOT NULL, - parent_schema_name STRING NOT NULL, - parent_table_name STRING NOT NULL -) CREATE TABLE crdb_internal.interleaved ( - database_name STRING NOT NULL, - schema_name STRING NOT NULL, - table_name STRING NOT NULL, - index_name STRING NOT NULL, - parent_database_name STRING NOT NULL, - parent_schema_name STRING NOT NULL, - parent_table_name STRING NOT NULL -) {} {} CREATE TABLE crdb_internal.invalid_objects ( id INT8 NULL, database_name STRING NULL, diff --git a/pkg/sql/logictest/testdata/logic_test/create_table b/pkg/sql/logictest/testdata/logic_test/create_table index 9baa725b60df..999420fa4474 100644 --- a/pkg/sql/logictest/testdata/logic_test/create_table +++ b/pkg/sql/logictest/testdata/logic_test/create_table @@ -441,9 +441,6 @@ CREATE TABLE error (a INT CHECK (a > 5), CONSTRAINT check_a CHECK (a > 5)) statement error pgcode 0A000 unique constraints without an index cannot store columns CREATE TABLE error (a INT, b INT, UNIQUE WITHOUT INDEX (a) STORING (b)) -statement error pgcode 0A000 interleaved unique constraints without an index are not supported -CREATE TABLE error (a INT, b INT, UNIQUE WITHOUT INDEX (a) INTERLEAVE IN PARENT foo (b)) - statement error pgcode 0A000 partitioned unique constraints without an index are not supported CREATE TABLE error (a INT, b INT, UNIQUE WITHOUT INDEX (a) PARTITION BY LIST (b) ( PARTITION p1 VALUES IN (1) diff --git a/pkg/sql/logictest/testdata/logic_test/grant_table b/pkg/sql/logictest/testdata/logic_test/grant_table index 58d68b055d07..7d57f6406aec 100644 --- a/pkg/sql/logictest/testdata/logic_test/grant_table +++ b/pkg/sql/logictest/testdata/logic_test/grant_table @@ -54,7 +54,6 @@ test crdb_internal gossip_network public test crdb_internal gossip_nodes public SELECT test crdb_internal index_columns public SELECT test crdb_internal index_usage_statistics public SELECT -test crdb_internal interleaved public SELECT test crdb_internal invalid_objects public SELECT test crdb_internal jobs public SELECT test crdb_internal kv_node_liveness public SELECT diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index 6dc765c47a9e..88d678e7e1f5 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -424,7 +424,6 @@ crdb_internal gossip_network crdb_internal gossip_nodes crdb_internal index_columns crdb_internal index_usage_statistics -crdb_internal interleaved crdb_internal invalid_objects crdb_internal jobs crdb_internal kv_node_liveness @@ -741,7 +740,6 @@ gossip_network gossip_nodes index_columns index_usage_statistics -interleaved invalid_objects jobs kv_node_liveness @@ -1095,7 +1093,6 @@ system crdb_internal gossip_network SYSTEM system crdb_internal gossip_nodes SYSTEM VIEW NO 1 system crdb_internal index_columns SYSTEM VIEW NO 1 system crdb_internal index_usage_statistics SYSTEM VIEW NO 1 -system crdb_internal interleaved SYSTEM VIEW NO 1 system crdb_internal invalid_objects SYSTEM VIEW NO 1 system crdb_internal jobs SYSTEM VIEW NO 1 system crdb_internal kv_node_liveness SYSTEM VIEW NO 1 @@ -2664,7 +2661,6 @@ NULL public system crdb_internal gossip_network NULL public system crdb_internal gossip_nodes SELECT NULL YES NULL public system crdb_internal index_columns SELECT NULL YES NULL public system crdb_internal index_usage_statistics SELECT NULL YES -NULL public system crdb_internal interleaved SELECT NULL YES NULL public system crdb_internal invalid_objects SELECT NULL YES NULL public system crdb_internal jobs SELECT NULL YES NULL public system crdb_internal kv_node_liveness SELECT NULL YES @@ -3270,7 +3266,6 @@ NULL public system crdb_internal gossip_network NULL public system crdb_internal gossip_nodes SELECT NULL YES NULL public system crdb_internal index_columns SELECT NULL YES NULL public system crdb_internal index_usage_statistics SELECT NULL YES -NULL public system crdb_internal interleaved SELECT NULL YES NULL public system crdb_internal invalid_objects SELECT NULL YES NULL public system crdb_internal jobs SELECT NULL YES NULL public system crdb_internal kv_node_liveness SELECT NULL YES @@ -4628,7 +4623,6 @@ disable_partially_distributed_plans off disable_plan_gists off disallow_full_table_scans off distsql_workmem 64 MiB -enable_copying_partitioning_when_deinterleaving_table off enable_drop_enum_value on enable_experimental_alter_column_type_general off enable_experimental_stream_replication off diff --git a/pkg/sql/logictest/testdata/logic_test/pg_builtins b/pkg/sql/logictest/testdata/logic_test/pg_builtins index b7a9fd3917d3..a65f0905e32a 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_builtins +++ b/pkg/sql/logictest/testdata/logic_test/pg_builtins @@ -177,42 +177,42 @@ is_updatable b 66 2 28 is_updatable c 66 3 28 false is_updatable_view a 67 1 0 false is_updatable_view b 67 2 0 false -pg_class oid 4294967131 1 0 false -pg_class relname 4294967131 2 0 false -pg_class relnamespace 4294967131 3 0 false -pg_class reltype 4294967131 4 0 false -pg_class reloftype 4294967131 5 0 false -pg_class relowner 4294967131 6 0 false -pg_class relam 4294967131 7 0 false -pg_class relfilenode 4294967131 8 0 false -pg_class reltablespace 4294967131 9 0 false -pg_class relpages 4294967131 10 0 false -pg_class reltuples 4294967131 11 0 false -pg_class relallvisible 4294967131 12 0 false -pg_class reltoastrelid 4294967131 13 0 false -pg_class relhasindex 4294967131 14 0 false -pg_class relisshared 4294967131 15 0 false -pg_class relpersistence 4294967131 16 0 false -pg_class relistemp 4294967131 17 0 false -pg_class relkind 4294967131 18 0 false -pg_class relnatts 4294967131 19 0 false -pg_class relchecks 4294967131 20 0 false -pg_class relhasoids 4294967131 21 0 false -pg_class relhaspkey 4294967131 22 0 false -pg_class relhasrules 4294967131 23 0 false -pg_class relhastriggers 4294967131 24 0 false -pg_class relhassubclass 4294967131 25 0 false -pg_class relfrozenxid 4294967131 26 0 false -pg_class relacl 4294967131 27 0 false -pg_class reloptions 4294967131 28 0 false -pg_class relforcerowsecurity 4294967131 29 0 false -pg_class relispartition 4294967131 30 0 false -pg_class relispopulated 4294967131 31 0 false -pg_class relreplident 4294967131 32 0 false -pg_class relrewrite 4294967131 33 0 false -pg_class relrowsecurity 4294967131 34 0 false -pg_class relpartbound 4294967131 35 0 false -pg_class relminmxid 4294967131 36 0 false +pg_class oid 4294967132 1 0 false +pg_class relname 4294967132 2 0 false +pg_class relnamespace 4294967132 3 0 false +pg_class reltype 4294967132 4 0 false +pg_class reloftype 4294967132 5 0 false +pg_class relowner 4294967132 6 0 false +pg_class relam 4294967132 7 0 false +pg_class relfilenode 4294967132 8 0 false +pg_class reltablespace 4294967132 9 0 false +pg_class relpages 4294967132 10 0 false +pg_class reltuples 4294967132 11 0 false +pg_class relallvisible 4294967132 12 0 false +pg_class reltoastrelid 4294967132 13 0 false +pg_class relhasindex 4294967132 14 0 false +pg_class relisshared 4294967132 15 0 false +pg_class relpersistence 4294967132 16 0 false +pg_class relistemp 4294967132 17 0 false +pg_class relkind 4294967132 18 0 false +pg_class relnatts 4294967132 19 0 false +pg_class relchecks 4294967132 20 0 false +pg_class relhasoids 4294967132 21 0 false +pg_class relhaspkey 4294967132 22 0 false +pg_class relhasrules 4294967132 23 0 false +pg_class relhastriggers 4294967132 24 0 false +pg_class relhassubclass 4294967132 25 0 false +pg_class relfrozenxid 4294967132 26 0 false +pg_class relacl 4294967132 27 0 false +pg_class reloptions 4294967132 28 0 false +pg_class relforcerowsecurity 4294967132 29 0 false +pg_class relispartition 4294967132 30 0 false +pg_class relispopulated 4294967132 31 0 false +pg_class relreplident 4294967132 32 0 false +pg_class relrewrite 4294967132 33 0 false +pg_class relrowsecurity 4294967132 34 0 false +pg_class relpartbound 4294967132 35 0 false +pg_class relminmxid 4294967132 36 0 false # Check that the oid does not exist. If this test fail, change the oid here and in # the next test at 'relation does not exist' value. diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index df5977c1f562..63089815bcf3 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -1408,14 +1408,14 @@ FROM pg_catalog.pg_depend ORDER BY objid ---- classid objid objsubid refclassid refobjid refobjsubid deptype -4294967128 109163875 0 4294967131 450499960 0 n -4294967128 1329876328 0 4294967131 0 0 n -4294967128 1652586190 0 4294967131 450499961 0 n -4294967128 2093076183 0 4294967131 0 0 n -4294967085 4079785833 0 4294967131 55 3 n -4294967085 4079785833 0 4294967131 55 4 n -4294967085 4079785833 0 4294967131 55 1 n -4294967085 4079785833 0 4294967131 55 2 n +4294967129 109163875 0 4294967132 450499960 0 n +4294967129 1329876328 0 4294967132 0 0 n +4294967129 1652586190 0 4294967132 450499961 0 n +4294967129 2093076183 0 4294967132 0 0 n +4294967086 4079785833 0 4294967132 55 3 n +4294967086 4079785833 0 4294967132 55 4 n +4294967086 4079785833 0 4294967132 55 1 n +4294967086 4079785833 0 4294967132 55 2 n # Some entries in pg_depend are dependency links from the pg_constraint system # table to the pg_class system table. Other entries are links to pg_class when it is @@ -1428,8 +1428,8 @@ JOIN pg_class cla ON classid=cla.oid JOIN pg_class refcla ON refclassid=refcla.oid ---- classid refclassid tablename reftablename -4294967085 4294967131 pg_rewrite pg_class -4294967128 4294967131 pg_constraint pg_class +4294967086 4294967132 pg_rewrite pg_class +4294967129 4294967132 pg_constraint pg_class # Some entries in pg_depend are foreign key constraints that reference an index # in pg_class. Other entries are table-view dependencies @@ -1621,232 +1621,231 @@ oid typname typnamespace typowner typ 100076 _newtype1 2332901747 1546506610 -1 false b 100077 newtype2 2332901747 1546506610 -1 false e 100078 _newtype2 2332901747 1546506610 -1 false b -4294967010 spatial_ref_sys 3553698885 3233629770 -1 false c -4294967011 geometry_columns 3553698885 3233629770 -1 false c -4294967012 geography_columns 3553698885 3233629770 -1 false c -4294967014 pg_views 1307062959 3233629770 -1 false c -4294967015 pg_user 1307062959 3233629770 -1 false c -4294967016 pg_user_mappings 1307062959 3233629770 -1 false c -4294967017 pg_user_mapping 1307062959 3233629770 -1 false c -4294967018 pg_type 1307062959 3233629770 -1 false c -4294967019 pg_ts_template 1307062959 3233629770 -1 false c -4294967020 pg_ts_parser 1307062959 3233629770 -1 false c -4294967021 pg_ts_dict 1307062959 3233629770 -1 false c -4294967022 pg_ts_config 1307062959 3233629770 -1 false c -4294967023 pg_ts_config_map 1307062959 3233629770 -1 false c -4294967024 pg_trigger 1307062959 3233629770 -1 false c -4294967025 pg_transform 1307062959 3233629770 -1 false c -4294967026 pg_timezone_names 1307062959 3233629770 -1 false c -4294967027 pg_timezone_abbrevs 1307062959 3233629770 -1 false c -4294967028 pg_tablespace 1307062959 3233629770 -1 false c -4294967029 pg_tables 1307062959 3233629770 -1 false c -4294967030 pg_subscription 1307062959 3233629770 -1 false c -4294967031 pg_subscription_rel 1307062959 3233629770 -1 false c -4294967032 pg_stats 1307062959 3233629770 -1 false c -4294967033 pg_stats_ext 1307062959 3233629770 -1 false c -4294967034 pg_statistic 1307062959 3233629770 -1 false c -4294967035 pg_statistic_ext 1307062959 3233629770 -1 false c -4294967036 pg_statistic_ext_data 1307062959 3233629770 -1 false c -4294967037 pg_statio_user_tables 1307062959 3233629770 -1 false c -4294967038 pg_statio_user_sequences 1307062959 3233629770 -1 false c -4294967039 pg_statio_user_indexes 1307062959 3233629770 -1 false c -4294967040 pg_statio_sys_tables 1307062959 3233629770 -1 false c -4294967041 pg_statio_sys_sequences 1307062959 3233629770 -1 false c -4294967042 pg_statio_sys_indexes 1307062959 3233629770 -1 false c -4294967043 pg_statio_all_tables 1307062959 3233629770 -1 false c -4294967044 pg_statio_all_sequences 1307062959 3233629770 -1 false c -4294967045 pg_statio_all_indexes 1307062959 3233629770 -1 false c -4294967046 pg_stat_xact_user_tables 1307062959 3233629770 -1 false c -4294967047 pg_stat_xact_user_functions 1307062959 3233629770 -1 false c -4294967048 pg_stat_xact_sys_tables 1307062959 3233629770 -1 false c -4294967049 pg_stat_xact_all_tables 1307062959 3233629770 -1 false c -4294967050 pg_stat_wal_receiver 1307062959 3233629770 -1 false c -4294967051 pg_stat_user_tables 1307062959 3233629770 -1 false c -4294967052 pg_stat_user_indexes 1307062959 3233629770 -1 false c -4294967053 pg_stat_user_functions 1307062959 3233629770 -1 false c -4294967054 pg_stat_sys_tables 1307062959 3233629770 -1 false c -4294967055 pg_stat_sys_indexes 1307062959 3233629770 -1 false c -4294967056 pg_stat_subscription 1307062959 3233629770 -1 false c -4294967057 pg_stat_ssl 1307062959 3233629770 -1 false c -4294967058 pg_stat_slru 1307062959 3233629770 -1 false c -4294967059 pg_stat_replication 1307062959 3233629770 -1 false c -4294967060 pg_stat_progress_vacuum 1307062959 3233629770 -1 false c -4294967061 pg_stat_progress_create_index 1307062959 3233629770 -1 false c -4294967062 pg_stat_progress_cluster 1307062959 3233629770 -1 false c -4294967063 pg_stat_progress_basebackup 1307062959 3233629770 -1 false c -4294967064 pg_stat_progress_analyze 1307062959 3233629770 -1 false c -4294967065 pg_stat_gssapi 1307062959 3233629770 -1 false c -4294967066 pg_stat_database 1307062959 3233629770 -1 false c -4294967067 pg_stat_database_conflicts 1307062959 3233629770 -1 false c -4294967068 pg_stat_bgwriter 1307062959 3233629770 -1 false c -4294967069 pg_stat_archiver 1307062959 3233629770 -1 false c -4294967070 pg_stat_all_tables 1307062959 3233629770 -1 false c -4294967071 pg_stat_all_indexes 1307062959 3233629770 -1 false c -4294967072 pg_stat_activity 1307062959 3233629770 -1 false c -4294967073 pg_shmem_allocations 1307062959 3233629770 -1 false c -4294967074 pg_shdepend 1307062959 3233629770 -1 false c -4294967075 pg_shseclabel 1307062959 3233629770 -1 false c -4294967076 pg_shdescription 1307062959 3233629770 -1 false c -4294967077 pg_shadow 1307062959 3233629770 -1 false c -4294967078 pg_settings 1307062959 3233629770 -1 false c -4294967079 pg_sequences 1307062959 3233629770 -1 false c -4294967080 pg_sequence 1307062959 3233629770 -1 false c -4294967081 pg_seclabel 1307062959 3233629770 -1 false c -4294967082 pg_seclabels 1307062959 3233629770 -1 false c -4294967083 pg_rules 1307062959 3233629770 -1 false c -4294967084 pg_roles 1307062959 3233629770 -1 false c -4294967085 pg_rewrite 1307062959 3233629770 -1 false c -4294967086 pg_replication_slots 1307062959 3233629770 -1 false c -4294967087 pg_replication_origin 1307062959 3233629770 -1 false c -4294967088 pg_replication_origin_status 1307062959 3233629770 -1 false c -4294967089 pg_range 1307062959 3233629770 -1 false c -4294967090 pg_publication_tables 1307062959 3233629770 -1 false c -4294967091 pg_publication 1307062959 3233629770 -1 false c -4294967092 pg_publication_rel 1307062959 3233629770 -1 false c -4294967093 pg_proc 1307062959 3233629770 -1 false c -4294967094 pg_prepared_xacts 1307062959 3233629770 -1 false c -4294967095 pg_prepared_statements 1307062959 3233629770 -1 false c -4294967096 pg_policy 1307062959 3233629770 -1 false c -4294967097 pg_policies 1307062959 3233629770 -1 false c -4294967098 pg_partitioned_table 1307062959 3233629770 -1 false c -4294967099 pg_opfamily 1307062959 3233629770 -1 false c -4294967100 pg_operator 1307062959 3233629770 -1 false c -4294967101 pg_opclass 1307062959 3233629770 -1 false c -4294967102 pg_namespace 1307062959 3233629770 -1 false c -4294967103 pg_matviews 1307062959 3233629770 -1 false c -4294967104 pg_locks 1307062959 3233629770 -1 false c -4294967105 pg_largeobject 1307062959 3233629770 -1 false c -4294967106 pg_largeobject_metadata 1307062959 3233629770 -1 false c -4294967107 pg_language 1307062959 3233629770 -1 false c -4294967108 pg_init_privs 1307062959 3233629770 -1 false c -4294967109 pg_inherits 1307062959 3233629770 -1 false c -4294967110 pg_indexes 1307062959 3233629770 -1 false c -4294967111 pg_index 1307062959 3233629770 -1 false c -4294967112 pg_hba_file_rules 1307062959 3233629770 -1 false c -4294967113 pg_group 1307062959 3233629770 -1 false c -4294967114 pg_foreign_table 1307062959 3233629770 -1 false c -4294967115 pg_foreign_server 1307062959 3233629770 -1 false c -4294967116 pg_foreign_data_wrapper 1307062959 3233629770 -1 false c -4294967117 pg_file_settings 1307062959 3233629770 -1 false c -4294967118 pg_extension 1307062959 3233629770 -1 false c -4294967119 pg_event_trigger 1307062959 3233629770 -1 false c -4294967120 pg_enum 1307062959 3233629770 -1 false c -4294967121 pg_description 1307062959 3233629770 -1 false c -4294967122 pg_depend 1307062959 3233629770 -1 false c -4294967123 pg_default_acl 1307062959 3233629770 -1 false c -4294967124 pg_db_role_setting 1307062959 3233629770 -1 false c -4294967125 pg_database 1307062959 3233629770 -1 false c -4294967126 pg_cursors 1307062959 3233629770 -1 false c -4294967127 pg_conversion 1307062959 3233629770 -1 false c -4294967128 pg_constraint 1307062959 3233629770 -1 false c -4294967129 pg_config 1307062959 3233629770 -1 false c -4294967130 pg_collation 1307062959 3233629770 -1 false c -4294967131 pg_class 1307062959 3233629770 -1 false c -4294967132 pg_cast 1307062959 3233629770 -1 false c -4294967133 pg_available_extensions 1307062959 3233629770 -1 false c -4294967134 pg_available_extension_versions 1307062959 3233629770 -1 false c -4294967135 pg_auth_members 1307062959 3233629770 -1 false c -4294967136 pg_authid 1307062959 3233629770 -1 false c -4294967137 pg_attribute 1307062959 3233629770 -1 false c -4294967138 pg_attrdef 1307062959 3233629770 -1 false c -4294967139 pg_amproc 1307062959 3233629770 -1 false c -4294967140 pg_amop 1307062959 3233629770 -1 false c -4294967141 pg_am 1307062959 3233629770 -1 false c -4294967142 pg_aggregate 1307062959 3233629770 -1 false c -4294967144 views 359535012 3233629770 -1 false c -4294967145 view_table_usage 359535012 3233629770 -1 false c -4294967146 view_routine_usage 359535012 3233629770 -1 false c -4294967147 view_column_usage 359535012 3233629770 -1 false c -4294967148 user_privileges 359535012 3233629770 -1 false c -4294967149 user_mappings 359535012 3233629770 -1 false c -4294967150 user_mapping_options 359535012 3233629770 -1 false c -4294967151 user_defined_types 359535012 3233629770 -1 false c -4294967152 user_attributes 359535012 3233629770 -1 false c -4294967153 usage_privileges 359535012 3233629770 -1 false c -4294967154 udt_privileges 359535012 3233629770 -1 false c -4294967155 type_privileges 359535012 3233629770 -1 false c -4294967156 triggers 359535012 3233629770 -1 false c -4294967157 triggered_update_columns 359535012 3233629770 -1 false c -4294967158 transforms 359535012 3233629770 -1 false c -4294967159 tablespaces 359535012 3233629770 -1 false c -4294967160 tablespaces_extensions 359535012 3233629770 -1 false c -4294967161 tables 359535012 3233629770 -1 false c -4294967162 tables_extensions 359535012 3233629770 -1 false c -4294967163 table_privileges 359535012 3233629770 -1 false c -4294967164 table_constraints_extensions 359535012 3233629770 -1 false c -4294967165 table_constraints 359535012 3233629770 -1 false c -4294967166 statistics 359535012 3233629770 -1 false c -4294967167 st_units_of_measure 359535012 3233629770 -1 false c -4294967168 st_spatial_reference_systems 359535012 3233629770 -1 false c -4294967169 st_geometry_columns 359535012 3233629770 -1 false c -4294967170 session_variables 359535012 3233629770 -1 false c -4294967171 sequences 359535012 3233629770 -1 false c -4294967172 schema_privileges 359535012 3233629770 -1 false c -4294967173 schemata 359535012 3233629770 -1 false c -4294967174 schemata_extensions 359535012 3233629770 -1 false c -4294967175 sql_sizing 359535012 3233629770 -1 false c -4294967176 sql_parts 359535012 3233629770 -1 false c -4294967177 sql_implementation_info 359535012 3233629770 -1 false c -4294967178 sql_features 359535012 3233629770 -1 false c -4294967179 routines 359535012 3233629770 -1 false c -4294967180 routine_privileges 359535012 3233629770 -1 false c -4294967181 role_usage_grants 359535012 3233629770 -1 false c -4294967182 role_udt_grants 359535012 3233629770 -1 false c -4294967183 role_table_grants 359535012 3233629770 -1 false c -4294967184 role_routine_grants 359535012 3233629770 -1 false c -4294967185 role_column_grants 359535012 3233629770 -1 false c -4294967186 resource_groups 359535012 3233629770 -1 false c -4294967187 referential_constraints 359535012 3233629770 -1 false c -4294967188 profiling 359535012 3233629770 -1 false c -4294967189 processlist 359535012 3233629770 -1 false c -4294967190 plugins 359535012 3233629770 -1 false c -4294967191 partitions 359535012 3233629770 -1 false c -4294967192 parameters 359535012 3233629770 -1 false c -4294967193 optimizer_trace 359535012 3233629770 -1 false c -4294967194 keywords 359535012 3233629770 -1 false c -4294967195 key_column_usage 359535012 3233629770 -1 false c -4294967196 information_schema_catalog_name 359535012 3233629770 -1 false c -4294967197 foreign_tables 359535012 3233629770 -1 false c -4294967198 foreign_table_options 359535012 3233629770 -1 false c -4294967199 foreign_servers 359535012 3233629770 -1 false c -4294967200 foreign_server_options 359535012 3233629770 -1 false c -4294967201 foreign_data_wrappers 359535012 3233629770 -1 false c -4294967202 foreign_data_wrapper_options 359535012 3233629770 -1 false c -4294967203 files 359535012 3233629770 -1 false c -4294967204 events 359535012 3233629770 -1 false c -4294967205 engines 359535012 3233629770 -1 false c -4294967206 enabled_roles 359535012 3233629770 -1 false c -4294967207 element_types 359535012 3233629770 -1 false c -4294967208 domains 359535012 3233629770 -1 false c -4294967209 domain_udt_usage 359535012 3233629770 -1 false c -4294967210 domain_constraints 359535012 3233629770 -1 false c -4294967211 data_type_privileges 359535012 3233629770 -1 false c -4294967212 constraint_table_usage 359535012 3233629770 -1 false c -4294967213 constraint_column_usage 359535012 3233629770 -1 false c -4294967214 columns 359535012 3233629770 -1 false c -4294967215 columns_extensions 359535012 3233629770 -1 false c -4294967216 column_udt_usage 359535012 3233629770 -1 false c -4294967217 column_statistics 359535012 3233629770 -1 false c -4294967218 column_privileges 359535012 3233629770 -1 false c -4294967219 column_options 359535012 3233629770 -1 false c -4294967220 column_domain_usage 359535012 3233629770 -1 false c -4294967221 column_column_usage 359535012 3233629770 -1 false c -4294967222 collations 359535012 3233629770 -1 false c -4294967223 collation_character_set_applicability 359535012 3233629770 -1 false c -4294967224 check_constraints 359535012 3233629770 -1 false c -4294967225 check_constraint_routine_usage 359535012 3233629770 -1 false c -4294967226 character_sets 359535012 3233629770 -1 false c -4294967227 attributes 359535012 3233629770 -1 false c -4294967228 applicable_roles 359535012 3233629770 -1 false c -4294967229 administrable_role_authorizations 359535012 3233629770 -1 false c -4294967231 tenant_usage_details 1146641803 3233629770 -1 false c -4294967232 active_range_feeds 1146641803 3233629770 -1 false c -4294967233 default_privileges 1146641803 3233629770 -1 false c -4294967234 regions 1146641803 3233629770 -1 false c -4294967235 cluster_inflight_traces 1146641803 3233629770 -1 false c -4294967236 lost_descriptors_with_data 1146641803 3233629770 -1 false c -4294967237 cross_db_references 1146641803 3233629770 -1 false c -4294967238 interleaved 1146641803 3233629770 -1 false c +4294967011 spatial_ref_sys 3553698885 3233629770 -1 false c +4294967012 geometry_columns 3553698885 3233629770 -1 false c +4294967013 geography_columns 3553698885 3233629770 -1 false c +4294967015 pg_views 1307062959 3233629770 -1 false c +4294967016 pg_user 1307062959 3233629770 -1 false c +4294967017 pg_user_mappings 1307062959 3233629770 -1 false c +4294967018 pg_user_mapping 1307062959 3233629770 -1 false c +4294967019 pg_type 1307062959 3233629770 -1 false c +4294967020 pg_ts_template 1307062959 3233629770 -1 false c +4294967021 pg_ts_parser 1307062959 3233629770 -1 false c +4294967022 pg_ts_dict 1307062959 3233629770 -1 false c +4294967023 pg_ts_config 1307062959 3233629770 -1 false c +4294967024 pg_ts_config_map 1307062959 3233629770 -1 false c +4294967025 pg_trigger 1307062959 3233629770 -1 false c +4294967026 pg_transform 1307062959 3233629770 -1 false c +4294967027 pg_timezone_names 1307062959 3233629770 -1 false c +4294967028 pg_timezone_abbrevs 1307062959 3233629770 -1 false c +4294967029 pg_tablespace 1307062959 3233629770 -1 false c +4294967030 pg_tables 1307062959 3233629770 -1 false c +4294967031 pg_subscription 1307062959 3233629770 -1 false c +4294967032 pg_subscription_rel 1307062959 3233629770 -1 false c +4294967033 pg_stats 1307062959 3233629770 -1 false c +4294967034 pg_stats_ext 1307062959 3233629770 -1 false c +4294967035 pg_statistic 1307062959 3233629770 -1 false c +4294967036 pg_statistic_ext 1307062959 3233629770 -1 false c +4294967037 pg_statistic_ext_data 1307062959 3233629770 -1 false c +4294967038 pg_statio_user_tables 1307062959 3233629770 -1 false c +4294967039 pg_statio_user_sequences 1307062959 3233629770 -1 false c +4294967040 pg_statio_user_indexes 1307062959 3233629770 -1 false c +4294967041 pg_statio_sys_tables 1307062959 3233629770 -1 false c +4294967042 pg_statio_sys_sequences 1307062959 3233629770 -1 false c +4294967043 pg_statio_sys_indexes 1307062959 3233629770 -1 false c +4294967044 pg_statio_all_tables 1307062959 3233629770 -1 false c +4294967045 pg_statio_all_sequences 1307062959 3233629770 -1 false c +4294967046 pg_statio_all_indexes 1307062959 3233629770 -1 false c +4294967047 pg_stat_xact_user_tables 1307062959 3233629770 -1 false c +4294967048 pg_stat_xact_user_functions 1307062959 3233629770 -1 false c +4294967049 pg_stat_xact_sys_tables 1307062959 3233629770 -1 false c +4294967050 pg_stat_xact_all_tables 1307062959 3233629770 -1 false c +4294967051 pg_stat_wal_receiver 1307062959 3233629770 -1 false c +4294967052 pg_stat_user_tables 1307062959 3233629770 -1 false c +4294967053 pg_stat_user_indexes 1307062959 3233629770 -1 false c +4294967054 pg_stat_user_functions 1307062959 3233629770 -1 false c +4294967055 pg_stat_sys_tables 1307062959 3233629770 -1 false c +4294967056 pg_stat_sys_indexes 1307062959 3233629770 -1 false c +4294967057 pg_stat_subscription 1307062959 3233629770 -1 false c +4294967058 pg_stat_ssl 1307062959 3233629770 -1 false c +4294967059 pg_stat_slru 1307062959 3233629770 -1 false c +4294967060 pg_stat_replication 1307062959 3233629770 -1 false c +4294967061 pg_stat_progress_vacuum 1307062959 3233629770 -1 false c +4294967062 pg_stat_progress_create_index 1307062959 3233629770 -1 false c +4294967063 pg_stat_progress_cluster 1307062959 3233629770 -1 false c +4294967064 pg_stat_progress_basebackup 1307062959 3233629770 -1 false c +4294967065 pg_stat_progress_analyze 1307062959 3233629770 -1 false c +4294967066 pg_stat_gssapi 1307062959 3233629770 -1 false c +4294967067 pg_stat_database 1307062959 3233629770 -1 false c +4294967068 pg_stat_database_conflicts 1307062959 3233629770 -1 false c +4294967069 pg_stat_bgwriter 1307062959 3233629770 -1 false c +4294967070 pg_stat_archiver 1307062959 3233629770 -1 false c +4294967071 pg_stat_all_tables 1307062959 3233629770 -1 false c +4294967072 pg_stat_all_indexes 1307062959 3233629770 -1 false c +4294967073 pg_stat_activity 1307062959 3233629770 -1 false c +4294967074 pg_shmem_allocations 1307062959 3233629770 -1 false c +4294967075 pg_shdepend 1307062959 3233629770 -1 false c +4294967076 pg_shseclabel 1307062959 3233629770 -1 false c +4294967077 pg_shdescription 1307062959 3233629770 -1 false c +4294967078 pg_shadow 1307062959 3233629770 -1 false c +4294967079 pg_settings 1307062959 3233629770 -1 false c +4294967080 pg_sequences 1307062959 3233629770 -1 false c +4294967081 pg_sequence 1307062959 3233629770 -1 false c +4294967082 pg_seclabel 1307062959 3233629770 -1 false c +4294967083 pg_seclabels 1307062959 3233629770 -1 false c +4294967084 pg_rules 1307062959 3233629770 -1 false c +4294967085 pg_roles 1307062959 3233629770 -1 false c +4294967086 pg_rewrite 1307062959 3233629770 -1 false c +4294967087 pg_replication_slots 1307062959 3233629770 -1 false c +4294967088 pg_replication_origin 1307062959 3233629770 -1 false c +4294967089 pg_replication_origin_status 1307062959 3233629770 -1 false c +4294967090 pg_range 1307062959 3233629770 -1 false c +4294967091 pg_publication_tables 1307062959 3233629770 -1 false c +4294967092 pg_publication 1307062959 3233629770 -1 false c +4294967093 pg_publication_rel 1307062959 3233629770 -1 false c +4294967094 pg_proc 1307062959 3233629770 -1 false c +4294967095 pg_prepared_xacts 1307062959 3233629770 -1 false c +4294967096 pg_prepared_statements 1307062959 3233629770 -1 false c +4294967097 pg_policy 1307062959 3233629770 -1 false c +4294967098 pg_policies 1307062959 3233629770 -1 false c +4294967099 pg_partitioned_table 1307062959 3233629770 -1 false c +4294967100 pg_opfamily 1307062959 3233629770 -1 false c +4294967101 pg_operator 1307062959 3233629770 -1 false c +4294967102 pg_opclass 1307062959 3233629770 -1 false c +4294967103 pg_namespace 1307062959 3233629770 -1 false c +4294967104 pg_matviews 1307062959 3233629770 -1 false c +4294967105 pg_locks 1307062959 3233629770 -1 false c +4294967106 pg_largeobject 1307062959 3233629770 -1 false c +4294967107 pg_largeobject_metadata 1307062959 3233629770 -1 false c +4294967108 pg_language 1307062959 3233629770 -1 false c +4294967109 pg_init_privs 1307062959 3233629770 -1 false c +4294967110 pg_inherits 1307062959 3233629770 -1 false c +4294967111 pg_indexes 1307062959 3233629770 -1 false c +4294967112 pg_index 1307062959 3233629770 -1 false c +4294967113 pg_hba_file_rules 1307062959 3233629770 -1 false c +4294967114 pg_group 1307062959 3233629770 -1 false c +4294967115 pg_foreign_table 1307062959 3233629770 -1 false c +4294967116 pg_foreign_server 1307062959 3233629770 -1 false c +4294967117 pg_foreign_data_wrapper 1307062959 3233629770 -1 false c +4294967118 pg_file_settings 1307062959 3233629770 -1 false c +4294967119 pg_extension 1307062959 3233629770 -1 false c +4294967120 pg_event_trigger 1307062959 3233629770 -1 false c +4294967121 pg_enum 1307062959 3233629770 -1 false c +4294967122 pg_description 1307062959 3233629770 -1 false c +4294967123 pg_depend 1307062959 3233629770 -1 false c +4294967124 pg_default_acl 1307062959 3233629770 -1 false c +4294967125 pg_db_role_setting 1307062959 3233629770 -1 false c +4294967126 pg_database 1307062959 3233629770 -1 false c +4294967127 pg_cursors 1307062959 3233629770 -1 false c +4294967128 pg_conversion 1307062959 3233629770 -1 false c +4294967129 pg_constraint 1307062959 3233629770 -1 false c +4294967130 pg_config 1307062959 3233629770 -1 false c +4294967131 pg_collation 1307062959 3233629770 -1 false c +4294967132 pg_class 1307062959 3233629770 -1 false c +4294967133 pg_cast 1307062959 3233629770 -1 false c +4294967134 pg_available_extensions 1307062959 3233629770 -1 false c +4294967135 pg_available_extension_versions 1307062959 3233629770 -1 false c +4294967136 pg_auth_members 1307062959 3233629770 -1 false c +4294967137 pg_authid 1307062959 3233629770 -1 false c +4294967138 pg_attribute 1307062959 3233629770 -1 false c +4294967139 pg_attrdef 1307062959 3233629770 -1 false c +4294967140 pg_amproc 1307062959 3233629770 -1 false c +4294967141 pg_amop 1307062959 3233629770 -1 false c +4294967142 pg_am 1307062959 3233629770 -1 false c +4294967143 pg_aggregate 1307062959 3233629770 -1 false c +4294967145 views 359535012 3233629770 -1 false c +4294967146 view_table_usage 359535012 3233629770 -1 false c +4294967147 view_routine_usage 359535012 3233629770 -1 false c +4294967148 view_column_usage 359535012 3233629770 -1 false c +4294967149 user_privileges 359535012 3233629770 -1 false c +4294967150 user_mappings 359535012 3233629770 -1 false c +4294967151 user_mapping_options 359535012 3233629770 -1 false c +4294967152 user_defined_types 359535012 3233629770 -1 false c +4294967153 user_attributes 359535012 3233629770 -1 false c +4294967154 usage_privileges 359535012 3233629770 -1 false c +4294967155 udt_privileges 359535012 3233629770 -1 false c +4294967156 type_privileges 359535012 3233629770 -1 false c +4294967157 triggers 359535012 3233629770 -1 false c +4294967158 triggered_update_columns 359535012 3233629770 -1 false c +4294967159 transforms 359535012 3233629770 -1 false c +4294967160 tablespaces 359535012 3233629770 -1 false c +4294967161 tablespaces_extensions 359535012 3233629770 -1 false c +4294967162 tables 359535012 3233629770 -1 false c +4294967163 tables_extensions 359535012 3233629770 -1 false c +4294967164 table_privileges 359535012 3233629770 -1 false c +4294967165 table_constraints_extensions 359535012 3233629770 -1 false c +4294967166 table_constraints 359535012 3233629770 -1 false c +4294967167 statistics 359535012 3233629770 -1 false c +4294967168 st_units_of_measure 359535012 3233629770 -1 false c +4294967169 st_spatial_reference_systems 359535012 3233629770 -1 false c +4294967170 st_geometry_columns 359535012 3233629770 -1 false c +4294967171 session_variables 359535012 3233629770 -1 false c +4294967172 sequences 359535012 3233629770 -1 false c +4294967173 schema_privileges 359535012 3233629770 -1 false c +4294967174 schemata 359535012 3233629770 -1 false c +4294967175 schemata_extensions 359535012 3233629770 -1 false c +4294967176 sql_sizing 359535012 3233629770 -1 false c +4294967177 sql_parts 359535012 3233629770 -1 false c +4294967178 sql_implementation_info 359535012 3233629770 -1 false c +4294967179 sql_features 359535012 3233629770 -1 false c +4294967180 routines 359535012 3233629770 -1 false c +4294967181 routine_privileges 359535012 3233629770 -1 false c +4294967182 role_usage_grants 359535012 3233629770 -1 false c +4294967183 role_udt_grants 359535012 3233629770 -1 false c +4294967184 role_table_grants 359535012 3233629770 -1 false c +4294967185 role_routine_grants 359535012 3233629770 -1 false c +4294967186 role_column_grants 359535012 3233629770 -1 false c +4294967187 resource_groups 359535012 3233629770 -1 false c +4294967188 referential_constraints 359535012 3233629770 -1 false c +4294967189 profiling 359535012 3233629770 -1 false c +4294967190 processlist 359535012 3233629770 -1 false c +4294967191 plugins 359535012 3233629770 -1 false c +4294967192 partitions 359535012 3233629770 -1 false c +4294967193 parameters 359535012 3233629770 -1 false c +4294967194 optimizer_trace 359535012 3233629770 -1 false c +4294967195 keywords 359535012 3233629770 -1 false c +4294967196 key_column_usage 359535012 3233629770 -1 false c +4294967197 information_schema_catalog_name 359535012 3233629770 -1 false c +4294967198 foreign_tables 359535012 3233629770 -1 false c +4294967199 foreign_table_options 359535012 3233629770 -1 false c +4294967200 foreign_servers 359535012 3233629770 -1 false c +4294967201 foreign_server_options 359535012 3233629770 -1 false c +4294967202 foreign_data_wrappers 359535012 3233629770 -1 false c +4294967203 foreign_data_wrapper_options 359535012 3233629770 -1 false c +4294967204 files 359535012 3233629770 -1 false c +4294967205 events 359535012 3233629770 -1 false c +4294967206 engines 359535012 3233629770 -1 false c +4294967207 enabled_roles 359535012 3233629770 -1 false c +4294967208 element_types 359535012 3233629770 -1 false c +4294967209 domains 359535012 3233629770 -1 false c +4294967210 domain_udt_usage 359535012 3233629770 -1 false c +4294967211 domain_constraints 359535012 3233629770 -1 false c +4294967212 data_type_privileges 359535012 3233629770 -1 false c +4294967213 constraint_table_usage 359535012 3233629770 -1 false c +4294967214 constraint_column_usage 359535012 3233629770 -1 false c +4294967215 columns 359535012 3233629770 -1 false c +4294967216 columns_extensions 359535012 3233629770 -1 false c +4294967217 column_udt_usage 359535012 3233629770 -1 false c +4294967218 column_statistics 359535012 3233629770 -1 false c +4294967219 column_privileges 359535012 3233629770 -1 false c +4294967220 column_options 359535012 3233629770 -1 false c +4294967221 column_domain_usage 359535012 3233629770 -1 false c +4294967222 column_column_usage 359535012 3233629770 -1 false c +4294967223 collations 359535012 3233629770 -1 false c +4294967224 collation_character_set_applicability 359535012 3233629770 -1 false c +4294967225 check_constraints 359535012 3233629770 -1 false c +4294967226 check_constraint_routine_usage 359535012 3233629770 -1 false c +4294967227 character_sets 359535012 3233629770 -1 false c +4294967228 attributes 359535012 3233629770 -1 false c +4294967229 applicable_roles 359535012 3233629770 -1 false c +4294967230 administrable_role_authorizations 359535012 3233629770 -1 false c +4294967232 tenant_usage_details 1146641803 3233629770 -1 false c +4294967233 active_range_feeds 1146641803 3233629770 -1 false c +4294967234 default_privileges 1146641803 3233629770 -1 false c +4294967235 regions 1146641803 3233629770 -1 false c +4294967236 cluster_inflight_traces 1146641803 3233629770 -1 false c +4294967237 lost_descriptors_with_data 1146641803 3233629770 -1 false c +4294967238 cross_db_references 1146641803 3233629770 -1 false c 4294967239 cluster_database_privileges 1146641803 3233629770 -1 false c 4294967240 invalid_objects 1146641803 3233629770 -1 false c 4294967241 zones 1146641803 3233629770 -1 false c @@ -2006,232 +2005,231 @@ oid typname typcategory typispreferred 100076 _newtype1 A false true , 0 100075 0 100077 newtype2 E false true , 0 0 100078 100078 _newtype2 A false true , 0 100077 0 -4294967010 spatial_ref_sys C false true , 4294967010 0 0 -4294967011 geometry_columns C false true , 4294967011 0 0 -4294967012 geography_columns C false true , 4294967012 0 0 -4294967014 pg_views C false true , 4294967014 0 0 -4294967015 pg_user C false true , 4294967015 0 0 -4294967016 pg_user_mappings C false true , 4294967016 0 0 -4294967017 pg_user_mapping C false true , 4294967017 0 0 -4294967018 pg_type C false true , 4294967018 0 0 -4294967019 pg_ts_template C false true , 4294967019 0 0 -4294967020 pg_ts_parser C false true , 4294967020 0 0 -4294967021 pg_ts_dict C false true , 4294967021 0 0 -4294967022 pg_ts_config C false true , 4294967022 0 0 -4294967023 pg_ts_config_map C false true , 4294967023 0 0 -4294967024 pg_trigger C false true , 4294967024 0 0 -4294967025 pg_transform C false true , 4294967025 0 0 -4294967026 pg_timezone_names C false true , 4294967026 0 0 -4294967027 pg_timezone_abbrevs C false true , 4294967027 0 0 -4294967028 pg_tablespace C false true , 4294967028 0 0 -4294967029 pg_tables C false true , 4294967029 0 0 -4294967030 pg_subscription C false true , 4294967030 0 0 -4294967031 pg_subscription_rel C false true , 4294967031 0 0 -4294967032 pg_stats C false true , 4294967032 0 0 -4294967033 pg_stats_ext C false true , 4294967033 0 0 -4294967034 pg_statistic C false true , 4294967034 0 0 -4294967035 pg_statistic_ext C false true , 4294967035 0 0 -4294967036 pg_statistic_ext_data C false true , 4294967036 0 0 -4294967037 pg_statio_user_tables C false true , 4294967037 0 0 -4294967038 pg_statio_user_sequences C false true , 4294967038 0 0 -4294967039 pg_statio_user_indexes C false true , 4294967039 0 0 -4294967040 pg_statio_sys_tables C false true , 4294967040 0 0 -4294967041 pg_statio_sys_sequences C false true , 4294967041 0 0 -4294967042 pg_statio_sys_indexes C false true , 4294967042 0 0 -4294967043 pg_statio_all_tables C false true , 4294967043 0 0 -4294967044 pg_statio_all_sequences C false true , 4294967044 0 0 -4294967045 pg_statio_all_indexes C false true , 4294967045 0 0 -4294967046 pg_stat_xact_user_tables C false true , 4294967046 0 0 -4294967047 pg_stat_xact_user_functions C false true , 4294967047 0 0 -4294967048 pg_stat_xact_sys_tables C false true , 4294967048 0 0 -4294967049 pg_stat_xact_all_tables C false true , 4294967049 0 0 -4294967050 pg_stat_wal_receiver C false true , 4294967050 0 0 -4294967051 pg_stat_user_tables C false true , 4294967051 0 0 -4294967052 pg_stat_user_indexes C false true , 4294967052 0 0 -4294967053 pg_stat_user_functions C false true , 4294967053 0 0 -4294967054 pg_stat_sys_tables C false true , 4294967054 0 0 -4294967055 pg_stat_sys_indexes C false true , 4294967055 0 0 -4294967056 pg_stat_subscription C false true , 4294967056 0 0 -4294967057 pg_stat_ssl C false true , 4294967057 0 0 -4294967058 pg_stat_slru C false true , 4294967058 0 0 -4294967059 pg_stat_replication C false true , 4294967059 0 0 -4294967060 pg_stat_progress_vacuum C false true , 4294967060 0 0 -4294967061 pg_stat_progress_create_index C false true , 4294967061 0 0 -4294967062 pg_stat_progress_cluster C false true , 4294967062 0 0 -4294967063 pg_stat_progress_basebackup C false true , 4294967063 0 0 -4294967064 pg_stat_progress_analyze C false true , 4294967064 0 0 -4294967065 pg_stat_gssapi C false true , 4294967065 0 0 -4294967066 pg_stat_database C false true , 4294967066 0 0 -4294967067 pg_stat_database_conflicts C false true , 4294967067 0 0 -4294967068 pg_stat_bgwriter C false true , 4294967068 0 0 -4294967069 pg_stat_archiver C false true , 4294967069 0 0 -4294967070 pg_stat_all_tables C false true , 4294967070 0 0 -4294967071 pg_stat_all_indexes C false true , 4294967071 0 0 -4294967072 pg_stat_activity C false true , 4294967072 0 0 -4294967073 pg_shmem_allocations C false true , 4294967073 0 0 -4294967074 pg_shdepend C false true , 4294967074 0 0 -4294967075 pg_shseclabel C false true , 4294967075 0 0 -4294967076 pg_shdescription C false true , 4294967076 0 0 -4294967077 pg_shadow C false true , 4294967077 0 0 -4294967078 pg_settings C false true , 4294967078 0 0 -4294967079 pg_sequences C false true , 4294967079 0 0 -4294967080 pg_sequence C false true , 4294967080 0 0 -4294967081 pg_seclabel C false true , 4294967081 0 0 -4294967082 pg_seclabels C false true , 4294967082 0 0 -4294967083 pg_rules C false true , 4294967083 0 0 -4294967084 pg_roles C false true , 4294967084 0 0 -4294967085 pg_rewrite C false true , 4294967085 0 0 -4294967086 pg_replication_slots C false true , 4294967086 0 0 -4294967087 pg_replication_origin C false true , 4294967087 0 0 -4294967088 pg_replication_origin_status C false true , 4294967088 0 0 -4294967089 pg_range C false true , 4294967089 0 0 -4294967090 pg_publication_tables C false true , 4294967090 0 0 -4294967091 pg_publication C false true , 4294967091 0 0 -4294967092 pg_publication_rel C false true , 4294967092 0 0 -4294967093 pg_proc C false true , 4294967093 0 0 -4294967094 pg_prepared_xacts C false true , 4294967094 0 0 -4294967095 pg_prepared_statements C false true , 4294967095 0 0 -4294967096 pg_policy C false true , 4294967096 0 0 -4294967097 pg_policies C false true , 4294967097 0 0 -4294967098 pg_partitioned_table C false true , 4294967098 0 0 -4294967099 pg_opfamily C false true , 4294967099 0 0 -4294967100 pg_operator C false true , 4294967100 0 0 -4294967101 pg_opclass C false true , 4294967101 0 0 -4294967102 pg_namespace C false true , 4294967102 0 0 -4294967103 pg_matviews C false true , 4294967103 0 0 -4294967104 pg_locks C false true , 4294967104 0 0 -4294967105 pg_largeobject C false true , 4294967105 0 0 -4294967106 pg_largeobject_metadata C false true , 4294967106 0 0 -4294967107 pg_language C false true , 4294967107 0 0 -4294967108 pg_init_privs C false true , 4294967108 0 0 -4294967109 pg_inherits C false true , 4294967109 0 0 -4294967110 pg_indexes C false true , 4294967110 0 0 -4294967111 pg_index C false true , 4294967111 0 0 -4294967112 pg_hba_file_rules C false true , 4294967112 0 0 -4294967113 pg_group C false true , 4294967113 0 0 -4294967114 pg_foreign_table C false true , 4294967114 0 0 -4294967115 pg_foreign_server C false true , 4294967115 0 0 -4294967116 pg_foreign_data_wrapper C false true , 4294967116 0 0 -4294967117 pg_file_settings C false true , 4294967117 0 0 -4294967118 pg_extension C false true , 4294967118 0 0 -4294967119 pg_event_trigger C false true , 4294967119 0 0 -4294967120 pg_enum C false true , 4294967120 0 0 -4294967121 pg_description C false true , 4294967121 0 0 -4294967122 pg_depend C false true , 4294967122 0 0 -4294967123 pg_default_acl C false true , 4294967123 0 0 -4294967124 pg_db_role_setting C false true , 4294967124 0 0 -4294967125 pg_database C false true , 4294967125 0 0 -4294967126 pg_cursors C false true , 4294967126 0 0 -4294967127 pg_conversion C false true , 4294967127 0 0 -4294967128 pg_constraint C false true , 4294967128 0 0 -4294967129 pg_config C false true , 4294967129 0 0 -4294967130 pg_collation C false true , 4294967130 0 0 -4294967131 pg_class C false true , 4294967131 0 0 -4294967132 pg_cast C false true , 4294967132 0 0 -4294967133 pg_available_extensions C false true , 4294967133 0 0 -4294967134 pg_available_extension_versions C false true , 4294967134 0 0 -4294967135 pg_auth_members C false true , 4294967135 0 0 -4294967136 pg_authid C false true , 4294967136 0 0 -4294967137 pg_attribute C false true , 4294967137 0 0 -4294967138 pg_attrdef C false true , 4294967138 0 0 -4294967139 pg_amproc C false true , 4294967139 0 0 -4294967140 pg_amop C false true , 4294967140 0 0 -4294967141 pg_am C false true , 4294967141 0 0 -4294967142 pg_aggregate C false true , 4294967142 0 0 -4294967144 views C false true , 4294967144 0 0 -4294967145 view_table_usage C false true , 4294967145 0 0 -4294967146 view_routine_usage C false true , 4294967146 0 0 -4294967147 view_column_usage C false true , 4294967147 0 0 -4294967148 user_privileges C false true , 4294967148 0 0 -4294967149 user_mappings C false true , 4294967149 0 0 -4294967150 user_mapping_options C false true , 4294967150 0 0 -4294967151 user_defined_types C false true , 4294967151 0 0 -4294967152 user_attributes C false true , 4294967152 0 0 -4294967153 usage_privileges C false true , 4294967153 0 0 -4294967154 udt_privileges C false true , 4294967154 0 0 -4294967155 type_privileges C false true , 4294967155 0 0 -4294967156 triggers C false true , 4294967156 0 0 -4294967157 triggered_update_columns C false true , 4294967157 0 0 -4294967158 transforms C false true , 4294967158 0 0 -4294967159 tablespaces C false true , 4294967159 0 0 -4294967160 tablespaces_extensions C false true , 4294967160 0 0 -4294967161 tables C false true , 4294967161 0 0 -4294967162 tables_extensions C false true , 4294967162 0 0 -4294967163 table_privileges C false true , 4294967163 0 0 -4294967164 table_constraints_extensions C false true , 4294967164 0 0 -4294967165 table_constraints C false true , 4294967165 0 0 -4294967166 statistics C false true , 4294967166 0 0 -4294967167 st_units_of_measure C false true , 4294967167 0 0 -4294967168 st_spatial_reference_systems C false true , 4294967168 0 0 -4294967169 st_geometry_columns C false true , 4294967169 0 0 -4294967170 session_variables C false true , 4294967170 0 0 -4294967171 sequences C false true , 4294967171 0 0 -4294967172 schema_privileges C false true , 4294967172 0 0 -4294967173 schemata C false true , 4294967173 0 0 -4294967174 schemata_extensions C false true , 4294967174 0 0 -4294967175 sql_sizing C false true , 4294967175 0 0 -4294967176 sql_parts C false true , 4294967176 0 0 -4294967177 sql_implementation_info C false true , 4294967177 0 0 -4294967178 sql_features C false true , 4294967178 0 0 -4294967179 routines C false true , 4294967179 0 0 -4294967180 routine_privileges C false true , 4294967180 0 0 -4294967181 role_usage_grants C false true , 4294967181 0 0 -4294967182 role_udt_grants C false true , 4294967182 0 0 -4294967183 role_table_grants C false true , 4294967183 0 0 -4294967184 role_routine_grants C false true , 4294967184 0 0 -4294967185 role_column_grants C false true , 4294967185 0 0 -4294967186 resource_groups C false true , 4294967186 0 0 -4294967187 referential_constraints C false true , 4294967187 0 0 -4294967188 profiling C false true , 4294967188 0 0 -4294967189 processlist C false true , 4294967189 0 0 -4294967190 plugins C false true , 4294967190 0 0 -4294967191 partitions C false true , 4294967191 0 0 -4294967192 parameters C false true , 4294967192 0 0 -4294967193 optimizer_trace C false true , 4294967193 0 0 -4294967194 keywords C false true , 4294967194 0 0 -4294967195 key_column_usage C false true , 4294967195 0 0 -4294967196 information_schema_catalog_name C false true , 4294967196 0 0 -4294967197 foreign_tables C false true , 4294967197 0 0 -4294967198 foreign_table_options C false true , 4294967198 0 0 -4294967199 foreign_servers C false true , 4294967199 0 0 -4294967200 foreign_server_options C false true , 4294967200 0 0 -4294967201 foreign_data_wrappers C false true , 4294967201 0 0 -4294967202 foreign_data_wrapper_options C false true , 4294967202 0 0 -4294967203 files C false true , 4294967203 0 0 -4294967204 events C false true , 4294967204 0 0 -4294967205 engines C false true , 4294967205 0 0 -4294967206 enabled_roles C false true , 4294967206 0 0 -4294967207 element_types C false true , 4294967207 0 0 -4294967208 domains C false true , 4294967208 0 0 -4294967209 domain_udt_usage C false true , 4294967209 0 0 -4294967210 domain_constraints C false true , 4294967210 0 0 -4294967211 data_type_privileges C false true , 4294967211 0 0 -4294967212 constraint_table_usage C false true , 4294967212 0 0 -4294967213 constraint_column_usage C false true , 4294967213 0 0 -4294967214 columns C false true , 4294967214 0 0 -4294967215 columns_extensions C false true , 4294967215 0 0 -4294967216 column_udt_usage C false true , 4294967216 0 0 -4294967217 column_statistics C false true , 4294967217 0 0 -4294967218 column_privileges C false true , 4294967218 0 0 -4294967219 column_options C false true , 4294967219 0 0 -4294967220 column_domain_usage C false true , 4294967220 0 0 -4294967221 column_column_usage C false true , 4294967221 0 0 -4294967222 collations C false true , 4294967222 0 0 -4294967223 collation_character_set_applicability C false true , 4294967223 0 0 -4294967224 check_constraints C false true , 4294967224 0 0 -4294967225 check_constraint_routine_usage C false true , 4294967225 0 0 -4294967226 character_sets C false true , 4294967226 0 0 -4294967227 attributes C false true , 4294967227 0 0 -4294967228 applicable_roles C false true , 4294967228 0 0 -4294967229 administrable_role_authorizations C false true , 4294967229 0 0 -4294967231 tenant_usage_details C false true , 4294967231 0 0 -4294967232 active_range_feeds C false true , 4294967232 0 0 -4294967233 default_privileges C false true , 4294967233 0 0 -4294967234 regions C false true , 4294967234 0 0 -4294967235 cluster_inflight_traces C false true , 4294967235 0 0 -4294967236 lost_descriptors_with_data C false true , 4294967236 0 0 -4294967237 cross_db_references C false true , 4294967237 0 0 -4294967238 interleaved C false true , 4294967238 0 0 +4294967011 spatial_ref_sys C false true , 4294967011 0 0 +4294967012 geometry_columns C false true , 4294967012 0 0 +4294967013 geography_columns C false true , 4294967013 0 0 +4294967015 pg_views C false true , 4294967015 0 0 +4294967016 pg_user C false true , 4294967016 0 0 +4294967017 pg_user_mappings C false true , 4294967017 0 0 +4294967018 pg_user_mapping C false true , 4294967018 0 0 +4294967019 pg_type C false true , 4294967019 0 0 +4294967020 pg_ts_template C false true , 4294967020 0 0 +4294967021 pg_ts_parser C false true , 4294967021 0 0 +4294967022 pg_ts_dict C false true , 4294967022 0 0 +4294967023 pg_ts_config C false true , 4294967023 0 0 +4294967024 pg_ts_config_map C false true , 4294967024 0 0 +4294967025 pg_trigger C false true , 4294967025 0 0 +4294967026 pg_transform C false true , 4294967026 0 0 +4294967027 pg_timezone_names C false true , 4294967027 0 0 +4294967028 pg_timezone_abbrevs C false true , 4294967028 0 0 +4294967029 pg_tablespace C false true , 4294967029 0 0 +4294967030 pg_tables C false true , 4294967030 0 0 +4294967031 pg_subscription C false true , 4294967031 0 0 +4294967032 pg_subscription_rel C false true , 4294967032 0 0 +4294967033 pg_stats C false true , 4294967033 0 0 +4294967034 pg_stats_ext C false true , 4294967034 0 0 +4294967035 pg_statistic C false true , 4294967035 0 0 +4294967036 pg_statistic_ext C false true , 4294967036 0 0 +4294967037 pg_statistic_ext_data C false true , 4294967037 0 0 +4294967038 pg_statio_user_tables C false true , 4294967038 0 0 +4294967039 pg_statio_user_sequences C false true , 4294967039 0 0 +4294967040 pg_statio_user_indexes C false true , 4294967040 0 0 +4294967041 pg_statio_sys_tables C false true , 4294967041 0 0 +4294967042 pg_statio_sys_sequences C false true , 4294967042 0 0 +4294967043 pg_statio_sys_indexes C false true , 4294967043 0 0 +4294967044 pg_statio_all_tables C false true , 4294967044 0 0 +4294967045 pg_statio_all_sequences C false true , 4294967045 0 0 +4294967046 pg_statio_all_indexes C false true , 4294967046 0 0 +4294967047 pg_stat_xact_user_tables C false true , 4294967047 0 0 +4294967048 pg_stat_xact_user_functions C false true , 4294967048 0 0 +4294967049 pg_stat_xact_sys_tables C false true , 4294967049 0 0 +4294967050 pg_stat_xact_all_tables C false true , 4294967050 0 0 +4294967051 pg_stat_wal_receiver C false true , 4294967051 0 0 +4294967052 pg_stat_user_tables C false true , 4294967052 0 0 +4294967053 pg_stat_user_indexes C false true , 4294967053 0 0 +4294967054 pg_stat_user_functions C false true , 4294967054 0 0 +4294967055 pg_stat_sys_tables C false true , 4294967055 0 0 +4294967056 pg_stat_sys_indexes C false true , 4294967056 0 0 +4294967057 pg_stat_subscription C false true , 4294967057 0 0 +4294967058 pg_stat_ssl C false true , 4294967058 0 0 +4294967059 pg_stat_slru C false true , 4294967059 0 0 +4294967060 pg_stat_replication C false true , 4294967060 0 0 +4294967061 pg_stat_progress_vacuum C false true , 4294967061 0 0 +4294967062 pg_stat_progress_create_index C false true , 4294967062 0 0 +4294967063 pg_stat_progress_cluster C false true , 4294967063 0 0 +4294967064 pg_stat_progress_basebackup C false true , 4294967064 0 0 +4294967065 pg_stat_progress_analyze C false true , 4294967065 0 0 +4294967066 pg_stat_gssapi C false true , 4294967066 0 0 +4294967067 pg_stat_database C false true , 4294967067 0 0 +4294967068 pg_stat_database_conflicts C false true , 4294967068 0 0 +4294967069 pg_stat_bgwriter C false true , 4294967069 0 0 +4294967070 pg_stat_archiver C false true , 4294967070 0 0 +4294967071 pg_stat_all_tables C false true , 4294967071 0 0 +4294967072 pg_stat_all_indexes C false true , 4294967072 0 0 +4294967073 pg_stat_activity C false true , 4294967073 0 0 +4294967074 pg_shmem_allocations C false true , 4294967074 0 0 +4294967075 pg_shdepend C false true , 4294967075 0 0 +4294967076 pg_shseclabel C false true , 4294967076 0 0 +4294967077 pg_shdescription C false true , 4294967077 0 0 +4294967078 pg_shadow C false true , 4294967078 0 0 +4294967079 pg_settings C false true , 4294967079 0 0 +4294967080 pg_sequences C false true , 4294967080 0 0 +4294967081 pg_sequence C false true , 4294967081 0 0 +4294967082 pg_seclabel C false true , 4294967082 0 0 +4294967083 pg_seclabels C false true , 4294967083 0 0 +4294967084 pg_rules C false true , 4294967084 0 0 +4294967085 pg_roles C false true , 4294967085 0 0 +4294967086 pg_rewrite C false true , 4294967086 0 0 +4294967087 pg_replication_slots C false true , 4294967087 0 0 +4294967088 pg_replication_origin C false true , 4294967088 0 0 +4294967089 pg_replication_origin_status C false true , 4294967089 0 0 +4294967090 pg_range C false true , 4294967090 0 0 +4294967091 pg_publication_tables C false true , 4294967091 0 0 +4294967092 pg_publication C false true , 4294967092 0 0 +4294967093 pg_publication_rel C false true , 4294967093 0 0 +4294967094 pg_proc C false true , 4294967094 0 0 +4294967095 pg_prepared_xacts C false true , 4294967095 0 0 +4294967096 pg_prepared_statements C false true , 4294967096 0 0 +4294967097 pg_policy C false true , 4294967097 0 0 +4294967098 pg_policies C false true , 4294967098 0 0 +4294967099 pg_partitioned_table C false true , 4294967099 0 0 +4294967100 pg_opfamily C false true , 4294967100 0 0 +4294967101 pg_operator C false true , 4294967101 0 0 +4294967102 pg_opclass C false true , 4294967102 0 0 +4294967103 pg_namespace C false true , 4294967103 0 0 +4294967104 pg_matviews C false true , 4294967104 0 0 +4294967105 pg_locks C false true , 4294967105 0 0 +4294967106 pg_largeobject C false true , 4294967106 0 0 +4294967107 pg_largeobject_metadata C false true , 4294967107 0 0 +4294967108 pg_language C false true , 4294967108 0 0 +4294967109 pg_init_privs C false true , 4294967109 0 0 +4294967110 pg_inherits C false true , 4294967110 0 0 +4294967111 pg_indexes C false true , 4294967111 0 0 +4294967112 pg_index C false true , 4294967112 0 0 +4294967113 pg_hba_file_rules C false true , 4294967113 0 0 +4294967114 pg_group C false true , 4294967114 0 0 +4294967115 pg_foreign_table C false true , 4294967115 0 0 +4294967116 pg_foreign_server C false true , 4294967116 0 0 +4294967117 pg_foreign_data_wrapper C false true , 4294967117 0 0 +4294967118 pg_file_settings C false true , 4294967118 0 0 +4294967119 pg_extension C false true , 4294967119 0 0 +4294967120 pg_event_trigger C false true , 4294967120 0 0 +4294967121 pg_enum C false true , 4294967121 0 0 +4294967122 pg_description C false true , 4294967122 0 0 +4294967123 pg_depend C false true , 4294967123 0 0 +4294967124 pg_default_acl C false true , 4294967124 0 0 +4294967125 pg_db_role_setting C false true , 4294967125 0 0 +4294967126 pg_database C false true , 4294967126 0 0 +4294967127 pg_cursors C false true , 4294967127 0 0 +4294967128 pg_conversion C false true , 4294967128 0 0 +4294967129 pg_constraint C false true , 4294967129 0 0 +4294967130 pg_config C false true , 4294967130 0 0 +4294967131 pg_collation C false true , 4294967131 0 0 +4294967132 pg_class C false true , 4294967132 0 0 +4294967133 pg_cast C false true , 4294967133 0 0 +4294967134 pg_available_extensions C false true , 4294967134 0 0 +4294967135 pg_available_extension_versions C false true , 4294967135 0 0 +4294967136 pg_auth_members C false true , 4294967136 0 0 +4294967137 pg_authid C false true , 4294967137 0 0 +4294967138 pg_attribute C false true , 4294967138 0 0 +4294967139 pg_attrdef C false true , 4294967139 0 0 +4294967140 pg_amproc C false true , 4294967140 0 0 +4294967141 pg_amop C false true , 4294967141 0 0 +4294967142 pg_am C false true , 4294967142 0 0 +4294967143 pg_aggregate C false true , 4294967143 0 0 +4294967145 views C false true , 4294967145 0 0 +4294967146 view_table_usage C false true , 4294967146 0 0 +4294967147 view_routine_usage C false true , 4294967147 0 0 +4294967148 view_column_usage C false true , 4294967148 0 0 +4294967149 user_privileges C false true , 4294967149 0 0 +4294967150 user_mappings C false true , 4294967150 0 0 +4294967151 user_mapping_options C false true , 4294967151 0 0 +4294967152 user_defined_types C false true , 4294967152 0 0 +4294967153 user_attributes C false true , 4294967153 0 0 +4294967154 usage_privileges C false true , 4294967154 0 0 +4294967155 udt_privileges C false true , 4294967155 0 0 +4294967156 type_privileges C false true , 4294967156 0 0 +4294967157 triggers C false true , 4294967157 0 0 +4294967158 triggered_update_columns C false true , 4294967158 0 0 +4294967159 transforms C false true , 4294967159 0 0 +4294967160 tablespaces C false true , 4294967160 0 0 +4294967161 tablespaces_extensions C false true , 4294967161 0 0 +4294967162 tables C false true , 4294967162 0 0 +4294967163 tables_extensions C false true , 4294967163 0 0 +4294967164 table_privileges C false true , 4294967164 0 0 +4294967165 table_constraints_extensions C false true , 4294967165 0 0 +4294967166 table_constraints C false true , 4294967166 0 0 +4294967167 statistics C false true , 4294967167 0 0 +4294967168 st_units_of_measure C false true , 4294967168 0 0 +4294967169 st_spatial_reference_systems C false true , 4294967169 0 0 +4294967170 st_geometry_columns C false true , 4294967170 0 0 +4294967171 session_variables C false true , 4294967171 0 0 +4294967172 sequences C false true , 4294967172 0 0 +4294967173 schema_privileges C false true , 4294967173 0 0 +4294967174 schemata C false true , 4294967174 0 0 +4294967175 schemata_extensions C false true , 4294967175 0 0 +4294967176 sql_sizing C false true , 4294967176 0 0 +4294967177 sql_parts C false true , 4294967177 0 0 +4294967178 sql_implementation_info C false true , 4294967178 0 0 +4294967179 sql_features C false true , 4294967179 0 0 +4294967180 routines C false true , 4294967180 0 0 +4294967181 routine_privileges C false true , 4294967181 0 0 +4294967182 role_usage_grants C false true , 4294967182 0 0 +4294967183 role_udt_grants C false true , 4294967183 0 0 +4294967184 role_table_grants C false true , 4294967184 0 0 +4294967185 role_routine_grants C false true , 4294967185 0 0 +4294967186 role_column_grants C false true , 4294967186 0 0 +4294967187 resource_groups C false true , 4294967187 0 0 +4294967188 referential_constraints C false true , 4294967188 0 0 +4294967189 profiling C false true , 4294967189 0 0 +4294967190 processlist C false true , 4294967190 0 0 +4294967191 plugins C false true , 4294967191 0 0 +4294967192 partitions C false true , 4294967192 0 0 +4294967193 parameters C false true , 4294967193 0 0 +4294967194 optimizer_trace C false true , 4294967194 0 0 +4294967195 keywords C false true , 4294967195 0 0 +4294967196 key_column_usage C false true , 4294967196 0 0 +4294967197 information_schema_catalog_name C false true , 4294967197 0 0 +4294967198 foreign_tables C false true , 4294967198 0 0 +4294967199 foreign_table_options C false true , 4294967199 0 0 +4294967200 foreign_servers C false true , 4294967200 0 0 +4294967201 foreign_server_options C false true , 4294967201 0 0 +4294967202 foreign_data_wrappers C false true , 4294967202 0 0 +4294967203 foreign_data_wrapper_options C false true , 4294967203 0 0 +4294967204 files C false true , 4294967204 0 0 +4294967205 events C false true , 4294967205 0 0 +4294967206 engines C false true , 4294967206 0 0 +4294967207 enabled_roles C false true , 4294967207 0 0 +4294967208 element_types C false true , 4294967208 0 0 +4294967209 domains C false true , 4294967209 0 0 +4294967210 domain_udt_usage C false true , 4294967210 0 0 +4294967211 domain_constraints C false true , 4294967211 0 0 +4294967212 data_type_privileges C false true , 4294967212 0 0 +4294967213 constraint_table_usage C false true , 4294967213 0 0 +4294967214 constraint_column_usage C false true , 4294967214 0 0 +4294967215 columns C false true , 4294967215 0 0 +4294967216 columns_extensions C false true , 4294967216 0 0 +4294967217 column_udt_usage C false true , 4294967217 0 0 +4294967218 column_statistics C false true , 4294967218 0 0 +4294967219 column_privileges C false true , 4294967219 0 0 +4294967220 column_options C false true , 4294967220 0 0 +4294967221 column_domain_usage C false true , 4294967221 0 0 +4294967222 column_column_usage C false true , 4294967222 0 0 +4294967223 collations C false true , 4294967223 0 0 +4294967224 collation_character_set_applicability C false true , 4294967224 0 0 +4294967225 check_constraints C false true , 4294967225 0 0 +4294967226 check_constraint_routine_usage C false true , 4294967226 0 0 +4294967227 character_sets C false true , 4294967227 0 0 +4294967228 attributes C false true , 4294967228 0 0 +4294967229 applicable_roles C false true , 4294967229 0 0 +4294967230 administrable_role_authorizations C false true , 4294967230 0 0 +4294967232 tenant_usage_details C false true , 4294967232 0 0 +4294967233 active_range_feeds C false true , 4294967233 0 0 +4294967234 default_privileges C false true , 4294967234 0 0 +4294967235 regions C false true , 4294967235 0 0 +4294967236 cluster_inflight_traces C false true , 4294967236 0 0 +4294967237 lost_descriptors_with_data C false true , 4294967237 0 0 +4294967238 cross_db_references C false true , 4294967238 0 0 4294967239 cluster_database_privileges C false true , 4294967239 0 0 4294967240 invalid_objects C false true , 4294967240 0 0 4294967241 zones C false true , 4294967241 0 0 @@ -2391,232 +2389,231 @@ oid typname typinput typoutput 100076 _newtype1 array_in array_out array_recv array_send 0 0 0 100077 newtype2 enum_in enum_out enum_recv enum_send 0 0 0 100078 _newtype2 array_in array_out array_recv array_send 0 0 0 -4294967010 spatial_ref_sys record_in record_out record_recv record_send 0 0 0 -4294967011 geometry_columns record_in record_out record_recv record_send 0 0 0 -4294967012 geography_columns record_in record_out record_recv record_send 0 0 0 -4294967014 pg_views record_in record_out record_recv record_send 0 0 0 -4294967015 pg_user record_in record_out record_recv record_send 0 0 0 -4294967016 pg_user_mappings record_in record_out record_recv record_send 0 0 0 -4294967017 pg_user_mapping record_in record_out record_recv record_send 0 0 0 -4294967018 pg_type record_in record_out record_recv record_send 0 0 0 -4294967019 pg_ts_template record_in record_out record_recv record_send 0 0 0 -4294967020 pg_ts_parser record_in record_out record_recv record_send 0 0 0 -4294967021 pg_ts_dict record_in record_out record_recv record_send 0 0 0 -4294967022 pg_ts_config record_in record_out record_recv record_send 0 0 0 -4294967023 pg_ts_config_map record_in record_out record_recv record_send 0 0 0 -4294967024 pg_trigger record_in record_out record_recv record_send 0 0 0 -4294967025 pg_transform record_in record_out record_recv record_send 0 0 0 -4294967026 pg_timezone_names record_in record_out record_recv record_send 0 0 0 -4294967027 pg_timezone_abbrevs record_in record_out record_recv record_send 0 0 0 -4294967028 pg_tablespace record_in record_out record_recv record_send 0 0 0 -4294967029 pg_tables record_in record_out record_recv record_send 0 0 0 -4294967030 pg_subscription record_in record_out record_recv record_send 0 0 0 -4294967031 pg_subscription_rel record_in record_out record_recv record_send 0 0 0 -4294967032 pg_stats record_in record_out record_recv record_send 0 0 0 -4294967033 pg_stats_ext record_in record_out record_recv record_send 0 0 0 -4294967034 pg_statistic record_in record_out record_recv record_send 0 0 0 -4294967035 pg_statistic_ext record_in record_out record_recv record_send 0 0 0 -4294967036 pg_statistic_ext_data record_in record_out record_recv record_send 0 0 0 -4294967037 pg_statio_user_tables record_in record_out record_recv record_send 0 0 0 -4294967038 pg_statio_user_sequences record_in record_out record_recv record_send 0 0 0 -4294967039 pg_statio_user_indexes record_in record_out record_recv record_send 0 0 0 -4294967040 pg_statio_sys_tables record_in record_out record_recv record_send 0 0 0 -4294967041 pg_statio_sys_sequences record_in record_out record_recv record_send 0 0 0 -4294967042 pg_statio_sys_indexes record_in record_out record_recv record_send 0 0 0 -4294967043 pg_statio_all_tables record_in record_out record_recv record_send 0 0 0 -4294967044 pg_statio_all_sequences record_in record_out record_recv record_send 0 0 0 -4294967045 pg_statio_all_indexes record_in record_out record_recv record_send 0 0 0 -4294967046 pg_stat_xact_user_tables record_in record_out record_recv record_send 0 0 0 -4294967047 pg_stat_xact_user_functions record_in record_out record_recv record_send 0 0 0 -4294967048 pg_stat_xact_sys_tables record_in record_out record_recv record_send 0 0 0 -4294967049 pg_stat_xact_all_tables record_in record_out record_recv record_send 0 0 0 -4294967050 pg_stat_wal_receiver record_in record_out record_recv record_send 0 0 0 -4294967051 pg_stat_user_tables record_in record_out record_recv record_send 0 0 0 -4294967052 pg_stat_user_indexes record_in record_out record_recv record_send 0 0 0 -4294967053 pg_stat_user_functions record_in record_out record_recv record_send 0 0 0 -4294967054 pg_stat_sys_tables record_in record_out record_recv record_send 0 0 0 -4294967055 pg_stat_sys_indexes record_in record_out record_recv record_send 0 0 0 -4294967056 pg_stat_subscription record_in record_out record_recv record_send 0 0 0 -4294967057 pg_stat_ssl record_in record_out record_recv record_send 0 0 0 -4294967058 pg_stat_slru record_in record_out record_recv record_send 0 0 0 -4294967059 pg_stat_replication record_in record_out record_recv record_send 0 0 0 -4294967060 pg_stat_progress_vacuum record_in record_out record_recv record_send 0 0 0 -4294967061 pg_stat_progress_create_index record_in record_out record_recv record_send 0 0 0 -4294967062 pg_stat_progress_cluster record_in record_out record_recv record_send 0 0 0 -4294967063 pg_stat_progress_basebackup record_in record_out record_recv record_send 0 0 0 -4294967064 pg_stat_progress_analyze record_in record_out record_recv record_send 0 0 0 -4294967065 pg_stat_gssapi record_in record_out record_recv record_send 0 0 0 -4294967066 pg_stat_database record_in record_out record_recv record_send 0 0 0 -4294967067 pg_stat_database_conflicts record_in record_out record_recv record_send 0 0 0 -4294967068 pg_stat_bgwriter record_in record_out record_recv record_send 0 0 0 -4294967069 pg_stat_archiver record_in record_out record_recv record_send 0 0 0 -4294967070 pg_stat_all_tables record_in record_out record_recv record_send 0 0 0 -4294967071 pg_stat_all_indexes record_in record_out record_recv record_send 0 0 0 -4294967072 pg_stat_activity record_in record_out record_recv record_send 0 0 0 -4294967073 pg_shmem_allocations record_in record_out record_recv record_send 0 0 0 -4294967074 pg_shdepend record_in record_out record_recv record_send 0 0 0 -4294967075 pg_shseclabel record_in record_out record_recv record_send 0 0 0 -4294967076 pg_shdescription record_in record_out record_recv record_send 0 0 0 -4294967077 pg_shadow record_in record_out record_recv record_send 0 0 0 -4294967078 pg_settings record_in record_out record_recv record_send 0 0 0 -4294967079 pg_sequences record_in record_out record_recv record_send 0 0 0 -4294967080 pg_sequence record_in record_out record_recv record_send 0 0 0 -4294967081 pg_seclabel record_in record_out record_recv record_send 0 0 0 -4294967082 pg_seclabels record_in record_out record_recv record_send 0 0 0 -4294967083 pg_rules record_in record_out record_recv record_send 0 0 0 -4294967084 pg_roles record_in record_out record_recv record_send 0 0 0 -4294967085 pg_rewrite record_in record_out record_recv record_send 0 0 0 -4294967086 pg_replication_slots record_in record_out record_recv record_send 0 0 0 -4294967087 pg_replication_origin record_in record_out record_recv record_send 0 0 0 -4294967088 pg_replication_origin_status record_in record_out record_recv record_send 0 0 0 -4294967089 pg_range record_in record_out record_recv record_send 0 0 0 -4294967090 pg_publication_tables record_in record_out record_recv record_send 0 0 0 -4294967091 pg_publication record_in record_out record_recv record_send 0 0 0 -4294967092 pg_publication_rel record_in record_out record_recv record_send 0 0 0 -4294967093 pg_proc record_in record_out record_recv record_send 0 0 0 -4294967094 pg_prepared_xacts record_in record_out record_recv record_send 0 0 0 -4294967095 pg_prepared_statements record_in record_out record_recv record_send 0 0 0 -4294967096 pg_policy record_in record_out record_recv record_send 0 0 0 -4294967097 pg_policies record_in record_out record_recv record_send 0 0 0 -4294967098 pg_partitioned_table record_in record_out record_recv record_send 0 0 0 -4294967099 pg_opfamily record_in record_out record_recv record_send 0 0 0 -4294967100 pg_operator record_in record_out record_recv record_send 0 0 0 -4294967101 pg_opclass record_in record_out record_recv record_send 0 0 0 -4294967102 pg_namespace record_in record_out record_recv record_send 0 0 0 -4294967103 pg_matviews record_in record_out record_recv record_send 0 0 0 -4294967104 pg_locks record_in record_out record_recv record_send 0 0 0 -4294967105 pg_largeobject record_in record_out record_recv record_send 0 0 0 -4294967106 pg_largeobject_metadata record_in record_out record_recv record_send 0 0 0 -4294967107 pg_language record_in record_out record_recv record_send 0 0 0 -4294967108 pg_init_privs record_in record_out record_recv record_send 0 0 0 -4294967109 pg_inherits record_in record_out record_recv record_send 0 0 0 -4294967110 pg_indexes record_in record_out record_recv record_send 0 0 0 -4294967111 pg_index record_in record_out record_recv record_send 0 0 0 -4294967112 pg_hba_file_rules record_in record_out record_recv record_send 0 0 0 -4294967113 pg_group record_in record_out record_recv record_send 0 0 0 -4294967114 pg_foreign_table record_in record_out record_recv record_send 0 0 0 -4294967115 pg_foreign_server record_in record_out record_recv record_send 0 0 0 -4294967116 pg_foreign_data_wrapper record_in record_out record_recv record_send 0 0 0 -4294967117 pg_file_settings record_in record_out record_recv record_send 0 0 0 -4294967118 pg_extension record_in record_out record_recv record_send 0 0 0 -4294967119 pg_event_trigger record_in record_out record_recv record_send 0 0 0 -4294967120 pg_enum record_in record_out record_recv record_send 0 0 0 -4294967121 pg_description record_in record_out record_recv record_send 0 0 0 -4294967122 pg_depend record_in record_out record_recv record_send 0 0 0 -4294967123 pg_default_acl record_in record_out record_recv record_send 0 0 0 -4294967124 pg_db_role_setting record_in record_out record_recv record_send 0 0 0 -4294967125 pg_database record_in record_out record_recv record_send 0 0 0 -4294967126 pg_cursors record_in record_out record_recv record_send 0 0 0 -4294967127 pg_conversion record_in record_out record_recv record_send 0 0 0 -4294967128 pg_constraint record_in record_out record_recv record_send 0 0 0 -4294967129 pg_config record_in record_out record_recv record_send 0 0 0 -4294967130 pg_collation record_in record_out record_recv record_send 0 0 0 -4294967131 pg_class record_in record_out record_recv record_send 0 0 0 -4294967132 pg_cast record_in record_out record_recv record_send 0 0 0 -4294967133 pg_available_extensions record_in record_out record_recv record_send 0 0 0 -4294967134 pg_available_extension_versions record_in record_out record_recv record_send 0 0 0 -4294967135 pg_auth_members record_in record_out record_recv record_send 0 0 0 -4294967136 pg_authid record_in record_out record_recv record_send 0 0 0 -4294967137 pg_attribute record_in record_out record_recv record_send 0 0 0 -4294967138 pg_attrdef record_in record_out record_recv record_send 0 0 0 -4294967139 pg_amproc record_in record_out record_recv record_send 0 0 0 -4294967140 pg_amop record_in record_out record_recv record_send 0 0 0 -4294967141 pg_am record_in record_out record_recv record_send 0 0 0 -4294967142 pg_aggregate record_in record_out record_recv record_send 0 0 0 -4294967144 views record_in record_out record_recv record_send 0 0 0 -4294967145 view_table_usage record_in record_out record_recv record_send 0 0 0 -4294967146 view_routine_usage record_in record_out record_recv record_send 0 0 0 -4294967147 view_column_usage record_in record_out record_recv record_send 0 0 0 -4294967148 user_privileges record_in record_out record_recv record_send 0 0 0 -4294967149 user_mappings record_in record_out record_recv record_send 0 0 0 -4294967150 user_mapping_options record_in record_out record_recv record_send 0 0 0 -4294967151 user_defined_types record_in record_out record_recv record_send 0 0 0 -4294967152 user_attributes record_in record_out record_recv record_send 0 0 0 -4294967153 usage_privileges record_in record_out record_recv record_send 0 0 0 -4294967154 udt_privileges record_in record_out record_recv record_send 0 0 0 -4294967155 type_privileges record_in record_out record_recv record_send 0 0 0 -4294967156 triggers record_in record_out record_recv record_send 0 0 0 -4294967157 triggered_update_columns record_in record_out record_recv record_send 0 0 0 -4294967158 transforms record_in record_out record_recv record_send 0 0 0 -4294967159 tablespaces record_in record_out record_recv record_send 0 0 0 -4294967160 tablespaces_extensions record_in record_out record_recv record_send 0 0 0 -4294967161 tables record_in record_out record_recv record_send 0 0 0 -4294967162 tables_extensions record_in record_out record_recv record_send 0 0 0 -4294967163 table_privileges record_in record_out record_recv record_send 0 0 0 -4294967164 table_constraints_extensions record_in record_out record_recv record_send 0 0 0 -4294967165 table_constraints record_in record_out record_recv record_send 0 0 0 -4294967166 statistics record_in record_out record_recv record_send 0 0 0 -4294967167 st_units_of_measure record_in record_out record_recv record_send 0 0 0 -4294967168 st_spatial_reference_systems record_in record_out record_recv record_send 0 0 0 -4294967169 st_geometry_columns record_in record_out record_recv record_send 0 0 0 -4294967170 session_variables record_in record_out record_recv record_send 0 0 0 -4294967171 sequences record_in record_out record_recv record_send 0 0 0 -4294967172 schema_privileges record_in record_out record_recv record_send 0 0 0 -4294967173 schemata record_in record_out record_recv record_send 0 0 0 -4294967174 schemata_extensions record_in record_out record_recv record_send 0 0 0 -4294967175 sql_sizing record_in record_out record_recv record_send 0 0 0 -4294967176 sql_parts record_in record_out record_recv record_send 0 0 0 -4294967177 sql_implementation_info record_in record_out record_recv record_send 0 0 0 -4294967178 sql_features record_in record_out record_recv record_send 0 0 0 -4294967179 routines record_in record_out record_recv record_send 0 0 0 -4294967180 routine_privileges record_in record_out record_recv record_send 0 0 0 -4294967181 role_usage_grants record_in record_out record_recv record_send 0 0 0 -4294967182 role_udt_grants record_in record_out record_recv record_send 0 0 0 -4294967183 role_table_grants record_in record_out record_recv record_send 0 0 0 -4294967184 role_routine_grants record_in record_out record_recv record_send 0 0 0 -4294967185 role_column_grants record_in record_out record_recv record_send 0 0 0 -4294967186 resource_groups record_in record_out record_recv record_send 0 0 0 -4294967187 referential_constraints record_in record_out record_recv record_send 0 0 0 -4294967188 profiling record_in record_out record_recv record_send 0 0 0 -4294967189 processlist record_in record_out record_recv record_send 0 0 0 -4294967190 plugins record_in record_out record_recv record_send 0 0 0 -4294967191 partitions record_in record_out record_recv record_send 0 0 0 -4294967192 parameters record_in record_out record_recv record_send 0 0 0 -4294967193 optimizer_trace record_in record_out record_recv record_send 0 0 0 -4294967194 keywords record_in record_out record_recv record_send 0 0 0 -4294967195 key_column_usage record_in record_out record_recv record_send 0 0 0 -4294967196 information_schema_catalog_name record_in record_out record_recv record_send 0 0 0 -4294967197 foreign_tables record_in record_out record_recv record_send 0 0 0 -4294967198 foreign_table_options record_in record_out record_recv record_send 0 0 0 -4294967199 foreign_servers record_in record_out record_recv record_send 0 0 0 -4294967200 foreign_server_options record_in record_out record_recv record_send 0 0 0 -4294967201 foreign_data_wrappers record_in record_out record_recv record_send 0 0 0 -4294967202 foreign_data_wrapper_options record_in record_out record_recv record_send 0 0 0 -4294967203 files record_in record_out record_recv record_send 0 0 0 -4294967204 events record_in record_out record_recv record_send 0 0 0 -4294967205 engines record_in record_out record_recv record_send 0 0 0 -4294967206 enabled_roles record_in record_out record_recv record_send 0 0 0 -4294967207 element_types record_in record_out record_recv record_send 0 0 0 -4294967208 domains record_in record_out record_recv record_send 0 0 0 -4294967209 domain_udt_usage record_in record_out record_recv record_send 0 0 0 -4294967210 domain_constraints record_in record_out record_recv record_send 0 0 0 -4294967211 data_type_privileges record_in record_out record_recv record_send 0 0 0 -4294967212 constraint_table_usage record_in record_out record_recv record_send 0 0 0 -4294967213 constraint_column_usage record_in record_out record_recv record_send 0 0 0 -4294967214 columns record_in record_out record_recv record_send 0 0 0 -4294967215 columns_extensions record_in record_out record_recv record_send 0 0 0 -4294967216 column_udt_usage record_in record_out record_recv record_send 0 0 0 -4294967217 column_statistics record_in record_out record_recv record_send 0 0 0 -4294967218 column_privileges record_in record_out record_recv record_send 0 0 0 -4294967219 column_options record_in record_out record_recv record_send 0 0 0 -4294967220 column_domain_usage record_in record_out record_recv record_send 0 0 0 -4294967221 column_column_usage record_in record_out record_recv record_send 0 0 0 -4294967222 collations record_in record_out record_recv record_send 0 0 0 -4294967223 collation_character_set_applicability record_in record_out record_recv record_send 0 0 0 -4294967224 check_constraints record_in record_out record_recv record_send 0 0 0 -4294967225 check_constraint_routine_usage record_in record_out record_recv record_send 0 0 0 -4294967226 character_sets record_in record_out record_recv record_send 0 0 0 -4294967227 attributes record_in record_out record_recv record_send 0 0 0 -4294967228 applicable_roles record_in record_out record_recv record_send 0 0 0 -4294967229 administrable_role_authorizations record_in record_out record_recv record_send 0 0 0 -4294967231 tenant_usage_details record_in record_out record_recv record_send 0 0 0 -4294967232 active_range_feeds record_in record_out record_recv record_send 0 0 0 -4294967233 default_privileges record_in record_out record_recv record_send 0 0 0 -4294967234 regions record_in record_out record_recv record_send 0 0 0 -4294967235 cluster_inflight_traces record_in record_out record_recv record_send 0 0 0 -4294967236 lost_descriptors_with_data record_in record_out record_recv record_send 0 0 0 -4294967237 cross_db_references record_in record_out record_recv record_send 0 0 0 -4294967238 interleaved record_in record_out record_recv record_send 0 0 0 +4294967011 spatial_ref_sys record_in record_out record_recv record_send 0 0 0 +4294967012 geometry_columns record_in record_out record_recv record_send 0 0 0 +4294967013 geography_columns record_in record_out record_recv record_send 0 0 0 +4294967015 pg_views record_in record_out record_recv record_send 0 0 0 +4294967016 pg_user record_in record_out record_recv record_send 0 0 0 +4294967017 pg_user_mappings record_in record_out record_recv record_send 0 0 0 +4294967018 pg_user_mapping record_in record_out record_recv record_send 0 0 0 +4294967019 pg_type record_in record_out record_recv record_send 0 0 0 +4294967020 pg_ts_template record_in record_out record_recv record_send 0 0 0 +4294967021 pg_ts_parser record_in record_out record_recv record_send 0 0 0 +4294967022 pg_ts_dict record_in record_out record_recv record_send 0 0 0 +4294967023 pg_ts_config record_in record_out record_recv record_send 0 0 0 +4294967024 pg_ts_config_map record_in record_out record_recv record_send 0 0 0 +4294967025 pg_trigger record_in record_out record_recv record_send 0 0 0 +4294967026 pg_transform record_in record_out record_recv record_send 0 0 0 +4294967027 pg_timezone_names record_in record_out record_recv record_send 0 0 0 +4294967028 pg_timezone_abbrevs record_in record_out record_recv record_send 0 0 0 +4294967029 pg_tablespace record_in record_out record_recv record_send 0 0 0 +4294967030 pg_tables record_in record_out record_recv record_send 0 0 0 +4294967031 pg_subscription record_in record_out record_recv record_send 0 0 0 +4294967032 pg_subscription_rel record_in record_out record_recv record_send 0 0 0 +4294967033 pg_stats record_in record_out record_recv record_send 0 0 0 +4294967034 pg_stats_ext record_in record_out record_recv record_send 0 0 0 +4294967035 pg_statistic record_in record_out record_recv record_send 0 0 0 +4294967036 pg_statistic_ext record_in record_out record_recv record_send 0 0 0 +4294967037 pg_statistic_ext_data record_in record_out record_recv record_send 0 0 0 +4294967038 pg_statio_user_tables record_in record_out record_recv record_send 0 0 0 +4294967039 pg_statio_user_sequences record_in record_out record_recv record_send 0 0 0 +4294967040 pg_statio_user_indexes record_in record_out record_recv record_send 0 0 0 +4294967041 pg_statio_sys_tables record_in record_out record_recv record_send 0 0 0 +4294967042 pg_statio_sys_sequences record_in record_out record_recv record_send 0 0 0 +4294967043 pg_statio_sys_indexes record_in record_out record_recv record_send 0 0 0 +4294967044 pg_statio_all_tables record_in record_out record_recv record_send 0 0 0 +4294967045 pg_statio_all_sequences record_in record_out record_recv record_send 0 0 0 +4294967046 pg_statio_all_indexes record_in record_out record_recv record_send 0 0 0 +4294967047 pg_stat_xact_user_tables record_in record_out record_recv record_send 0 0 0 +4294967048 pg_stat_xact_user_functions record_in record_out record_recv record_send 0 0 0 +4294967049 pg_stat_xact_sys_tables record_in record_out record_recv record_send 0 0 0 +4294967050 pg_stat_xact_all_tables record_in record_out record_recv record_send 0 0 0 +4294967051 pg_stat_wal_receiver record_in record_out record_recv record_send 0 0 0 +4294967052 pg_stat_user_tables record_in record_out record_recv record_send 0 0 0 +4294967053 pg_stat_user_indexes record_in record_out record_recv record_send 0 0 0 +4294967054 pg_stat_user_functions record_in record_out record_recv record_send 0 0 0 +4294967055 pg_stat_sys_tables record_in record_out record_recv record_send 0 0 0 +4294967056 pg_stat_sys_indexes record_in record_out record_recv record_send 0 0 0 +4294967057 pg_stat_subscription record_in record_out record_recv record_send 0 0 0 +4294967058 pg_stat_ssl record_in record_out record_recv record_send 0 0 0 +4294967059 pg_stat_slru record_in record_out record_recv record_send 0 0 0 +4294967060 pg_stat_replication record_in record_out record_recv record_send 0 0 0 +4294967061 pg_stat_progress_vacuum record_in record_out record_recv record_send 0 0 0 +4294967062 pg_stat_progress_create_index record_in record_out record_recv record_send 0 0 0 +4294967063 pg_stat_progress_cluster record_in record_out record_recv record_send 0 0 0 +4294967064 pg_stat_progress_basebackup record_in record_out record_recv record_send 0 0 0 +4294967065 pg_stat_progress_analyze record_in record_out record_recv record_send 0 0 0 +4294967066 pg_stat_gssapi record_in record_out record_recv record_send 0 0 0 +4294967067 pg_stat_database record_in record_out record_recv record_send 0 0 0 +4294967068 pg_stat_database_conflicts record_in record_out record_recv record_send 0 0 0 +4294967069 pg_stat_bgwriter record_in record_out record_recv record_send 0 0 0 +4294967070 pg_stat_archiver record_in record_out record_recv record_send 0 0 0 +4294967071 pg_stat_all_tables record_in record_out record_recv record_send 0 0 0 +4294967072 pg_stat_all_indexes record_in record_out record_recv record_send 0 0 0 +4294967073 pg_stat_activity record_in record_out record_recv record_send 0 0 0 +4294967074 pg_shmem_allocations record_in record_out record_recv record_send 0 0 0 +4294967075 pg_shdepend record_in record_out record_recv record_send 0 0 0 +4294967076 pg_shseclabel record_in record_out record_recv record_send 0 0 0 +4294967077 pg_shdescription record_in record_out record_recv record_send 0 0 0 +4294967078 pg_shadow record_in record_out record_recv record_send 0 0 0 +4294967079 pg_settings record_in record_out record_recv record_send 0 0 0 +4294967080 pg_sequences record_in record_out record_recv record_send 0 0 0 +4294967081 pg_sequence record_in record_out record_recv record_send 0 0 0 +4294967082 pg_seclabel record_in record_out record_recv record_send 0 0 0 +4294967083 pg_seclabels record_in record_out record_recv record_send 0 0 0 +4294967084 pg_rules record_in record_out record_recv record_send 0 0 0 +4294967085 pg_roles record_in record_out record_recv record_send 0 0 0 +4294967086 pg_rewrite record_in record_out record_recv record_send 0 0 0 +4294967087 pg_replication_slots record_in record_out record_recv record_send 0 0 0 +4294967088 pg_replication_origin record_in record_out record_recv record_send 0 0 0 +4294967089 pg_replication_origin_status record_in record_out record_recv record_send 0 0 0 +4294967090 pg_range record_in record_out record_recv record_send 0 0 0 +4294967091 pg_publication_tables record_in record_out record_recv record_send 0 0 0 +4294967092 pg_publication record_in record_out record_recv record_send 0 0 0 +4294967093 pg_publication_rel record_in record_out record_recv record_send 0 0 0 +4294967094 pg_proc record_in record_out record_recv record_send 0 0 0 +4294967095 pg_prepared_xacts record_in record_out record_recv record_send 0 0 0 +4294967096 pg_prepared_statements record_in record_out record_recv record_send 0 0 0 +4294967097 pg_policy record_in record_out record_recv record_send 0 0 0 +4294967098 pg_policies record_in record_out record_recv record_send 0 0 0 +4294967099 pg_partitioned_table record_in record_out record_recv record_send 0 0 0 +4294967100 pg_opfamily record_in record_out record_recv record_send 0 0 0 +4294967101 pg_operator record_in record_out record_recv record_send 0 0 0 +4294967102 pg_opclass record_in record_out record_recv record_send 0 0 0 +4294967103 pg_namespace record_in record_out record_recv record_send 0 0 0 +4294967104 pg_matviews record_in record_out record_recv record_send 0 0 0 +4294967105 pg_locks record_in record_out record_recv record_send 0 0 0 +4294967106 pg_largeobject record_in record_out record_recv record_send 0 0 0 +4294967107 pg_largeobject_metadata record_in record_out record_recv record_send 0 0 0 +4294967108 pg_language record_in record_out record_recv record_send 0 0 0 +4294967109 pg_init_privs record_in record_out record_recv record_send 0 0 0 +4294967110 pg_inherits record_in record_out record_recv record_send 0 0 0 +4294967111 pg_indexes record_in record_out record_recv record_send 0 0 0 +4294967112 pg_index record_in record_out record_recv record_send 0 0 0 +4294967113 pg_hba_file_rules record_in record_out record_recv record_send 0 0 0 +4294967114 pg_group record_in record_out record_recv record_send 0 0 0 +4294967115 pg_foreign_table record_in record_out record_recv record_send 0 0 0 +4294967116 pg_foreign_server record_in record_out record_recv record_send 0 0 0 +4294967117 pg_foreign_data_wrapper record_in record_out record_recv record_send 0 0 0 +4294967118 pg_file_settings record_in record_out record_recv record_send 0 0 0 +4294967119 pg_extension record_in record_out record_recv record_send 0 0 0 +4294967120 pg_event_trigger record_in record_out record_recv record_send 0 0 0 +4294967121 pg_enum record_in record_out record_recv record_send 0 0 0 +4294967122 pg_description record_in record_out record_recv record_send 0 0 0 +4294967123 pg_depend record_in record_out record_recv record_send 0 0 0 +4294967124 pg_default_acl record_in record_out record_recv record_send 0 0 0 +4294967125 pg_db_role_setting record_in record_out record_recv record_send 0 0 0 +4294967126 pg_database record_in record_out record_recv record_send 0 0 0 +4294967127 pg_cursors record_in record_out record_recv record_send 0 0 0 +4294967128 pg_conversion record_in record_out record_recv record_send 0 0 0 +4294967129 pg_constraint record_in record_out record_recv record_send 0 0 0 +4294967130 pg_config record_in record_out record_recv record_send 0 0 0 +4294967131 pg_collation record_in record_out record_recv record_send 0 0 0 +4294967132 pg_class record_in record_out record_recv record_send 0 0 0 +4294967133 pg_cast record_in record_out record_recv record_send 0 0 0 +4294967134 pg_available_extensions record_in record_out record_recv record_send 0 0 0 +4294967135 pg_available_extension_versions record_in record_out record_recv record_send 0 0 0 +4294967136 pg_auth_members record_in record_out record_recv record_send 0 0 0 +4294967137 pg_authid record_in record_out record_recv record_send 0 0 0 +4294967138 pg_attribute record_in record_out record_recv record_send 0 0 0 +4294967139 pg_attrdef record_in record_out record_recv record_send 0 0 0 +4294967140 pg_amproc record_in record_out record_recv record_send 0 0 0 +4294967141 pg_amop record_in record_out record_recv record_send 0 0 0 +4294967142 pg_am record_in record_out record_recv record_send 0 0 0 +4294967143 pg_aggregate record_in record_out record_recv record_send 0 0 0 +4294967145 views record_in record_out record_recv record_send 0 0 0 +4294967146 view_table_usage record_in record_out record_recv record_send 0 0 0 +4294967147 view_routine_usage record_in record_out record_recv record_send 0 0 0 +4294967148 view_column_usage record_in record_out record_recv record_send 0 0 0 +4294967149 user_privileges record_in record_out record_recv record_send 0 0 0 +4294967150 user_mappings record_in record_out record_recv record_send 0 0 0 +4294967151 user_mapping_options record_in record_out record_recv record_send 0 0 0 +4294967152 user_defined_types record_in record_out record_recv record_send 0 0 0 +4294967153 user_attributes record_in record_out record_recv record_send 0 0 0 +4294967154 usage_privileges record_in record_out record_recv record_send 0 0 0 +4294967155 udt_privileges record_in record_out record_recv record_send 0 0 0 +4294967156 type_privileges record_in record_out record_recv record_send 0 0 0 +4294967157 triggers record_in record_out record_recv record_send 0 0 0 +4294967158 triggered_update_columns record_in record_out record_recv record_send 0 0 0 +4294967159 transforms record_in record_out record_recv record_send 0 0 0 +4294967160 tablespaces record_in record_out record_recv record_send 0 0 0 +4294967161 tablespaces_extensions record_in record_out record_recv record_send 0 0 0 +4294967162 tables record_in record_out record_recv record_send 0 0 0 +4294967163 tables_extensions record_in record_out record_recv record_send 0 0 0 +4294967164 table_privileges record_in record_out record_recv record_send 0 0 0 +4294967165 table_constraints_extensions record_in record_out record_recv record_send 0 0 0 +4294967166 table_constraints record_in record_out record_recv record_send 0 0 0 +4294967167 statistics record_in record_out record_recv record_send 0 0 0 +4294967168 st_units_of_measure record_in record_out record_recv record_send 0 0 0 +4294967169 st_spatial_reference_systems record_in record_out record_recv record_send 0 0 0 +4294967170 st_geometry_columns record_in record_out record_recv record_send 0 0 0 +4294967171 session_variables record_in record_out record_recv record_send 0 0 0 +4294967172 sequences record_in record_out record_recv record_send 0 0 0 +4294967173 schema_privileges record_in record_out record_recv record_send 0 0 0 +4294967174 schemata record_in record_out record_recv record_send 0 0 0 +4294967175 schemata_extensions record_in record_out record_recv record_send 0 0 0 +4294967176 sql_sizing record_in record_out record_recv record_send 0 0 0 +4294967177 sql_parts record_in record_out record_recv record_send 0 0 0 +4294967178 sql_implementation_info record_in record_out record_recv record_send 0 0 0 +4294967179 sql_features record_in record_out record_recv record_send 0 0 0 +4294967180 routines record_in record_out record_recv record_send 0 0 0 +4294967181 routine_privileges record_in record_out record_recv record_send 0 0 0 +4294967182 role_usage_grants record_in record_out record_recv record_send 0 0 0 +4294967183 role_udt_grants record_in record_out record_recv record_send 0 0 0 +4294967184 role_table_grants record_in record_out record_recv record_send 0 0 0 +4294967185 role_routine_grants record_in record_out record_recv record_send 0 0 0 +4294967186 role_column_grants record_in record_out record_recv record_send 0 0 0 +4294967187 resource_groups record_in record_out record_recv record_send 0 0 0 +4294967188 referential_constraints record_in record_out record_recv record_send 0 0 0 +4294967189 profiling record_in record_out record_recv record_send 0 0 0 +4294967190 processlist record_in record_out record_recv record_send 0 0 0 +4294967191 plugins record_in record_out record_recv record_send 0 0 0 +4294967192 partitions record_in record_out record_recv record_send 0 0 0 +4294967193 parameters record_in record_out record_recv record_send 0 0 0 +4294967194 optimizer_trace record_in record_out record_recv record_send 0 0 0 +4294967195 keywords record_in record_out record_recv record_send 0 0 0 +4294967196 key_column_usage record_in record_out record_recv record_send 0 0 0 +4294967197 information_schema_catalog_name record_in record_out record_recv record_send 0 0 0 +4294967198 foreign_tables record_in record_out record_recv record_send 0 0 0 +4294967199 foreign_table_options record_in record_out record_recv record_send 0 0 0 +4294967200 foreign_servers record_in record_out record_recv record_send 0 0 0 +4294967201 foreign_server_options record_in record_out record_recv record_send 0 0 0 +4294967202 foreign_data_wrappers record_in record_out record_recv record_send 0 0 0 +4294967203 foreign_data_wrapper_options record_in record_out record_recv record_send 0 0 0 +4294967204 files record_in record_out record_recv record_send 0 0 0 +4294967205 events record_in record_out record_recv record_send 0 0 0 +4294967206 engines record_in record_out record_recv record_send 0 0 0 +4294967207 enabled_roles record_in record_out record_recv record_send 0 0 0 +4294967208 element_types record_in record_out record_recv record_send 0 0 0 +4294967209 domains record_in record_out record_recv record_send 0 0 0 +4294967210 domain_udt_usage record_in record_out record_recv record_send 0 0 0 +4294967211 domain_constraints record_in record_out record_recv record_send 0 0 0 +4294967212 data_type_privileges record_in record_out record_recv record_send 0 0 0 +4294967213 constraint_table_usage record_in record_out record_recv record_send 0 0 0 +4294967214 constraint_column_usage record_in record_out record_recv record_send 0 0 0 +4294967215 columns record_in record_out record_recv record_send 0 0 0 +4294967216 columns_extensions record_in record_out record_recv record_send 0 0 0 +4294967217 column_udt_usage record_in record_out record_recv record_send 0 0 0 +4294967218 column_statistics record_in record_out record_recv record_send 0 0 0 +4294967219 column_privileges record_in record_out record_recv record_send 0 0 0 +4294967220 column_options record_in record_out record_recv record_send 0 0 0 +4294967221 column_domain_usage record_in record_out record_recv record_send 0 0 0 +4294967222 column_column_usage record_in record_out record_recv record_send 0 0 0 +4294967223 collations record_in record_out record_recv record_send 0 0 0 +4294967224 collation_character_set_applicability record_in record_out record_recv record_send 0 0 0 +4294967225 check_constraints record_in record_out record_recv record_send 0 0 0 +4294967226 check_constraint_routine_usage record_in record_out record_recv record_send 0 0 0 +4294967227 character_sets record_in record_out record_recv record_send 0 0 0 +4294967228 attributes record_in record_out record_recv record_send 0 0 0 +4294967229 applicable_roles record_in record_out record_recv record_send 0 0 0 +4294967230 administrable_role_authorizations record_in record_out record_recv record_send 0 0 0 +4294967232 tenant_usage_details record_in record_out record_recv record_send 0 0 0 +4294967233 active_range_feeds record_in record_out record_recv record_send 0 0 0 +4294967234 default_privileges record_in record_out record_recv record_send 0 0 0 +4294967235 regions record_in record_out record_recv record_send 0 0 0 +4294967236 cluster_inflight_traces record_in record_out record_recv record_send 0 0 0 +4294967237 lost_descriptors_with_data record_in record_out record_recv record_send 0 0 0 +4294967238 cross_db_references record_in record_out record_recv record_send 0 0 0 4294967239 cluster_database_privileges record_in record_out record_recv record_send 0 0 0 4294967240 invalid_objects record_in record_out record_recv record_send 0 0 0 4294967241 zones record_in record_out record_recv record_send 0 0 0 @@ -2776,232 +2773,231 @@ oid typname typalign typstorage typnotn 100076 _newtype1 NULL NULL false 0 -1 100077 newtype2 NULL NULL false 0 -1 100078 _newtype2 NULL NULL false 0 -1 -4294967010 spatial_ref_sys NULL NULL false 0 -1 -4294967011 geometry_columns NULL NULL false 0 -1 -4294967012 geography_columns NULL NULL false 0 -1 -4294967014 pg_views NULL NULL false 0 -1 -4294967015 pg_user NULL NULL false 0 -1 -4294967016 pg_user_mappings NULL NULL false 0 -1 -4294967017 pg_user_mapping NULL NULL false 0 -1 -4294967018 pg_type NULL NULL false 0 -1 -4294967019 pg_ts_template NULL NULL false 0 -1 -4294967020 pg_ts_parser NULL NULL false 0 -1 -4294967021 pg_ts_dict NULL NULL false 0 -1 -4294967022 pg_ts_config NULL NULL false 0 -1 -4294967023 pg_ts_config_map NULL NULL false 0 -1 -4294967024 pg_trigger NULL NULL false 0 -1 -4294967025 pg_transform NULL NULL false 0 -1 -4294967026 pg_timezone_names NULL NULL false 0 -1 -4294967027 pg_timezone_abbrevs NULL NULL false 0 -1 -4294967028 pg_tablespace NULL NULL false 0 -1 -4294967029 pg_tables NULL NULL false 0 -1 -4294967030 pg_subscription NULL NULL false 0 -1 -4294967031 pg_subscription_rel NULL NULL false 0 -1 -4294967032 pg_stats NULL NULL false 0 -1 -4294967033 pg_stats_ext NULL NULL false 0 -1 -4294967034 pg_statistic NULL NULL false 0 -1 -4294967035 pg_statistic_ext NULL NULL false 0 -1 -4294967036 pg_statistic_ext_data NULL NULL false 0 -1 -4294967037 pg_statio_user_tables NULL NULL false 0 -1 -4294967038 pg_statio_user_sequences NULL NULL false 0 -1 -4294967039 pg_statio_user_indexes NULL NULL false 0 -1 -4294967040 pg_statio_sys_tables NULL NULL false 0 -1 -4294967041 pg_statio_sys_sequences NULL NULL false 0 -1 -4294967042 pg_statio_sys_indexes NULL NULL false 0 -1 -4294967043 pg_statio_all_tables NULL NULL false 0 -1 -4294967044 pg_statio_all_sequences NULL NULL false 0 -1 -4294967045 pg_statio_all_indexes NULL NULL false 0 -1 -4294967046 pg_stat_xact_user_tables NULL NULL false 0 -1 -4294967047 pg_stat_xact_user_functions NULL NULL false 0 -1 -4294967048 pg_stat_xact_sys_tables NULL NULL false 0 -1 -4294967049 pg_stat_xact_all_tables NULL NULL false 0 -1 -4294967050 pg_stat_wal_receiver NULL NULL false 0 -1 -4294967051 pg_stat_user_tables NULL NULL false 0 -1 -4294967052 pg_stat_user_indexes NULL NULL false 0 -1 -4294967053 pg_stat_user_functions NULL NULL false 0 -1 -4294967054 pg_stat_sys_tables NULL NULL false 0 -1 -4294967055 pg_stat_sys_indexes NULL NULL false 0 -1 -4294967056 pg_stat_subscription NULL NULL false 0 -1 -4294967057 pg_stat_ssl NULL NULL false 0 -1 -4294967058 pg_stat_slru NULL NULL false 0 -1 -4294967059 pg_stat_replication NULL NULL false 0 -1 -4294967060 pg_stat_progress_vacuum NULL NULL false 0 -1 -4294967061 pg_stat_progress_create_index NULL NULL false 0 -1 -4294967062 pg_stat_progress_cluster NULL NULL false 0 -1 -4294967063 pg_stat_progress_basebackup NULL NULL false 0 -1 -4294967064 pg_stat_progress_analyze NULL NULL false 0 -1 -4294967065 pg_stat_gssapi NULL NULL false 0 -1 -4294967066 pg_stat_database NULL NULL false 0 -1 -4294967067 pg_stat_database_conflicts NULL NULL false 0 -1 -4294967068 pg_stat_bgwriter NULL NULL false 0 -1 -4294967069 pg_stat_archiver NULL NULL false 0 -1 -4294967070 pg_stat_all_tables NULL NULL false 0 -1 -4294967071 pg_stat_all_indexes NULL NULL false 0 -1 -4294967072 pg_stat_activity NULL NULL false 0 -1 -4294967073 pg_shmem_allocations NULL NULL false 0 -1 -4294967074 pg_shdepend NULL NULL false 0 -1 -4294967075 pg_shseclabel NULL NULL false 0 -1 -4294967076 pg_shdescription NULL NULL false 0 -1 -4294967077 pg_shadow NULL NULL false 0 -1 -4294967078 pg_settings NULL NULL false 0 -1 -4294967079 pg_sequences NULL NULL false 0 -1 -4294967080 pg_sequence NULL NULL false 0 -1 -4294967081 pg_seclabel NULL NULL false 0 -1 -4294967082 pg_seclabels NULL NULL false 0 -1 -4294967083 pg_rules NULL NULL false 0 -1 -4294967084 pg_roles NULL NULL false 0 -1 -4294967085 pg_rewrite NULL NULL false 0 -1 -4294967086 pg_replication_slots NULL NULL false 0 -1 -4294967087 pg_replication_origin NULL NULL false 0 -1 -4294967088 pg_replication_origin_status NULL NULL false 0 -1 -4294967089 pg_range NULL NULL false 0 -1 -4294967090 pg_publication_tables NULL NULL false 0 -1 -4294967091 pg_publication NULL NULL false 0 -1 -4294967092 pg_publication_rel NULL NULL false 0 -1 -4294967093 pg_proc NULL NULL false 0 -1 -4294967094 pg_prepared_xacts NULL NULL false 0 -1 -4294967095 pg_prepared_statements NULL NULL false 0 -1 -4294967096 pg_policy NULL NULL false 0 -1 -4294967097 pg_policies NULL NULL false 0 -1 -4294967098 pg_partitioned_table NULL NULL false 0 -1 -4294967099 pg_opfamily NULL NULL false 0 -1 -4294967100 pg_operator NULL NULL false 0 -1 -4294967101 pg_opclass NULL NULL false 0 -1 -4294967102 pg_namespace NULL NULL false 0 -1 -4294967103 pg_matviews NULL NULL false 0 -1 -4294967104 pg_locks NULL NULL false 0 -1 -4294967105 pg_largeobject NULL NULL false 0 -1 -4294967106 pg_largeobject_metadata NULL NULL false 0 -1 -4294967107 pg_language NULL NULL false 0 -1 -4294967108 pg_init_privs NULL NULL false 0 -1 -4294967109 pg_inherits NULL NULL false 0 -1 -4294967110 pg_indexes NULL NULL false 0 -1 -4294967111 pg_index NULL NULL false 0 -1 -4294967112 pg_hba_file_rules NULL NULL false 0 -1 -4294967113 pg_group NULL NULL false 0 -1 -4294967114 pg_foreign_table NULL NULL false 0 -1 -4294967115 pg_foreign_server NULL NULL false 0 -1 -4294967116 pg_foreign_data_wrapper NULL NULL false 0 -1 -4294967117 pg_file_settings NULL NULL false 0 -1 -4294967118 pg_extension NULL NULL false 0 -1 -4294967119 pg_event_trigger NULL NULL false 0 -1 -4294967120 pg_enum NULL NULL false 0 -1 -4294967121 pg_description NULL NULL false 0 -1 -4294967122 pg_depend NULL NULL false 0 -1 -4294967123 pg_default_acl NULL NULL false 0 -1 -4294967124 pg_db_role_setting NULL NULL false 0 -1 -4294967125 pg_database NULL NULL false 0 -1 -4294967126 pg_cursors NULL NULL false 0 -1 -4294967127 pg_conversion NULL NULL false 0 -1 -4294967128 pg_constraint NULL NULL false 0 -1 -4294967129 pg_config NULL NULL false 0 -1 -4294967130 pg_collation NULL NULL false 0 -1 -4294967131 pg_class NULL NULL false 0 -1 -4294967132 pg_cast NULL NULL false 0 -1 -4294967133 pg_available_extensions NULL NULL false 0 -1 -4294967134 pg_available_extension_versions NULL NULL false 0 -1 -4294967135 pg_auth_members NULL NULL false 0 -1 -4294967136 pg_authid NULL NULL false 0 -1 -4294967137 pg_attribute NULL NULL false 0 -1 -4294967138 pg_attrdef NULL NULL false 0 -1 -4294967139 pg_amproc NULL NULL false 0 -1 -4294967140 pg_amop NULL NULL false 0 -1 -4294967141 pg_am NULL NULL false 0 -1 -4294967142 pg_aggregate NULL NULL false 0 -1 -4294967144 views NULL NULL false 0 -1 -4294967145 view_table_usage NULL NULL false 0 -1 -4294967146 view_routine_usage NULL NULL false 0 -1 -4294967147 view_column_usage NULL NULL false 0 -1 -4294967148 user_privileges NULL NULL false 0 -1 -4294967149 user_mappings NULL NULL false 0 -1 -4294967150 user_mapping_options NULL NULL false 0 -1 -4294967151 user_defined_types NULL NULL false 0 -1 -4294967152 user_attributes NULL NULL false 0 -1 -4294967153 usage_privileges NULL NULL false 0 -1 -4294967154 udt_privileges NULL NULL false 0 -1 -4294967155 type_privileges NULL NULL false 0 -1 -4294967156 triggers NULL NULL false 0 -1 -4294967157 triggered_update_columns NULL NULL false 0 -1 -4294967158 transforms NULL NULL false 0 -1 -4294967159 tablespaces NULL NULL false 0 -1 -4294967160 tablespaces_extensions NULL NULL false 0 -1 -4294967161 tables NULL NULL false 0 -1 -4294967162 tables_extensions NULL NULL false 0 -1 -4294967163 table_privileges NULL NULL false 0 -1 -4294967164 table_constraints_extensions NULL NULL false 0 -1 -4294967165 table_constraints NULL NULL false 0 -1 -4294967166 statistics NULL NULL false 0 -1 -4294967167 st_units_of_measure NULL NULL false 0 -1 -4294967168 st_spatial_reference_systems NULL NULL false 0 -1 -4294967169 st_geometry_columns NULL NULL false 0 -1 -4294967170 session_variables NULL NULL false 0 -1 -4294967171 sequences NULL NULL false 0 -1 -4294967172 schema_privileges NULL NULL false 0 -1 -4294967173 schemata NULL NULL false 0 -1 -4294967174 schemata_extensions NULL NULL false 0 -1 -4294967175 sql_sizing NULL NULL false 0 -1 -4294967176 sql_parts NULL NULL false 0 -1 -4294967177 sql_implementation_info NULL NULL false 0 -1 -4294967178 sql_features NULL NULL false 0 -1 -4294967179 routines NULL NULL false 0 -1 -4294967180 routine_privileges NULL NULL false 0 -1 -4294967181 role_usage_grants NULL NULL false 0 -1 -4294967182 role_udt_grants NULL NULL false 0 -1 -4294967183 role_table_grants NULL NULL false 0 -1 -4294967184 role_routine_grants NULL NULL false 0 -1 -4294967185 role_column_grants NULL NULL false 0 -1 -4294967186 resource_groups NULL NULL false 0 -1 -4294967187 referential_constraints NULL NULL false 0 -1 -4294967188 profiling NULL NULL false 0 -1 -4294967189 processlist NULL NULL false 0 -1 -4294967190 plugins NULL NULL false 0 -1 -4294967191 partitions NULL NULL false 0 -1 -4294967192 parameters NULL NULL false 0 -1 -4294967193 optimizer_trace NULL NULL false 0 -1 -4294967194 keywords NULL NULL false 0 -1 -4294967195 key_column_usage NULL NULL false 0 -1 -4294967196 information_schema_catalog_name NULL NULL false 0 -1 -4294967197 foreign_tables NULL NULL false 0 -1 -4294967198 foreign_table_options NULL NULL false 0 -1 -4294967199 foreign_servers NULL NULL false 0 -1 -4294967200 foreign_server_options NULL NULL false 0 -1 -4294967201 foreign_data_wrappers NULL NULL false 0 -1 -4294967202 foreign_data_wrapper_options NULL NULL false 0 -1 -4294967203 files NULL NULL false 0 -1 -4294967204 events NULL NULL false 0 -1 -4294967205 engines NULL NULL false 0 -1 -4294967206 enabled_roles NULL NULL false 0 -1 -4294967207 element_types NULL NULL false 0 -1 -4294967208 domains NULL NULL false 0 -1 -4294967209 domain_udt_usage NULL NULL false 0 -1 -4294967210 domain_constraints NULL NULL false 0 -1 -4294967211 data_type_privileges NULL NULL false 0 -1 -4294967212 constraint_table_usage NULL NULL false 0 -1 -4294967213 constraint_column_usage NULL NULL false 0 -1 -4294967214 columns NULL NULL false 0 -1 -4294967215 columns_extensions NULL NULL false 0 -1 -4294967216 column_udt_usage NULL NULL false 0 -1 -4294967217 column_statistics NULL NULL false 0 -1 -4294967218 column_privileges NULL NULL false 0 -1 -4294967219 column_options NULL NULL false 0 -1 -4294967220 column_domain_usage NULL NULL false 0 -1 -4294967221 column_column_usage NULL NULL false 0 -1 -4294967222 collations NULL NULL false 0 -1 -4294967223 collation_character_set_applicability NULL NULL false 0 -1 -4294967224 check_constraints NULL NULL false 0 -1 -4294967225 check_constraint_routine_usage NULL NULL false 0 -1 -4294967226 character_sets NULL NULL false 0 -1 -4294967227 attributes NULL NULL false 0 -1 -4294967228 applicable_roles NULL NULL false 0 -1 -4294967229 administrable_role_authorizations NULL NULL false 0 -1 -4294967231 tenant_usage_details NULL NULL false 0 -1 -4294967232 active_range_feeds NULL NULL false 0 -1 -4294967233 default_privileges NULL NULL false 0 -1 -4294967234 regions NULL NULL false 0 -1 -4294967235 cluster_inflight_traces NULL NULL false 0 -1 -4294967236 lost_descriptors_with_data NULL NULL false 0 -1 -4294967237 cross_db_references NULL NULL false 0 -1 -4294967238 interleaved NULL NULL false 0 -1 +4294967011 spatial_ref_sys NULL NULL false 0 -1 +4294967012 geometry_columns NULL NULL false 0 -1 +4294967013 geography_columns NULL NULL false 0 -1 +4294967015 pg_views NULL NULL false 0 -1 +4294967016 pg_user NULL NULL false 0 -1 +4294967017 pg_user_mappings NULL NULL false 0 -1 +4294967018 pg_user_mapping NULL NULL false 0 -1 +4294967019 pg_type NULL NULL false 0 -1 +4294967020 pg_ts_template NULL NULL false 0 -1 +4294967021 pg_ts_parser NULL NULL false 0 -1 +4294967022 pg_ts_dict NULL NULL false 0 -1 +4294967023 pg_ts_config NULL NULL false 0 -1 +4294967024 pg_ts_config_map NULL NULL false 0 -1 +4294967025 pg_trigger NULL NULL false 0 -1 +4294967026 pg_transform NULL NULL false 0 -1 +4294967027 pg_timezone_names NULL NULL false 0 -1 +4294967028 pg_timezone_abbrevs NULL NULL false 0 -1 +4294967029 pg_tablespace NULL NULL false 0 -1 +4294967030 pg_tables NULL NULL false 0 -1 +4294967031 pg_subscription NULL NULL false 0 -1 +4294967032 pg_subscription_rel NULL NULL false 0 -1 +4294967033 pg_stats NULL NULL false 0 -1 +4294967034 pg_stats_ext NULL NULL false 0 -1 +4294967035 pg_statistic NULL NULL false 0 -1 +4294967036 pg_statistic_ext NULL NULL false 0 -1 +4294967037 pg_statistic_ext_data NULL NULL false 0 -1 +4294967038 pg_statio_user_tables NULL NULL false 0 -1 +4294967039 pg_statio_user_sequences NULL NULL false 0 -1 +4294967040 pg_statio_user_indexes NULL NULL false 0 -1 +4294967041 pg_statio_sys_tables NULL NULL false 0 -1 +4294967042 pg_statio_sys_sequences NULL NULL false 0 -1 +4294967043 pg_statio_sys_indexes NULL NULL false 0 -1 +4294967044 pg_statio_all_tables NULL NULL false 0 -1 +4294967045 pg_statio_all_sequences NULL NULL false 0 -1 +4294967046 pg_statio_all_indexes NULL NULL false 0 -1 +4294967047 pg_stat_xact_user_tables NULL NULL false 0 -1 +4294967048 pg_stat_xact_user_functions NULL NULL false 0 -1 +4294967049 pg_stat_xact_sys_tables NULL NULL false 0 -1 +4294967050 pg_stat_xact_all_tables NULL NULL false 0 -1 +4294967051 pg_stat_wal_receiver NULL NULL false 0 -1 +4294967052 pg_stat_user_tables NULL NULL false 0 -1 +4294967053 pg_stat_user_indexes NULL NULL false 0 -1 +4294967054 pg_stat_user_functions NULL NULL false 0 -1 +4294967055 pg_stat_sys_tables NULL NULL false 0 -1 +4294967056 pg_stat_sys_indexes NULL NULL false 0 -1 +4294967057 pg_stat_subscription NULL NULL false 0 -1 +4294967058 pg_stat_ssl NULL NULL false 0 -1 +4294967059 pg_stat_slru NULL NULL false 0 -1 +4294967060 pg_stat_replication NULL NULL false 0 -1 +4294967061 pg_stat_progress_vacuum NULL NULL false 0 -1 +4294967062 pg_stat_progress_create_index NULL NULL false 0 -1 +4294967063 pg_stat_progress_cluster NULL NULL false 0 -1 +4294967064 pg_stat_progress_basebackup NULL NULL false 0 -1 +4294967065 pg_stat_progress_analyze NULL NULL false 0 -1 +4294967066 pg_stat_gssapi NULL NULL false 0 -1 +4294967067 pg_stat_database NULL NULL false 0 -1 +4294967068 pg_stat_database_conflicts NULL NULL false 0 -1 +4294967069 pg_stat_bgwriter NULL NULL false 0 -1 +4294967070 pg_stat_archiver NULL NULL false 0 -1 +4294967071 pg_stat_all_tables NULL NULL false 0 -1 +4294967072 pg_stat_all_indexes NULL NULL false 0 -1 +4294967073 pg_stat_activity NULL NULL false 0 -1 +4294967074 pg_shmem_allocations NULL NULL false 0 -1 +4294967075 pg_shdepend NULL NULL false 0 -1 +4294967076 pg_shseclabel NULL NULL false 0 -1 +4294967077 pg_shdescription NULL NULL false 0 -1 +4294967078 pg_shadow NULL NULL false 0 -1 +4294967079 pg_settings NULL NULL false 0 -1 +4294967080 pg_sequences NULL NULL false 0 -1 +4294967081 pg_sequence NULL NULL false 0 -1 +4294967082 pg_seclabel NULL NULL false 0 -1 +4294967083 pg_seclabels NULL NULL false 0 -1 +4294967084 pg_rules NULL NULL false 0 -1 +4294967085 pg_roles NULL NULL false 0 -1 +4294967086 pg_rewrite NULL NULL false 0 -1 +4294967087 pg_replication_slots NULL NULL false 0 -1 +4294967088 pg_replication_origin NULL NULL false 0 -1 +4294967089 pg_replication_origin_status NULL NULL false 0 -1 +4294967090 pg_range NULL NULL false 0 -1 +4294967091 pg_publication_tables NULL NULL false 0 -1 +4294967092 pg_publication NULL NULL false 0 -1 +4294967093 pg_publication_rel NULL NULL false 0 -1 +4294967094 pg_proc NULL NULL false 0 -1 +4294967095 pg_prepared_xacts NULL NULL false 0 -1 +4294967096 pg_prepared_statements NULL NULL false 0 -1 +4294967097 pg_policy NULL NULL false 0 -1 +4294967098 pg_policies NULL NULL false 0 -1 +4294967099 pg_partitioned_table NULL NULL false 0 -1 +4294967100 pg_opfamily NULL NULL false 0 -1 +4294967101 pg_operator NULL NULL false 0 -1 +4294967102 pg_opclass NULL NULL false 0 -1 +4294967103 pg_namespace NULL NULL false 0 -1 +4294967104 pg_matviews NULL NULL false 0 -1 +4294967105 pg_locks NULL NULL false 0 -1 +4294967106 pg_largeobject NULL NULL false 0 -1 +4294967107 pg_largeobject_metadata NULL NULL false 0 -1 +4294967108 pg_language NULL NULL false 0 -1 +4294967109 pg_init_privs NULL NULL false 0 -1 +4294967110 pg_inherits NULL NULL false 0 -1 +4294967111 pg_indexes NULL NULL false 0 -1 +4294967112 pg_index NULL NULL false 0 -1 +4294967113 pg_hba_file_rules NULL NULL false 0 -1 +4294967114 pg_group NULL NULL false 0 -1 +4294967115 pg_foreign_table NULL NULL false 0 -1 +4294967116 pg_foreign_server NULL NULL false 0 -1 +4294967117 pg_foreign_data_wrapper NULL NULL false 0 -1 +4294967118 pg_file_settings NULL NULL false 0 -1 +4294967119 pg_extension NULL NULL false 0 -1 +4294967120 pg_event_trigger NULL NULL false 0 -1 +4294967121 pg_enum NULL NULL false 0 -1 +4294967122 pg_description NULL NULL false 0 -1 +4294967123 pg_depend NULL NULL false 0 -1 +4294967124 pg_default_acl NULL NULL false 0 -1 +4294967125 pg_db_role_setting NULL NULL false 0 -1 +4294967126 pg_database NULL NULL false 0 -1 +4294967127 pg_cursors NULL NULL false 0 -1 +4294967128 pg_conversion NULL NULL false 0 -1 +4294967129 pg_constraint NULL NULL false 0 -1 +4294967130 pg_config NULL NULL false 0 -1 +4294967131 pg_collation NULL NULL false 0 -1 +4294967132 pg_class NULL NULL false 0 -1 +4294967133 pg_cast NULL NULL false 0 -1 +4294967134 pg_available_extensions NULL NULL false 0 -1 +4294967135 pg_available_extension_versions NULL NULL false 0 -1 +4294967136 pg_auth_members NULL NULL false 0 -1 +4294967137 pg_authid NULL NULL false 0 -1 +4294967138 pg_attribute NULL NULL false 0 -1 +4294967139 pg_attrdef NULL NULL false 0 -1 +4294967140 pg_amproc NULL NULL false 0 -1 +4294967141 pg_amop NULL NULL false 0 -1 +4294967142 pg_am NULL NULL false 0 -1 +4294967143 pg_aggregate NULL NULL false 0 -1 +4294967145 views NULL NULL false 0 -1 +4294967146 view_table_usage NULL NULL false 0 -1 +4294967147 view_routine_usage NULL NULL false 0 -1 +4294967148 view_column_usage NULL NULL false 0 -1 +4294967149 user_privileges NULL NULL false 0 -1 +4294967150 user_mappings NULL NULL false 0 -1 +4294967151 user_mapping_options NULL NULL false 0 -1 +4294967152 user_defined_types NULL NULL false 0 -1 +4294967153 user_attributes NULL NULL false 0 -1 +4294967154 usage_privileges NULL NULL false 0 -1 +4294967155 udt_privileges NULL NULL false 0 -1 +4294967156 type_privileges NULL NULL false 0 -1 +4294967157 triggers NULL NULL false 0 -1 +4294967158 triggered_update_columns NULL NULL false 0 -1 +4294967159 transforms NULL NULL false 0 -1 +4294967160 tablespaces NULL NULL false 0 -1 +4294967161 tablespaces_extensions NULL NULL false 0 -1 +4294967162 tables NULL NULL false 0 -1 +4294967163 tables_extensions NULL NULL false 0 -1 +4294967164 table_privileges NULL NULL false 0 -1 +4294967165 table_constraints_extensions NULL NULL false 0 -1 +4294967166 table_constraints NULL NULL false 0 -1 +4294967167 statistics NULL NULL false 0 -1 +4294967168 st_units_of_measure NULL NULL false 0 -1 +4294967169 st_spatial_reference_systems NULL NULL false 0 -1 +4294967170 st_geometry_columns NULL NULL false 0 -1 +4294967171 session_variables NULL NULL false 0 -1 +4294967172 sequences NULL NULL false 0 -1 +4294967173 schema_privileges NULL NULL false 0 -1 +4294967174 schemata NULL NULL false 0 -1 +4294967175 schemata_extensions NULL NULL false 0 -1 +4294967176 sql_sizing NULL NULL false 0 -1 +4294967177 sql_parts NULL NULL false 0 -1 +4294967178 sql_implementation_info NULL NULL false 0 -1 +4294967179 sql_features NULL NULL false 0 -1 +4294967180 routines NULL NULL false 0 -1 +4294967181 routine_privileges NULL NULL false 0 -1 +4294967182 role_usage_grants NULL NULL false 0 -1 +4294967183 role_udt_grants NULL NULL false 0 -1 +4294967184 role_table_grants NULL NULL false 0 -1 +4294967185 role_routine_grants NULL NULL false 0 -1 +4294967186 role_column_grants NULL NULL false 0 -1 +4294967187 resource_groups NULL NULL false 0 -1 +4294967188 referential_constraints NULL NULL false 0 -1 +4294967189 profiling NULL NULL false 0 -1 +4294967190 processlist NULL NULL false 0 -1 +4294967191 plugins NULL NULL false 0 -1 +4294967192 partitions NULL NULL false 0 -1 +4294967193 parameters NULL NULL false 0 -1 +4294967194 optimizer_trace NULL NULL false 0 -1 +4294967195 keywords NULL NULL false 0 -1 +4294967196 key_column_usage NULL NULL false 0 -1 +4294967197 information_schema_catalog_name NULL NULL false 0 -1 +4294967198 foreign_tables NULL NULL false 0 -1 +4294967199 foreign_table_options NULL NULL false 0 -1 +4294967200 foreign_servers NULL NULL false 0 -1 +4294967201 foreign_server_options NULL NULL false 0 -1 +4294967202 foreign_data_wrappers NULL NULL false 0 -1 +4294967203 foreign_data_wrapper_options NULL NULL false 0 -1 +4294967204 files NULL NULL false 0 -1 +4294967205 events NULL NULL false 0 -1 +4294967206 engines NULL NULL false 0 -1 +4294967207 enabled_roles NULL NULL false 0 -1 +4294967208 element_types NULL NULL false 0 -1 +4294967209 domains NULL NULL false 0 -1 +4294967210 domain_udt_usage NULL NULL false 0 -1 +4294967211 domain_constraints NULL NULL false 0 -1 +4294967212 data_type_privileges NULL NULL false 0 -1 +4294967213 constraint_table_usage NULL NULL false 0 -1 +4294967214 constraint_column_usage NULL NULL false 0 -1 +4294967215 columns NULL NULL false 0 -1 +4294967216 columns_extensions NULL NULL false 0 -1 +4294967217 column_udt_usage NULL NULL false 0 -1 +4294967218 column_statistics NULL NULL false 0 -1 +4294967219 column_privileges NULL NULL false 0 -1 +4294967220 column_options NULL NULL false 0 -1 +4294967221 column_domain_usage NULL NULL false 0 -1 +4294967222 column_column_usage NULL NULL false 0 -1 +4294967223 collations NULL NULL false 0 -1 +4294967224 collation_character_set_applicability NULL NULL false 0 -1 +4294967225 check_constraints NULL NULL false 0 -1 +4294967226 check_constraint_routine_usage NULL NULL false 0 -1 +4294967227 character_sets NULL NULL false 0 -1 +4294967228 attributes NULL NULL false 0 -1 +4294967229 applicable_roles NULL NULL false 0 -1 +4294967230 administrable_role_authorizations NULL NULL false 0 -1 +4294967232 tenant_usage_details NULL NULL false 0 -1 +4294967233 active_range_feeds NULL NULL false 0 -1 +4294967234 default_privileges NULL NULL false 0 -1 +4294967235 regions NULL NULL false 0 -1 +4294967236 cluster_inflight_traces NULL NULL false 0 -1 +4294967237 lost_descriptors_with_data NULL NULL false 0 -1 +4294967238 cross_db_references NULL NULL false 0 -1 4294967239 cluster_database_privileges NULL NULL false 0 -1 4294967240 invalid_objects NULL NULL false 0 -1 4294967241 zones NULL NULL false 0 -1 @@ -3161,232 +3157,231 @@ oid typname typndims typcollation typde 100076 _newtype1 0 0 NULL NULL NULL 100077 newtype2 0 0 NULL NULL NULL 100078 _newtype2 0 0 NULL NULL NULL -4294967010 spatial_ref_sys 0 0 NULL NULL NULL -4294967011 geometry_columns 0 0 NULL NULL NULL -4294967012 geography_columns 0 0 NULL NULL NULL -4294967014 pg_views 0 0 NULL NULL NULL -4294967015 pg_user 0 0 NULL NULL NULL -4294967016 pg_user_mappings 0 0 NULL NULL NULL -4294967017 pg_user_mapping 0 0 NULL NULL NULL -4294967018 pg_type 0 0 NULL NULL NULL -4294967019 pg_ts_template 0 0 NULL NULL NULL -4294967020 pg_ts_parser 0 0 NULL NULL NULL -4294967021 pg_ts_dict 0 0 NULL NULL NULL -4294967022 pg_ts_config 0 0 NULL NULL NULL -4294967023 pg_ts_config_map 0 0 NULL NULL NULL -4294967024 pg_trigger 0 0 NULL NULL NULL -4294967025 pg_transform 0 0 NULL NULL NULL -4294967026 pg_timezone_names 0 0 NULL NULL NULL -4294967027 pg_timezone_abbrevs 0 0 NULL NULL NULL -4294967028 pg_tablespace 0 0 NULL NULL NULL -4294967029 pg_tables 0 0 NULL NULL NULL -4294967030 pg_subscription 0 0 NULL NULL NULL -4294967031 pg_subscription_rel 0 0 NULL NULL NULL -4294967032 pg_stats 0 0 NULL NULL NULL -4294967033 pg_stats_ext 0 0 NULL NULL NULL -4294967034 pg_statistic 0 0 NULL NULL NULL -4294967035 pg_statistic_ext 0 0 NULL NULL NULL -4294967036 pg_statistic_ext_data 0 0 NULL NULL NULL -4294967037 pg_statio_user_tables 0 0 NULL NULL NULL -4294967038 pg_statio_user_sequences 0 0 NULL NULL NULL -4294967039 pg_statio_user_indexes 0 0 NULL NULL NULL -4294967040 pg_statio_sys_tables 0 0 NULL NULL NULL -4294967041 pg_statio_sys_sequences 0 0 NULL NULL NULL -4294967042 pg_statio_sys_indexes 0 0 NULL NULL NULL -4294967043 pg_statio_all_tables 0 0 NULL NULL NULL -4294967044 pg_statio_all_sequences 0 0 NULL NULL NULL -4294967045 pg_statio_all_indexes 0 0 NULL NULL NULL -4294967046 pg_stat_xact_user_tables 0 0 NULL NULL NULL -4294967047 pg_stat_xact_user_functions 0 0 NULL NULL NULL -4294967048 pg_stat_xact_sys_tables 0 0 NULL NULL NULL -4294967049 pg_stat_xact_all_tables 0 0 NULL NULL NULL -4294967050 pg_stat_wal_receiver 0 0 NULL NULL NULL -4294967051 pg_stat_user_tables 0 0 NULL NULL NULL -4294967052 pg_stat_user_indexes 0 0 NULL NULL NULL -4294967053 pg_stat_user_functions 0 0 NULL NULL NULL -4294967054 pg_stat_sys_tables 0 0 NULL NULL NULL -4294967055 pg_stat_sys_indexes 0 0 NULL NULL NULL -4294967056 pg_stat_subscription 0 0 NULL NULL NULL -4294967057 pg_stat_ssl 0 0 NULL NULL NULL -4294967058 pg_stat_slru 0 0 NULL NULL NULL -4294967059 pg_stat_replication 0 0 NULL NULL NULL -4294967060 pg_stat_progress_vacuum 0 0 NULL NULL NULL -4294967061 pg_stat_progress_create_index 0 0 NULL NULL NULL -4294967062 pg_stat_progress_cluster 0 0 NULL NULL NULL -4294967063 pg_stat_progress_basebackup 0 0 NULL NULL NULL -4294967064 pg_stat_progress_analyze 0 0 NULL NULL NULL -4294967065 pg_stat_gssapi 0 0 NULL NULL NULL -4294967066 pg_stat_database 0 0 NULL NULL NULL -4294967067 pg_stat_database_conflicts 0 0 NULL NULL NULL -4294967068 pg_stat_bgwriter 0 0 NULL NULL NULL -4294967069 pg_stat_archiver 0 0 NULL NULL NULL -4294967070 pg_stat_all_tables 0 0 NULL NULL NULL -4294967071 pg_stat_all_indexes 0 0 NULL NULL NULL -4294967072 pg_stat_activity 0 0 NULL NULL NULL -4294967073 pg_shmem_allocations 0 0 NULL NULL NULL -4294967074 pg_shdepend 0 0 NULL NULL NULL -4294967075 pg_shseclabel 0 0 NULL NULL NULL -4294967076 pg_shdescription 0 0 NULL NULL NULL -4294967077 pg_shadow 0 0 NULL NULL NULL -4294967078 pg_settings 0 0 NULL NULL NULL -4294967079 pg_sequences 0 0 NULL NULL NULL -4294967080 pg_sequence 0 0 NULL NULL NULL -4294967081 pg_seclabel 0 0 NULL NULL NULL -4294967082 pg_seclabels 0 0 NULL NULL NULL -4294967083 pg_rules 0 0 NULL NULL NULL -4294967084 pg_roles 0 0 NULL NULL NULL -4294967085 pg_rewrite 0 0 NULL NULL NULL -4294967086 pg_replication_slots 0 0 NULL NULL NULL -4294967087 pg_replication_origin 0 0 NULL NULL NULL -4294967088 pg_replication_origin_status 0 0 NULL NULL NULL -4294967089 pg_range 0 0 NULL NULL NULL -4294967090 pg_publication_tables 0 0 NULL NULL NULL -4294967091 pg_publication 0 0 NULL NULL NULL -4294967092 pg_publication_rel 0 0 NULL NULL NULL -4294967093 pg_proc 0 0 NULL NULL NULL -4294967094 pg_prepared_xacts 0 0 NULL NULL NULL -4294967095 pg_prepared_statements 0 0 NULL NULL NULL -4294967096 pg_policy 0 0 NULL NULL NULL -4294967097 pg_policies 0 0 NULL NULL NULL -4294967098 pg_partitioned_table 0 0 NULL NULL NULL -4294967099 pg_opfamily 0 0 NULL NULL NULL -4294967100 pg_operator 0 0 NULL NULL NULL -4294967101 pg_opclass 0 0 NULL NULL NULL -4294967102 pg_namespace 0 0 NULL NULL NULL -4294967103 pg_matviews 0 0 NULL NULL NULL -4294967104 pg_locks 0 0 NULL NULL NULL -4294967105 pg_largeobject 0 0 NULL NULL NULL -4294967106 pg_largeobject_metadata 0 0 NULL NULL NULL -4294967107 pg_language 0 0 NULL NULL NULL -4294967108 pg_init_privs 0 0 NULL NULL NULL -4294967109 pg_inherits 0 0 NULL NULL NULL -4294967110 pg_indexes 0 0 NULL NULL NULL -4294967111 pg_index 0 0 NULL NULL NULL -4294967112 pg_hba_file_rules 0 0 NULL NULL NULL -4294967113 pg_group 0 0 NULL NULL NULL -4294967114 pg_foreign_table 0 0 NULL NULL NULL -4294967115 pg_foreign_server 0 0 NULL NULL NULL -4294967116 pg_foreign_data_wrapper 0 0 NULL NULL NULL -4294967117 pg_file_settings 0 0 NULL NULL NULL -4294967118 pg_extension 0 0 NULL NULL NULL -4294967119 pg_event_trigger 0 0 NULL NULL NULL -4294967120 pg_enum 0 0 NULL NULL NULL -4294967121 pg_description 0 0 NULL NULL NULL -4294967122 pg_depend 0 0 NULL NULL NULL -4294967123 pg_default_acl 0 0 NULL NULL NULL -4294967124 pg_db_role_setting 0 0 NULL NULL NULL -4294967125 pg_database 0 0 NULL NULL NULL -4294967126 pg_cursors 0 0 NULL NULL NULL -4294967127 pg_conversion 0 0 NULL NULL NULL -4294967128 pg_constraint 0 0 NULL NULL NULL -4294967129 pg_config 0 0 NULL NULL NULL -4294967130 pg_collation 0 0 NULL NULL NULL -4294967131 pg_class 0 0 NULL NULL NULL -4294967132 pg_cast 0 0 NULL NULL NULL -4294967133 pg_available_extensions 0 0 NULL NULL NULL -4294967134 pg_available_extension_versions 0 0 NULL NULL NULL -4294967135 pg_auth_members 0 0 NULL NULL NULL -4294967136 pg_authid 0 0 NULL NULL NULL -4294967137 pg_attribute 0 0 NULL NULL NULL -4294967138 pg_attrdef 0 0 NULL NULL NULL -4294967139 pg_amproc 0 0 NULL NULL NULL -4294967140 pg_amop 0 0 NULL NULL NULL -4294967141 pg_am 0 0 NULL NULL NULL -4294967142 pg_aggregate 0 0 NULL NULL NULL -4294967144 views 0 0 NULL NULL NULL -4294967145 view_table_usage 0 0 NULL NULL NULL -4294967146 view_routine_usage 0 0 NULL NULL NULL -4294967147 view_column_usage 0 0 NULL NULL NULL -4294967148 user_privileges 0 0 NULL NULL NULL -4294967149 user_mappings 0 0 NULL NULL NULL -4294967150 user_mapping_options 0 0 NULL NULL NULL -4294967151 user_defined_types 0 0 NULL NULL NULL -4294967152 user_attributes 0 0 NULL NULL NULL -4294967153 usage_privileges 0 0 NULL NULL NULL -4294967154 udt_privileges 0 0 NULL NULL NULL -4294967155 type_privileges 0 0 NULL NULL NULL -4294967156 triggers 0 0 NULL NULL NULL -4294967157 triggered_update_columns 0 0 NULL NULL NULL -4294967158 transforms 0 0 NULL NULL NULL -4294967159 tablespaces 0 0 NULL NULL NULL -4294967160 tablespaces_extensions 0 0 NULL NULL NULL -4294967161 tables 0 0 NULL NULL NULL -4294967162 tables_extensions 0 0 NULL NULL NULL -4294967163 table_privileges 0 0 NULL NULL NULL -4294967164 table_constraints_extensions 0 0 NULL NULL NULL -4294967165 table_constraints 0 0 NULL NULL NULL -4294967166 statistics 0 0 NULL NULL NULL -4294967167 st_units_of_measure 0 0 NULL NULL NULL -4294967168 st_spatial_reference_systems 0 0 NULL NULL NULL -4294967169 st_geometry_columns 0 0 NULL NULL NULL -4294967170 session_variables 0 0 NULL NULL NULL -4294967171 sequences 0 0 NULL NULL NULL -4294967172 schema_privileges 0 0 NULL NULL NULL -4294967173 schemata 0 0 NULL NULL NULL -4294967174 schemata_extensions 0 0 NULL NULL NULL -4294967175 sql_sizing 0 0 NULL NULL NULL -4294967176 sql_parts 0 0 NULL NULL NULL -4294967177 sql_implementation_info 0 0 NULL NULL NULL -4294967178 sql_features 0 0 NULL NULL NULL -4294967179 routines 0 0 NULL NULL NULL -4294967180 routine_privileges 0 0 NULL NULL NULL -4294967181 role_usage_grants 0 0 NULL NULL NULL -4294967182 role_udt_grants 0 0 NULL NULL NULL -4294967183 role_table_grants 0 0 NULL NULL NULL -4294967184 role_routine_grants 0 0 NULL NULL NULL -4294967185 role_column_grants 0 0 NULL NULL NULL -4294967186 resource_groups 0 0 NULL NULL NULL -4294967187 referential_constraints 0 0 NULL NULL NULL -4294967188 profiling 0 0 NULL NULL NULL -4294967189 processlist 0 0 NULL NULL NULL -4294967190 plugins 0 0 NULL NULL NULL -4294967191 partitions 0 0 NULL NULL NULL -4294967192 parameters 0 0 NULL NULL NULL -4294967193 optimizer_trace 0 0 NULL NULL NULL -4294967194 keywords 0 0 NULL NULL NULL -4294967195 key_column_usage 0 0 NULL NULL NULL -4294967196 information_schema_catalog_name 0 0 NULL NULL NULL -4294967197 foreign_tables 0 0 NULL NULL NULL -4294967198 foreign_table_options 0 0 NULL NULL NULL -4294967199 foreign_servers 0 0 NULL NULL NULL -4294967200 foreign_server_options 0 0 NULL NULL NULL -4294967201 foreign_data_wrappers 0 0 NULL NULL NULL -4294967202 foreign_data_wrapper_options 0 0 NULL NULL NULL -4294967203 files 0 0 NULL NULL NULL -4294967204 events 0 0 NULL NULL NULL -4294967205 engines 0 0 NULL NULL NULL -4294967206 enabled_roles 0 0 NULL NULL NULL -4294967207 element_types 0 0 NULL NULL NULL -4294967208 domains 0 0 NULL NULL NULL -4294967209 domain_udt_usage 0 0 NULL NULL NULL -4294967210 domain_constraints 0 0 NULL NULL NULL -4294967211 data_type_privileges 0 0 NULL NULL NULL -4294967212 constraint_table_usage 0 0 NULL NULL NULL -4294967213 constraint_column_usage 0 0 NULL NULL NULL -4294967214 columns 0 0 NULL NULL NULL -4294967215 columns_extensions 0 0 NULL NULL NULL -4294967216 column_udt_usage 0 0 NULL NULL NULL -4294967217 column_statistics 0 0 NULL NULL NULL -4294967218 column_privileges 0 0 NULL NULL NULL -4294967219 column_options 0 0 NULL NULL NULL -4294967220 column_domain_usage 0 0 NULL NULL NULL -4294967221 column_column_usage 0 0 NULL NULL NULL -4294967222 collations 0 0 NULL NULL NULL -4294967223 collation_character_set_applicability 0 0 NULL NULL NULL -4294967224 check_constraints 0 0 NULL NULL NULL -4294967225 check_constraint_routine_usage 0 0 NULL NULL NULL -4294967226 character_sets 0 0 NULL NULL NULL -4294967227 attributes 0 0 NULL NULL NULL -4294967228 applicable_roles 0 0 NULL NULL NULL -4294967229 administrable_role_authorizations 0 0 NULL NULL NULL -4294967231 tenant_usage_details 0 0 NULL NULL NULL -4294967232 active_range_feeds 0 0 NULL NULL NULL -4294967233 default_privileges 0 0 NULL NULL NULL -4294967234 regions 0 0 NULL NULL NULL -4294967235 cluster_inflight_traces 0 0 NULL NULL NULL -4294967236 lost_descriptors_with_data 0 0 NULL NULL NULL -4294967237 cross_db_references 0 0 NULL NULL NULL -4294967238 interleaved 0 0 NULL NULL NULL +4294967011 spatial_ref_sys 0 0 NULL NULL NULL +4294967012 geometry_columns 0 0 NULL NULL NULL +4294967013 geography_columns 0 0 NULL NULL NULL +4294967015 pg_views 0 0 NULL NULL NULL +4294967016 pg_user 0 0 NULL NULL NULL +4294967017 pg_user_mappings 0 0 NULL NULL NULL +4294967018 pg_user_mapping 0 0 NULL NULL NULL +4294967019 pg_type 0 0 NULL NULL NULL +4294967020 pg_ts_template 0 0 NULL NULL NULL +4294967021 pg_ts_parser 0 0 NULL NULL NULL +4294967022 pg_ts_dict 0 0 NULL NULL NULL +4294967023 pg_ts_config 0 0 NULL NULL NULL +4294967024 pg_ts_config_map 0 0 NULL NULL NULL +4294967025 pg_trigger 0 0 NULL NULL NULL +4294967026 pg_transform 0 0 NULL NULL NULL +4294967027 pg_timezone_names 0 0 NULL NULL NULL +4294967028 pg_timezone_abbrevs 0 0 NULL NULL NULL +4294967029 pg_tablespace 0 0 NULL NULL NULL +4294967030 pg_tables 0 0 NULL NULL NULL +4294967031 pg_subscription 0 0 NULL NULL NULL +4294967032 pg_subscription_rel 0 0 NULL NULL NULL +4294967033 pg_stats 0 0 NULL NULL NULL +4294967034 pg_stats_ext 0 0 NULL NULL NULL +4294967035 pg_statistic 0 0 NULL NULL NULL +4294967036 pg_statistic_ext 0 0 NULL NULL NULL +4294967037 pg_statistic_ext_data 0 0 NULL NULL NULL +4294967038 pg_statio_user_tables 0 0 NULL NULL NULL +4294967039 pg_statio_user_sequences 0 0 NULL NULL NULL +4294967040 pg_statio_user_indexes 0 0 NULL NULL NULL +4294967041 pg_statio_sys_tables 0 0 NULL NULL NULL +4294967042 pg_statio_sys_sequences 0 0 NULL NULL NULL +4294967043 pg_statio_sys_indexes 0 0 NULL NULL NULL +4294967044 pg_statio_all_tables 0 0 NULL NULL NULL +4294967045 pg_statio_all_sequences 0 0 NULL NULL NULL +4294967046 pg_statio_all_indexes 0 0 NULL NULL NULL +4294967047 pg_stat_xact_user_tables 0 0 NULL NULL NULL +4294967048 pg_stat_xact_user_functions 0 0 NULL NULL NULL +4294967049 pg_stat_xact_sys_tables 0 0 NULL NULL NULL +4294967050 pg_stat_xact_all_tables 0 0 NULL NULL NULL +4294967051 pg_stat_wal_receiver 0 0 NULL NULL NULL +4294967052 pg_stat_user_tables 0 0 NULL NULL NULL +4294967053 pg_stat_user_indexes 0 0 NULL NULL NULL +4294967054 pg_stat_user_functions 0 0 NULL NULL NULL +4294967055 pg_stat_sys_tables 0 0 NULL NULL NULL +4294967056 pg_stat_sys_indexes 0 0 NULL NULL NULL +4294967057 pg_stat_subscription 0 0 NULL NULL NULL +4294967058 pg_stat_ssl 0 0 NULL NULL NULL +4294967059 pg_stat_slru 0 0 NULL NULL NULL +4294967060 pg_stat_replication 0 0 NULL NULL NULL +4294967061 pg_stat_progress_vacuum 0 0 NULL NULL NULL +4294967062 pg_stat_progress_create_index 0 0 NULL NULL NULL +4294967063 pg_stat_progress_cluster 0 0 NULL NULL NULL +4294967064 pg_stat_progress_basebackup 0 0 NULL NULL NULL +4294967065 pg_stat_progress_analyze 0 0 NULL NULL NULL +4294967066 pg_stat_gssapi 0 0 NULL NULL NULL +4294967067 pg_stat_database 0 0 NULL NULL NULL +4294967068 pg_stat_database_conflicts 0 0 NULL NULL NULL +4294967069 pg_stat_bgwriter 0 0 NULL NULL NULL +4294967070 pg_stat_archiver 0 0 NULL NULL NULL +4294967071 pg_stat_all_tables 0 0 NULL NULL NULL +4294967072 pg_stat_all_indexes 0 0 NULL NULL NULL +4294967073 pg_stat_activity 0 0 NULL NULL NULL +4294967074 pg_shmem_allocations 0 0 NULL NULL NULL +4294967075 pg_shdepend 0 0 NULL NULL NULL +4294967076 pg_shseclabel 0 0 NULL NULL NULL +4294967077 pg_shdescription 0 0 NULL NULL NULL +4294967078 pg_shadow 0 0 NULL NULL NULL +4294967079 pg_settings 0 0 NULL NULL NULL +4294967080 pg_sequences 0 0 NULL NULL NULL +4294967081 pg_sequence 0 0 NULL NULL NULL +4294967082 pg_seclabel 0 0 NULL NULL NULL +4294967083 pg_seclabels 0 0 NULL NULL NULL +4294967084 pg_rules 0 0 NULL NULL NULL +4294967085 pg_roles 0 0 NULL NULL NULL +4294967086 pg_rewrite 0 0 NULL NULL NULL +4294967087 pg_replication_slots 0 0 NULL NULL NULL +4294967088 pg_replication_origin 0 0 NULL NULL NULL +4294967089 pg_replication_origin_status 0 0 NULL NULL NULL +4294967090 pg_range 0 0 NULL NULL NULL +4294967091 pg_publication_tables 0 0 NULL NULL NULL +4294967092 pg_publication 0 0 NULL NULL NULL +4294967093 pg_publication_rel 0 0 NULL NULL NULL +4294967094 pg_proc 0 0 NULL NULL NULL +4294967095 pg_prepared_xacts 0 0 NULL NULL NULL +4294967096 pg_prepared_statements 0 0 NULL NULL NULL +4294967097 pg_policy 0 0 NULL NULL NULL +4294967098 pg_policies 0 0 NULL NULL NULL +4294967099 pg_partitioned_table 0 0 NULL NULL NULL +4294967100 pg_opfamily 0 0 NULL NULL NULL +4294967101 pg_operator 0 0 NULL NULL NULL +4294967102 pg_opclass 0 0 NULL NULL NULL +4294967103 pg_namespace 0 0 NULL NULL NULL +4294967104 pg_matviews 0 0 NULL NULL NULL +4294967105 pg_locks 0 0 NULL NULL NULL +4294967106 pg_largeobject 0 0 NULL NULL NULL +4294967107 pg_largeobject_metadata 0 0 NULL NULL NULL +4294967108 pg_language 0 0 NULL NULL NULL +4294967109 pg_init_privs 0 0 NULL NULL NULL +4294967110 pg_inherits 0 0 NULL NULL NULL +4294967111 pg_indexes 0 0 NULL NULL NULL +4294967112 pg_index 0 0 NULL NULL NULL +4294967113 pg_hba_file_rules 0 0 NULL NULL NULL +4294967114 pg_group 0 0 NULL NULL NULL +4294967115 pg_foreign_table 0 0 NULL NULL NULL +4294967116 pg_foreign_server 0 0 NULL NULL NULL +4294967117 pg_foreign_data_wrapper 0 0 NULL NULL NULL +4294967118 pg_file_settings 0 0 NULL NULL NULL +4294967119 pg_extension 0 0 NULL NULL NULL +4294967120 pg_event_trigger 0 0 NULL NULL NULL +4294967121 pg_enum 0 0 NULL NULL NULL +4294967122 pg_description 0 0 NULL NULL NULL +4294967123 pg_depend 0 0 NULL NULL NULL +4294967124 pg_default_acl 0 0 NULL NULL NULL +4294967125 pg_db_role_setting 0 0 NULL NULL NULL +4294967126 pg_database 0 0 NULL NULL NULL +4294967127 pg_cursors 0 0 NULL NULL NULL +4294967128 pg_conversion 0 0 NULL NULL NULL +4294967129 pg_constraint 0 0 NULL NULL NULL +4294967130 pg_config 0 0 NULL NULL NULL +4294967131 pg_collation 0 0 NULL NULL NULL +4294967132 pg_class 0 0 NULL NULL NULL +4294967133 pg_cast 0 0 NULL NULL NULL +4294967134 pg_available_extensions 0 0 NULL NULL NULL +4294967135 pg_available_extension_versions 0 0 NULL NULL NULL +4294967136 pg_auth_members 0 0 NULL NULL NULL +4294967137 pg_authid 0 0 NULL NULL NULL +4294967138 pg_attribute 0 0 NULL NULL NULL +4294967139 pg_attrdef 0 0 NULL NULL NULL +4294967140 pg_amproc 0 0 NULL NULL NULL +4294967141 pg_amop 0 0 NULL NULL NULL +4294967142 pg_am 0 0 NULL NULL NULL +4294967143 pg_aggregate 0 0 NULL NULL NULL +4294967145 views 0 0 NULL NULL NULL +4294967146 view_table_usage 0 0 NULL NULL NULL +4294967147 view_routine_usage 0 0 NULL NULL NULL +4294967148 view_column_usage 0 0 NULL NULL NULL +4294967149 user_privileges 0 0 NULL NULL NULL +4294967150 user_mappings 0 0 NULL NULL NULL +4294967151 user_mapping_options 0 0 NULL NULL NULL +4294967152 user_defined_types 0 0 NULL NULL NULL +4294967153 user_attributes 0 0 NULL NULL NULL +4294967154 usage_privileges 0 0 NULL NULL NULL +4294967155 udt_privileges 0 0 NULL NULL NULL +4294967156 type_privileges 0 0 NULL NULL NULL +4294967157 triggers 0 0 NULL NULL NULL +4294967158 triggered_update_columns 0 0 NULL NULL NULL +4294967159 transforms 0 0 NULL NULL NULL +4294967160 tablespaces 0 0 NULL NULL NULL +4294967161 tablespaces_extensions 0 0 NULL NULL NULL +4294967162 tables 0 0 NULL NULL NULL +4294967163 tables_extensions 0 0 NULL NULL NULL +4294967164 table_privileges 0 0 NULL NULL NULL +4294967165 table_constraints_extensions 0 0 NULL NULL NULL +4294967166 table_constraints 0 0 NULL NULL NULL +4294967167 statistics 0 0 NULL NULL NULL +4294967168 st_units_of_measure 0 0 NULL NULL NULL +4294967169 st_spatial_reference_systems 0 0 NULL NULL NULL +4294967170 st_geometry_columns 0 0 NULL NULL NULL +4294967171 session_variables 0 0 NULL NULL NULL +4294967172 sequences 0 0 NULL NULL NULL +4294967173 schema_privileges 0 0 NULL NULL NULL +4294967174 schemata 0 0 NULL NULL NULL +4294967175 schemata_extensions 0 0 NULL NULL NULL +4294967176 sql_sizing 0 0 NULL NULL NULL +4294967177 sql_parts 0 0 NULL NULL NULL +4294967178 sql_implementation_info 0 0 NULL NULL NULL +4294967179 sql_features 0 0 NULL NULL NULL +4294967180 routines 0 0 NULL NULL NULL +4294967181 routine_privileges 0 0 NULL NULL NULL +4294967182 role_usage_grants 0 0 NULL NULL NULL +4294967183 role_udt_grants 0 0 NULL NULL NULL +4294967184 role_table_grants 0 0 NULL NULL NULL +4294967185 role_routine_grants 0 0 NULL NULL NULL +4294967186 role_column_grants 0 0 NULL NULL NULL +4294967187 resource_groups 0 0 NULL NULL NULL +4294967188 referential_constraints 0 0 NULL NULL NULL +4294967189 profiling 0 0 NULL NULL NULL +4294967190 processlist 0 0 NULL NULL NULL +4294967191 plugins 0 0 NULL NULL NULL +4294967192 partitions 0 0 NULL NULL NULL +4294967193 parameters 0 0 NULL NULL NULL +4294967194 optimizer_trace 0 0 NULL NULL NULL +4294967195 keywords 0 0 NULL NULL NULL +4294967196 key_column_usage 0 0 NULL NULL NULL +4294967197 information_schema_catalog_name 0 0 NULL NULL NULL +4294967198 foreign_tables 0 0 NULL NULL NULL +4294967199 foreign_table_options 0 0 NULL NULL NULL +4294967200 foreign_servers 0 0 NULL NULL NULL +4294967201 foreign_server_options 0 0 NULL NULL NULL +4294967202 foreign_data_wrappers 0 0 NULL NULL NULL +4294967203 foreign_data_wrapper_options 0 0 NULL NULL NULL +4294967204 files 0 0 NULL NULL NULL +4294967205 events 0 0 NULL NULL NULL +4294967206 engines 0 0 NULL NULL NULL +4294967207 enabled_roles 0 0 NULL NULL NULL +4294967208 element_types 0 0 NULL NULL NULL +4294967209 domains 0 0 NULL NULL NULL +4294967210 domain_udt_usage 0 0 NULL NULL NULL +4294967211 domain_constraints 0 0 NULL NULL NULL +4294967212 data_type_privileges 0 0 NULL NULL NULL +4294967213 constraint_table_usage 0 0 NULL NULL NULL +4294967214 constraint_column_usage 0 0 NULL NULL NULL +4294967215 columns 0 0 NULL NULL NULL +4294967216 columns_extensions 0 0 NULL NULL NULL +4294967217 column_udt_usage 0 0 NULL NULL NULL +4294967218 column_statistics 0 0 NULL NULL NULL +4294967219 column_privileges 0 0 NULL NULL NULL +4294967220 column_options 0 0 NULL NULL NULL +4294967221 column_domain_usage 0 0 NULL NULL NULL +4294967222 column_column_usage 0 0 NULL NULL NULL +4294967223 collations 0 0 NULL NULL NULL +4294967224 collation_character_set_applicability 0 0 NULL NULL NULL +4294967225 check_constraints 0 0 NULL NULL NULL +4294967226 check_constraint_routine_usage 0 0 NULL NULL NULL +4294967227 character_sets 0 0 NULL NULL NULL +4294967228 attributes 0 0 NULL NULL NULL +4294967229 applicable_roles 0 0 NULL NULL NULL +4294967230 administrable_role_authorizations 0 0 NULL NULL NULL +4294967232 tenant_usage_details 0 0 NULL NULL NULL +4294967233 active_range_feeds 0 0 NULL NULL NULL +4294967234 default_privileges 0 0 NULL NULL NULL +4294967235 regions 0 0 NULL NULL NULL +4294967236 cluster_inflight_traces 0 0 NULL NULL NULL +4294967237 lost_descriptors_with_data 0 0 NULL NULL NULL +4294967238 cross_db_references 0 0 NULL NULL NULL 4294967239 cluster_database_privileges 0 0 NULL NULL NULL 4294967240 invalid_objects 0 0 NULL NULL NULL 4294967241 zones 0 0 NULL NULL NULL @@ -3678,283 +3673,282 @@ SELECT objoid, classoid, objsubid, regexp_replace(description, e'\n.*', '') AS d FROM pg_catalog.pg_description ---- objoid classoid objsubid description -4294967232 4294967131 0 node-level table listing all currently running range feeds -4294967294 4294967131 0 backward inter-descriptor dependencies starting from tables accessible by current user in current database (KV scan) -4294967292 4294967131 0 built-in functions (RAM/static) -4294967288 4294967131 0 contention information (cluster RPC; expensive!) -4294967239 4294967131 0 virtual table with database privileges -4294967287 4294967131 0 DistSQL remote flows information (cluster RPC; expensive!) -4294967235 4294967131 0 traces for in-flight spans across all nodes in the cluster (cluster RPC; expensive!) -4294967286 4294967131 0 running queries visible by current user (cluster RPC; expensive!) -4294967284 4294967131 0 running sessions visible to current user (cluster RPC; expensive!) -4294967283 4294967131 0 cluster settings (RAM) -4294967285 4294967131 0 running user transactions visible by the current user (cluster RPC; expensive!) -4294967282 4294967131 0 CREATE statements for all user defined schemas accessible by the current user in current database (KV scan) -4294967281 4294967131 0 CREATE and ALTER statements for all tables accessible by current user in current database (KV scan) -4294967280 4294967131 0 CREATE statements for all user defined types accessible by the current user in current database (KV scan) -4294967237 4294967131 0 virtual table with cross db references -4294967279 4294967131 0 databases accessible by the current user (KV scan) -4294967233 4294967131 0 virtual table with default privileges -4294967278 4294967131 0 telemetry counters (RAM; local node only) -4294967277 4294967131 0 forward inter-descriptor dependencies starting from tables accessible by current user in current database (KV scan) -4294967274 4294967131 0 locally known gossiped health alerts (RAM; local node only) -4294967273 4294967131 0 locally known gossiped node liveness (RAM; local node only) -4294967272 4294967131 0 locally known edges in the gossip network (RAM; local node only) -4294967275 4294967131 0 locally known gossiped node details (RAM; local node only) -4294967271 4294967131 0 index columns for all indexes accessible by current user in current database (KV scan) -4294967270 4294967131 0 cluster-wide index usage statistics (in-memory, not durable).Querying this table is an expensive operation since it creates acluster-wide RPC fanout. -4294967238 4294967131 0 virtual table with interleaved table information -4294967240 4294967131 0 virtual table to validate descriptors -4294967268 4294967131 0 decoded job metadata from system.jobs (KV scan) -4294967276 4294967131 0 node liveness status, as seen by kv -4294967267 4294967131 0 node details across the entire cluster (cluster RPC; expensive!) -4294967266 4294967131 0 store details and status (cluster RPC; expensive!) -4294967265 4294967131 0 acquired table leases (RAM; local node only) -4294967236 4294967131 0 virtual table with table descriptors that still have data -4294967293 4294967131 0 detailed identification strings (RAM, local node only) -4294967264 4294967131 0 contention information (RAM; local node only) -4294967263 4294967131 0 DistSQL remote flows information (RAM; local node only) -4294967269 4294967131 0 in-flight spans (RAM; local node only) -4294967259 4294967131 0 current values for metrics (RAM; local node only) -4294967262 4294967131 0 running queries visible by current user (RAM; local node only) -4294967252 4294967131 0 server parameters, useful to construct connection URLs (RAM, local node only) -4294967260 4294967131 0 running sessions visible by current user (RAM; local node only) -4294967258 4294967131 0 statement statistics (in-memory, not durable; local node only). This table is wiped periodically (by default, at least every two hours) -4294967243 4294967131 0 finer-grained transaction statistics (in-memory, not durable; local node only). This table is wiped periodically (by default, at least every two hours) -4294967261 4294967131 0 running user transactions visible by the current user (RAM; local node only) -4294967257 4294967131 0 per-application transaction statistics (in-memory, not durable; local node only). This table is wiped periodically (by default, at least every two hours) -4294967256 4294967131 0 defined partitions for all tables/indexes accessible by the current user in the current database (KV scan) -4294967255 4294967131 0 comments for predefined virtual tables (RAM/static) -4294967254 4294967131 0 range metadata without leaseholder details (KV join; expensive!) -4294967234 4294967131 0 available regions for the cluster -4294967251 4294967131 0 ongoing schema changes, across all descriptors accessible by current user (KV scan; expensive!) -4294967250 4294967131 0 session trace accumulated so far (RAM) -4294967249 4294967131 0 session variables (RAM) -4294967248 4294967131 0 statement statistics (cluster-wide).Querying this table is an expensive operation since it creates a cluster-wide RPC-fanout. -4294967247 4294967131 0 details for all columns accessible by current user in current database (KV scan) -4294967246 4294967131 0 indexes accessible by current user in current database (KV scan) -4294967244 4294967131 0 stats for all tables accessible by current user in current database as of 10s ago -4294967245 4294967131 0 table descriptors accessible by current user, including non-public and virtual (KV scan; expensive!) -4294967242 4294967131 0 transaction statistics (cluster-wide).Querying this table is an expensive operation since it creates a cluster-wide RPC-fanout. -4294967241 4294967131 0 decoded zone configurations from system.zones (KV scan) -4294967229 4294967131 0 roles for which the current user has admin option -4294967228 4294967131 0 roles available to the current user -4294967227 4294967131 0 attributes was created for compatibility and is currently unimplemented -4294967226 4294967131 0 character sets available in the current database -4294967225 4294967131 0 check_constraint_routine_usage was created for compatibility and is currently unimplemented -4294967224 4294967131 0 check constraints -4294967223 4294967131 0 identifies which character set the available collations are -4294967222 4294967131 0 shows the collations available in the current database -4294967221 4294967131 0 column_column_usage was created for compatibility and is currently unimplemented -4294967220 4294967131 0 column_domain_usage was created for compatibility and is currently unimplemented -4294967219 4294967131 0 column_options was created for compatibility and is currently unimplemented -4294967218 4294967131 0 column privilege grants (incomplete) -4294967217 4294967131 0 column_statistics was created for compatibility and is currently unimplemented -4294967216 4294967131 0 columns with user defined types -4294967214 4294967131 0 table and view columns (incomplete) -4294967215 4294967131 0 columns_extensions was created for compatibility and is currently unimplemented -4294967213 4294967131 0 columns usage by constraints -4294967212 4294967131 0 constraint_table_usage was created for compatibility and is currently unimplemented -4294967211 4294967131 0 data_type_privileges was created for compatibility and is currently unimplemented -4294967210 4294967131 0 domain_constraints was created for compatibility and is currently unimplemented -4294967209 4294967131 0 domain_udt_usage was created for compatibility and is currently unimplemented -4294967208 4294967131 0 domains was created for compatibility and is currently unimplemented -4294967207 4294967131 0 element_types was created for compatibility and is currently unimplemented -4294967206 4294967131 0 roles for the current user -4294967205 4294967131 0 engines was created for compatibility and is currently unimplemented -4294967204 4294967131 0 events was created for compatibility and is currently unimplemented -4294967203 4294967131 0 files was created for compatibility and is currently unimplemented -4294967202 4294967131 0 foreign_data_wrapper_options was created for compatibility and is currently unimplemented -4294967201 4294967131 0 foreign_data_wrappers was created for compatibility and is currently unimplemented -4294967200 4294967131 0 foreign_server_options was created for compatibility and is currently unimplemented -4294967199 4294967131 0 foreign_servers was created for compatibility and is currently unimplemented -4294967198 4294967131 0 foreign_table_options was created for compatibility and is currently unimplemented -4294967197 4294967131 0 foreign_tables was created for compatibility and is currently unimplemented -4294967196 4294967131 0 information_schema_catalog_name was created for compatibility and is currently unimplemented -4294967195 4294967131 0 column usage by indexes and key constraints -4294967194 4294967131 0 keywords was created for compatibility and is currently unimplemented -4294967193 4294967131 0 optimizer_trace was created for compatibility and is currently unimplemented -4294967192 4294967131 0 built-in function parameters (empty - introspection not yet supported) -4294967191 4294967131 0 partitions was created for compatibility and is currently unimplemented -4294967190 4294967131 0 plugins was created for compatibility and is currently unimplemented -4294967189 4294967131 0 processlist was created for compatibility and is currently unimplemented -4294967188 4294967131 0 profiling was created for compatibility and is currently unimplemented -4294967187 4294967131 0 foreign key constraints -4294967186 4294967131 0 resource_groups was created for compatibility and is currently unimplemented -4294967185 4294967131 0 role_column_grants was created for compatibility and is currently unimplemented -4294967184 4294967131 0 role_routine_grants was created for compatibility and is currently unimplemented -4294967183 4294967131 0 privileges granted on table or views (incomplete; see also information_schema.table_privileges; may contain excess users or roles) -4294967182 4294967131 0 role_udt_grants was created for compatibility and is currently unimplemented -4294967181 4294967131 0 role_usage_grants was created for compatibility and is currently unimplemented -4294967180 4294967131 0 routine_privileges was created for compatibility and is currently unimplemented -4294967179 4294967131 0 built-in functions (empty - introspection not yet supported) -4294967172 4294967131 0 schema privileges (incomplete; may contain excess users or roles) -4294967173 4294967131 0 database schemas (may contain schemata without permission) -4294967174 4294967131 0 schemata_extensions was created for compatibility and is currently unimplemented -4294967171 4294967131 0 sequences -4294967170 4294967131 0 exposes the session variables. -4294967178 4294967131 0 sql_features was created for compatibility and is currently unimplemented -4294967177 4294967131 0 sql_implementation_info was created for compatibility and is currently unimplemented -4294967176 4294967131 0 sql_parts was created for compatibility and is currently unimplemented -4294967175 4294967131 0 sql_sizing was created for compatibility and is currently unimplemented -4294967169 4294967131 0 st_geometry_columns was created for compatibility and is currently unimplemented -4294967168 4294967131 0 st_spatial_reference_systems was created for compatibility and is currently unimplemented -4294967167 4294967131 0 st_units_of_measure was created for compatibility and is currently unimplemented -4294967166 4294967131 0 index metadata and statistics (incomplete) -4294967165 4294967131 0 table constraints -4294967164 4294967131 0 table_constraints_extensions was created for compatibility and is currently unimplemented -4294967163 4294967131 0 privileges granted on table or views (incomplete; may contain excess users or roles) -4294967161 4294967131 0 tables and views -4294967162 4294967131 0 tables_extensions was created for compatibility and is currently unimplemented -4294967159 4294967131 0 tablespaces was created for compatibility and is currently unimplemented -4294967160 4294967131 0 tablespaces_extensions was created for compatibility and is currently unimplemented -4294967158 4294967131 0 transforms was created for compatibility and is currently unimplemented -4294967157 4294967131 0 triggered_update_columns was created for compatibility and is currently unimplemented -4294967156 4294967131 0 triggers was created for compatibility and is currently unimplemented -4294967155 4294967131 0 type privileges (incomplete; may contain excess users or roles) -4294967154 4294967131 0 udt_privileges was created for compatibility and is currently unimplemented -4294967153 4294967131 0 usage_privileges was created for compatibility and is currently unimplemented -4294967152 4294967131 0 user_attributes was created for compatibility and is currently unimplemented -4294967151 4294967131 0 user_defined_types was created for compatibility and is currently unimplemented -4294967150 4294967131 0 user_mapping_options was created for compatibility and is currently unimplemented -4294967149 4294967131 0 user_mappings was created for compatibility and is currently unimplemented -4294967148 4294967131 0 grantable privileges (incomplete) -4294967147 4294967131 0 view_column_usage was created for compatibility and is currently unimplemented -4294967146 4294967131 0 view_routine_usage was created for compatibility and is currently unimplemented -4294967145 4294967131 0 view_table_usage was created for compatibility and is currently unimplemented -4294967144 4294967131 0 views (incomplete) -4294967142 4294967131 0 aggregated built-in functions (incomplete) -4294967141 4294967131 0 index access methods (incomplete) -4294967140 4294967131 0 pg_amop was created for compatibility and is currently unimplemented -4294967139 4294967131 0 pg_amproc was created for compatibility and is currently unimplemented -4294967138 4294967131 0 column default values -4294967137 4294967131 0 table columns (incomplete - see also information_schema.columns) -4294967135 4294967131 0 role membership -4294967136 4294967131 0 authorization identifiers - differs from postgres as we do not display passwords, -4294967134 4294967131 0 pg_available_extension_versions was created for compatibility and is currently unimplemented -4294967133 4294967131 0 available extensions -4294967132 4294967131 0 casts (empty - needs filling out) -4294967131 4294967131 0 tables and relation-like objects (incomplete - see also information_schema.tables/sequences/views) -4294967130 4294967131 0 available collations (incomplete) -4294967129 4294967131 0 pg_config was created for compatibility and is currently unimplemented -4294967128 4294967131 0 table constraints (incomplete - see also information_schema.table_constraints) -4294967127 4294967131 0 encoding conversions (empty - unimplemented) -4294967126 4294967131 0 pg_cursors was created for compatibility and is currently unimplemented -4294967125 4294967131 0 available databases (incomplete) -4294967124 4294967131 0 contains the default values that have been configured for session variables -4294967123 4294967131 0 default ACLs; these are the privileges that will be assigned to newly created objects -4294967122 4294967131 0 dependency relationships (incomplete) -4294967121 4294967131 0 object comments -4294967120 4294967131 0 enum types and labels (empty - feature does not exist) -4294967119 4294967131 0 event triggers (empty - feature does not exist) -4294967118 4294967131 0 installed extensions (empty - feature does not exist) -4294967117 4294967131 0 pg_file_settings was created for compatibility and is currently unimplemented -4294967116 4294967131 0 foreign data wrappers (empty - feature does not exist) -4294967115 4294967131 0 foreign servers (empty - feature does not exist) -4294967114 4294967131 0 foreign tables (empty - feature does not exist) -4294967113 4294967131 0 pg_group was created for compatibility and is currently unimplemented -4294967112 4294967131 0 pg_hba_file_rules was created for compatibility and is currently unimplemented -4294967111 4294967131 0 indexes (incomplete) -4294967110 4294967131 0 index creation statements -4294967109 4294967131 0 table inheritance hierarchy (empty - feature does not exist) -4294967108 4294967131 0 pg_init_privs was created for compatibility and is currently unimplemented -4294967107 4294967131 0 available languages (empty - feature does not exist) -4294967105 4294967131 0 pg_largeobject was created for compatibility and is currently unimplemented -4294967106 4294967131 0 pg_largeobject_metadata was created for compatibility and is currently unimplemented -4294967104 4294967131 0 locks held by active processes (empty - feature does not exist) -4294967103 4294967131 0 available materialized views (empty - feature does not exist) -4294967102 4294967131 0 available namespaces (incomplete; namespaces and databases are congruent in CockroachDB) -4294967101 4294967131 0 opclass (empty - Operator classes not supported yet) -4294967100 4294967131 0 operators (incomplete) -4294967099 4294967131 0 pg_opfamily was created for compatibility and is currently unimplemented -4294967098 4294967131 0 pg_partitioned_table was created for compatibility and is currently unimplemented -4294967097 4294967131 0 pg_policies was created for compatibility and is currently unimplemented -4294967096 4294967131 0 pg_policy was created for compatibility and is currently unimplemented -4294967095 4294967131 0 prepared statements -4294967094 4294967131 0 prepared transactions (empty - feature does not exist) -4294967093 4294967131 0 built-in functions (incomplete) -4294967091 4294967131 0 pg_publication was created for compatibility and is currently unimplemented -4294967092 4294967131 0 pg_publication_rel was created for compatibility and is currently unimplemented -4294967090 4294967131 0 pg_publication_tables was created for compatibility and is currently unimplemented -4294967089 4294967131 0 range types (empty - feature does not exist) -4294967087 4294967131 0 pg_replication_origin was created for compatibility and is currently unimplemented -4294967088 4294967131 0 pg_replication_origin_status was created for compatibility and is currently unimplemented -4294967086 4294967131 0 pg_replication_slots was created for compatibility and is currently unimplemented -4294967085 4294967131 0 rewrite rules (only for referencing on pg_depend for table-view dependencies) -4294967084 4294967131 0 database roles -4294967083 4294967131 0 pg_rules was created for compatibility and is currently unimplemented -4294967081 4294967131 0 security labels (empty - feature does not exist) -4294967082 4294967131 0 security labels (empty) -4294967080 4294967131 0 sequences (see also information_schema.sequences) -4294967079 4294967131 0 pg_sequences is very similar as pg_sequence. -4294967078 4294967131 0 session variables (incomplete) -4294967077 4294967131 0 pg_shadow lists properties for roles that are marked as rolcanlogin in pg_authid -4294967074 4294967131 0 Shared Dependencies (Roles depending on objects). -4294967076 4294967131 0 shared object comments -4294967073 4294967131 0 pg_shmem_allocations was created for compatibility and is currently unimplemented -4294967075 4294967131 0 shared security labels (empty - feature not supported) -4294967072 4294967131 0 backend access statistics (empty - monitoring works differently in CockroachDB) -4294967071 4294967131 0 pg_stat_all_indexes was created for compatibility and is currently unimplemented -4294967070 4294967131 0 pg_stat_all_tables was created for compatibility and is currently unimplemented -4294967069 4294967131 0 pg_stat_archiver was created for compatibility and is currently unimplemented -4294967068 4294967131 0 pg_stat_bgwriter was created for compatibility and is currently unimplemented -4294967066 4294967131 0 pg_stat_database was created for compatibility and is currently unimplemented -4294967067 4294967131 0 pg_stat_database_conflicts was created for compatibility and is currently unimplemented -4294967065 4294967131 0 pg_stat_gssapi was created for compatibility and is currently unimplemented -4294967064 4294967131 0 pg_stat_progress_analyze was created for compatibility and is currently unimplemented -4294967063 4294967131 0 pg_stat_progress_basebackup was created for compatibility and is currently unimplemented -4294967062 4294967131 0 pg_stat_progress_cluster was created for compatibility and is currently unimplemented -4294967061 4294967131 0 pg_stat_progress_create_index was created for compatibility and is currently unimplemented -4294967060 4294967131 0 pg_stat_progress_vacuum was created for compatibility and is currently unimplemented -4294967059 4294967131 0 pg_stat_replication was created for compatibility and is currently unimplemented -4294967058 4294967131 0 pg_stat_slru was created for compatibility and is currently unimplemented -4294967057 4294967131 0 pg_stat_ssl was created for compatibility and is currently unimplemented -4294967056 4294967131 0 pg_stat_subscription was created for compatibility and is currently unimplemented -4294967055 4294967131 0 pg_stat_sys_indexes was created for compatibility and is currently unimplemented -4294967054 4294967131 0 pg_stat_sys_tables was created for compatibility and is currently unimplemented -4294967053 4294967131 0 pg_stat_user_functions was created for compatibility and is currently unimplemented -4294967052 4294967131 0 pg_stat_user_indexes was created for compatibility and is currently unimplemented -4294967051 4294967131 0 pg_stat_user_tables was created for compatibility and is currently unimplemented -4294967050 4294967131 0 pg_stat_wal_receiver was created for compatibility and is currently unimplemented -4294967049 4294967131 0 pg_stat_xact_all_tables was created for compatibility and is currently unimplemented -4294967048 4294967131 0 pg_stat_xact_sys_tables was created for compatibility and is currently unimplemented -4294967047 4294967131 0 pg_stat_xact_user_functions was created for compatibility and is currently unimplemented -4294967046 4294967131 0 pg_stat_xact_user_tables was created for compatibility and is currently unimplemented -4294967045 4294967131 0 pg_statio_all_indexes was created for compatibility and is currently unimplemented -4294967044 4294967131 0 pg_statio_all_sequences was created for compatibility and is currently unimplemented -4294967043 4294967131 0 pg_statio_all_tables was created for compatibility and is currently unimplemented -4294967042 4294967131 0 pg_statio_sys_indexes was created for compatibility and is currently unimplemented -4294967041 4294967131 0 pg_statio_sys_sequences was created for compatibility and is currently unimplemented -4294967040 4294967131 0 pg_statio_sys_tables was created for compatibility and is currently unimplemented -4294967039 4294967131 0 pg_statio_user_indexes was created for compatibility and is currently unimplemented -4294967038 4294967131 0 pg_statio_user_sequences was created for compatibility and is currently unimplemented -4294967037 4294967131 0 pg_statio_user_tables was created for compatibility and is currently unimplemented -4294967034 4294967131 0 pg_statistic was created for compatibility and is currently unimplemented -4294967035 4294967131 0 pg_statistic_ext has the statistics objects created with CREATE STATISTICS -4294967036 4294967131 0 pg_statistic_ext_data was created for compatibility and is currently unimplemented -4294967032 4294967131 0 pg_stats was created for compatibility and is currently unimplemented -4294967033 4294967131 0 pg_stats_ext was created for compatibility and is currently unimplemented -4294967030 4294967131 0 pg_subscription was created for compatibility and is currently unimplemented -4294967031 4294967131 0 pg_subscription_rel was created for compatibility and is currently unimplemented -4294967029 4294967131 0 tables summary (see also information_schema.tables, pg_catalog.pg_class) -4294967028 4294967131 0 available tablespaces (incomplete; concept inapplicable to CockroachDB) -4294967027 4294967131 0 pg_timezone_abbrevs was created for compatibility and is currently unimplemented -4294967026 4294967131 0 pg_timezone_names was created for compatibility and is currently unimplemented -4294967025 4294967131 0 pg_transform was created for compatibility and is currently unimplemented -4294967024 4294967131 0 triggers (empty - feature does not exist) -4294967022 4294967131 0 pg_ts_config was created for compatibility and is currently unimplemented -4294967023 4294967131 0 pg_ts_config_map was created for compatibility and is currently unimplemented -4294967021 4294967131 0 pg_ts_dict was created for compatibility and is currently unimplemented -4294967020 4294967131 0 pg_ts_parser was created for compatibility and is currently unimplemented -4294967019 4294967131 0 pg_ts_template was created for compatibility and is currently unimplemented -4294967018 4294967131 0 scalar types (incomplete) -4294967015 4294967131 0 database users -4294967017 4294967131 0 local to remote user mapping (empty - feature does not exist) -4294967016 4294967131 0 pg_user_mappings was created for compatibility and is currently unimplemented -4294967014 4294967131 0 view definitions (incomplete - see also information_schema.views) -4294967012 4294967131 0 Shows all defined geography columns. Matches PostGIS' geography_columns functionality. -4294967011 4294967131 0 Shows all defined geometry columns. Matches PostGIS' geometry_columns functionality. -4294967010 4294967131 0 Shows all defined Spatial Reference Identifiers (SRIDs). Matches PostGIS' spatial_ref_sys table. +4294967233 4294967132 0 node-level table listing all currently running range feeds +4294967294 4294967132 0 backward inter-descriptor dependencies starting from tables accessible by current user in current database (KV scan) +4294967292 4294967132 0 built-in functions (RAM/static) +4294967288 4294967132 0 contention information (cluster RPC; expensive!) +4294967239 4294967132 0 virtual table with database privileges +4294967287 4294967132 0 DistSQL remote flows information (cluster RPC; expensive!) +4294967236 4294967132 0 traces for in-flight spans across all nodes in the cluster (cluster RPC; expensive!) +4294967286 4294967132 0 running queries visible by current user (cluster RPC; expensive!) +4294967284 4294967132 0 running sessions visible to current user (cluster RPC; expensive!) +4294967283 4294967132 0 cluster settings (RAM) +4294967285 4294967132 0 running user transactions visible by the current user (cluster RPC; expensive!) +4294967282 4294967132 0 CREATE statements for all user defined schemas accessible by the current user in current database (KV scan) +4294967281 4294967132 0 CREATE and ALTER statements for all tables accessible by current user in current database (KV scan) +4294967280 4294967132 0 CREATE statements for all user defined types accessible by the current user in current database (KV scan) +4294967238 4294967132 0 virtual table with cross db references +4294967279 4294967132 0 databases accessible by the current user (KV scan) +4294967234 4294967132 0 virtual table with default privileges +4294967278 4294967132 0 telemetry counters (RAM; local node only) +4294967277 4294967132 0 forward inter-descriptor dependencies starting from tables accessible by current user in current database (KV scan) +4294967274 4294967132 0 locally known gossiped health alerts (RAM; local node only) +4294967273 4294967132 0 locally known gossiped node liveness (RAM; local node only) +4294967272 4294967132 0 locally known edges in the gossip network (RAM; local node only) +4294967275 4294967132 0 locally known gossiped node details (RAM; local node only) +4294967271 4294967132 0 index columns for all indexes accessible by current user in current database (KV scan) +4294967270 4294967132 0 cluster-wide index usage statistics (in-memory, not durable).Querying this table is an expensive operation since it creates acluster-wide RPC fanout. +4294967240 4294967132 0 virtual table to validate descriptors +4294967268 4294967132 0 decoded job metadata from system.jobs (KV scan) +4294967276 4294967132 0 node liveness status, as seen by kv +4294967267 4294967132 0 node details across the entire cluster (cluster RPC; expensive!) +4294967266 4294967132 0 store details and status (cluster RPC; expensive!) +4294967265 4294967132 0 acquired table leases (RAM; local node only) +4294967237 4294967132 0 virtual table with table descriptors that still have data +4294967293 4294967132 0 detailed identification strings (RAM, local node only) +4294967264 4294967132 0 contention information (RAM; local node only) +4294967263 4294967132 0 DistSQL remote flows information (RAM; local node only) +4294967269 4294967132 0 in-flight spans (RAM; local node only) +4294967259 4294967132 0 current values for metrics (RAM; local node only) +4294967262 4294967132 0 running queries visible by current user (RAM; local node only) +4294967252 4294967132 0 server parameters, useful to construct connection URLs (RAM, local node only) +4294967260 4294967132 0 running sessions visible by current user (RAM; local node only) +4294967258 4294967132 0 statement statistics (in-memory, not durable; local node only). This table is wiped periodically (by default, at least every two hours) +4294967243 4294967132 0 finer-grained transaction statistics (in-memory, not durable; local node only). This table is wiped periodically (by default, at least every two hours) +4294967261 4294967132 0 running user transactions visible by the current user (RAM; local node only) +4294967257 4294967132 0 per-application transaction statistics (in-memory, not durable; local node only). This table is wiped periodically (by default, at least every two hours) +4294967256 4294967132 0 defined partitions for all tables/indexes accessible by the current user in the current database (KV scan) +4294967255 4294967132 0 comments for predefined virtual tables (RAM/static) +4294967254 4294967132 0 range metadata without leaseholder details (KV join; expensive!) +4294967235 4294967132 0 available regions for the cluster +4294967251 4294967132 0 ongoing schema changes, across all descriptors accessible by current user (KV scan; expensive!) +4294967250 4294967132 0 session trace accumulated so far (RAM) +4294967249 4294967132 0 session variables (RAM) +4294967248 4294967132 0 statement statistics (cluster-wide).Querying this table is an expensive operation since it creates a cluster-wide RPC-fanout. +4294967247 4294967132 0 details for all columns accessible by current user in current database (KV scan) +4294967246 4294967132 0 indexes accessible by current user in current database (KV scan) +4294967244 4294967132 0 stats for all tables accessible by current user in current database as of 10s ago +4294967245 4294967132 0 table descriptors accessible by current user, including non-public and virtual (KV scan; expensive!) +4294967242 4294967132 0 transaction statistics (cluster-wide).Querying this table is an expensive operation since it creates a cluster-wide RPC-fanout. +4294967241 4294967132 0 decoded zone configurations from system.zones (KV scan) +4294967230 4294967132 0 roles for which the current user has admin option +4294967229 4294967132 0 roles available to the current user +4294967228 4294967132 0 attributes was created for compatibility and is currently unimplemented +4294967227 4294967132 0 character sets available in the current database +4294967226 4294967132 0 check_constraint_routine_usage was created for compatibility and is currently unimplemented +4294967225 4294967132 0 check constraints +4294967224 4294967132 0 identifies which character set the available collations are +4294967223 4294967132 0 shows the collations available in the current database +4294967222 4294967132 0 column_column_usage was created for compatibility and is currently unimplemented +4294967221 4294967132 0 column_domain_usage was created for compatibility and is currently unimplemented +4294967220 4294967132 0 column_options was created for compatibility and is currently unimplemented +4294967219 4294967132 0 column privilege grants (incomplete) +4294967218 4294967132 0 column_statistics was created for compatibility and is currently unimplemented +4294967217 4294967132 0 columns with user defined types +4294967215 4294967132 0 table and view columns (incomplete) +4294967216 4294967132 0 columns_extensions was created for compatibility and is currently unimplemented +4294967214 4294967132 0 columns usage by constraints +4294967213 4294967132 0 constraint_table_usage was created for compatibility and is currently unimplemented +4294967212 4294967132 0 data_type_privileges was created for compatibility and is currently unimplemented +4294967211 4294967132 0 domain_constraints was created for compatibility and is currently unimplemented +4294967210 4294967132 0 domain_udt_usage was created for compatibility and is currently unimplemented +4294967209 4294967132 0 domains was created for compatibility and is currently unimplemented +4294967208 4294967132 0 element_types was created for compatibility and is currently unimplemented +4294967207 4294967132 0 roles for the current user +4294967206 4294967132 0 engines was created for compatibility and is currently unimplemented +4294967205 4294967132 0 events was created for compatibility and is currently unimplemented +4294967204 4294967132 0 files was created for compatibility and is currently unimplemented +4294967203 4294967132 0 foreign_data_wrapper_options was created for compatibility and is currently unimplemented +4294967202 4294967132 0 foreign_data_wrappers was created for compatibility and is currently unimplemented +4294967201 4294967132 0 foreign_server_options was created for compatibility and is currently unimplemented +4294967200 4294967132 0 foreign_servers was created for compatibility and is currently unimplemented +4294967199 4294967132 0 foreign_table_options was created for compatibility and is currently unimplemented +4294967198 4294967132 0 foreign_tables was created for compatibility and is currently unimplemented +4294967197 4294967132 0 information_schema_catalog_name was created for compatibility and is currently unimplemented +4294967196 4294967132 0 column usage by indexes and key constraints +4294967195 4294967132 0 keywords was created for compatibility and is currently unimplemented +4294967194 4294967132 0 optimizer_trace was created for compatibility and is currently unimplemented +4294967193 4294967132 0 built-in function parameters (empty - introspection not yet supported) +4294967192 4294967132 0 partitions was created for compatibility and is currently unimplemented +4294967191 4294967132 0 plugins was created for compatibility and is currently unimplemented +4294967190 4294967132 0 processlist was created for compatibility and is currently unimplemented +4294967189 4294967132 0 profiling was created for compatibility and is currently unimplemented +4294967188 4294967132 0 foreign key constraints +4294967187 4294967132 0 resource_groups was created for compatibility and is currently unimplemented +4294967186 4294967132 0 role_column_grants was created for compatibility and is currently unimplemented +4294967185 4294967132 0 role_routine_grants was created for compatibility and is currently unimplemented +4294967184 4294967132 0 privileges granted on table or views (incomplete; see also information_schema.table_privileges; may contain excess users or roles) +4294967183 4294967132 0 role_udt_grants was created for compatibility and is currently unimplemented +4294967182 4294967132 0 role_usage_grants was created for compatibility and is currently unimplemented +4294967181 4294967132 0 routine_privileges was created for compatibility and is currently unimplemented +4294967180 4294967132 0 built-in functions (empty - introspection not yet supported) +4294967173 4294967132 0 schema privileges (incomplete; may contain excess users or roles) +4294967174 4294967132 0 database schemas (may contain schemata without permission) +4294967175 4294967132 0 schemata_extensions was created for compatibility and is currently unimplemented +4294967172 4294967132 0 sequences +4294967171 4294967132 0 exposes the session variables. +4294967179 4294967132 0 sql_features was created for compatibility and is currently unimplemented +4294967178 4294967132 0 sql_implementation_info was created for compatibility and is currently unimplemented +4294967177 4294967132 0 sql_parts was created for compatibility and is currently unimplemented +4294967176 4294967132 0 sql_sizing was created for compatibility and is currently unimplemented +4294967170 4294967132 0 st_geometry_columns was created for compatibility and is currently unimplemented +4294967169 4294967132 0 st_spatial_reference_systems was created for compatibility and is currently unimplemented +4294967168 4294967132 0 st_units_of_measure was created for compatibility and is currently unimplemented +4294967167 4294967132 0 index metadata and statistics (incomplete) +4294967166 4294967132 0 table constraints +4294967165 4294967132 0 table_constraints_extensions was created for compatibility and is currently unimplemented +4294967164 4294967132 0 privileges granted on table or views (incomplete; may contain excess users or roles) +4294967162 4294967132 0 tables and views +4294967163 4294967132 0 tables_extensions was created for compatibility and is currently unimplemented +4294967160 4294967132 0 tablespaces was created for compatibility and is currently unimplemented +4294967161 4294967132 0 tablespaces_extensions was created for compatibility and is currently unimplemented +4294967159 4294967132 0 transforms was created for compatibility and is currently unimplemented +4294967158 4294967132 0 triggered_update_columns was created for compatibility and is currently unimplemented +4294967157 4294967132 0 triggers was created for compatibility and is currently unimplemented +4294967156 4294967132 0 type privileges (incomplete; may contain excess users or roles) +4294967155 4294967132 0 udt_privileges was created for compatibility and is currently unimplemented +4294967154 4294967132 0 usage_privileges was created for compatibility and is currently unimplemented +4294967153 4294967132 0 user_attributes was created for compatibility and is currently unimplemented +4294967152 4294967132 0 user_defined_types was created for compatibility and is currently unimplemented +4294967151 4294967132 0 user_mapping_options was created for compatibility and is currently unimplemented +4294967150 4294967132 0 user_mappings was created for compatibility and is currently unimplemented +4294967149 4294967132 0 grantable privileges (incomplete) +4294967148 4294967132 0 view_column_usage was created for compatibility and is currently unimplemented +4294967147 4294967132 0 view_routine_usage was created for compatibility and is currently unimplemented +4294967146 4294967132 0 view_table_usage was created for compatibility and is currently unimplemented +4294967145 4294967132 0 views (incomplete) +4294967143 4294967132 0 aggregated built-in functions (incomplete) +4294967142 4294967132 0 index access methods (incomplete) +4294967141 4294967132 0 pg_amop was created for compatibility and is currently unimplemented +4294967140 4294967132 0 pg_amproc was created for compatibility and is currently unimplemented +4294967139 4294967132 0 column default values +4294967138 4294967132 0 table columns (incomplete - see also information_schema.columns) +4294967136 4294967132 0 role membership +4294967137 4294967132 0 authorization identifiers - differs from postgres as we do not display passwords, +4294967135 4294967132 0 pg_available_extension_versions was created for compatibility and is currently unimplemented +4294967134 4294967132 0 available extensions +4294967133 4294967132 0 casts (empty - needs filling out) +4294967132 4294967132 0 tables and relation-like objects (incomplete - see also information_schema.tables/sequences/views) +4294967131 4294967132 0 available collations (incomplete) +4294967130 4294967132 0 pg_config was created for compatibility and is currently unimplemented +4294967129 4294967132 0 table constraints (incomplete - see also information_schema.table_constraints) +4294967128 4294967132 0 encoding conversions (empty - unimplemented) +4294967127 4294967132 0 pg_cursors was created for compatibility and is currently unimplemented +4294967126 4294967132 0 available databases (incomplete) +4294967125 4294967132 0 contains the default values that have been configured for session variables +4294967124 4294967132 0 default ACLs; these are the privileges that will be assigned to newly created objects +4294967123 4294967132 0 dependency relationships (incomplete) +4294967122 4294967132 0 object comments +4294967121 4294967132 0 enum types and labels (empty - feature does not exist) +4294967120 4294967132 0 event triggers (empty - feature does not exist) +4294967119 4294967132 0 installed extensions (empty - feature does not exist) +4294967118 4294967132 0 pg_file_settings was created for compatibility and is currently unimplemented +4294967117 4294967132 0 foreign data wrappers (empty - feature does not exist) +4294967116 4294967132 0 foreign servers (empty - feature does not exist) +4294967115 4294967132 0 foreign tables (empty - feature does not exist) +4294967114 4294967132 0 pg_group was created for compatibility and is currently unimplemented +4294967113 4294967132 0 pg_hba_file_rules was created for compatibility and is currently unimplemented +4294967112 4294967132 0 indexes (incomplete) +4294967111 4294967132 0 index creation statements +4294967110 4294967132 0 table inheritance hierarchy (empty - feature does not exist) +4294967109 4294967132 0 pg_init_privs was created for compatibility and is currently unimplemented +4294967108 4294967132 0 available languages (empty - feature does not exist) +4294967106 4294967132 0 pg_largeobject was created for compatibility and is currently unimplemented +4294967107 4294967132 0 pg_largeobject_metadata was created for compatibility and is currently unimplemented +4294967105 4294967132 0 locks held by active processes (empty - feature does not exist) +4294967104 4294967132 0 available materialized views (empty - feature does not exist) +4294967103 4294967132 0 available namespaces (incomplete; namespaces and databases are congruent in CockroachDB) +4294967102 4294967132 0 opclass (empty - Operator classes not supported yet) +4294967101 4294967132 0 operators (incomplete) +4294967100 4294967132 0 pg_opfamily was created for compatibility and is currently unimplemented +4294967099 4294967132 0 pg_partitioned_table was created for compatibility and is currently unimplemented +4294967098 4294967132 0 pg_policies was created for compatibility and is currently unimplemented +4294967097 4294967132 0 pg_policy was created for compatibility and is currently unimplemented +4294967096 4294967132 0 prepared statements +4294967095 4294967132 0 prepared transactions (empty - feature does not exist) +4294967094 4294967132 0 built-in functions (incomplete) +4294967092 4294967132 0 pg_publication was created for compatibility and is currently unimplemented +4294967093 4294967132 0 pg_publication_rel was created for compatibility and is currently unimplemented +4294967091 4294967132 0 pg_publication_tables was created for compatibility and is currently unimplemented +4294967090 4294967132 0 range types (empty - feature does not exist) +4294967088 4294967132 0 pg_replication_origin was created for compatibility and is currently unimplemented +4294967089 4294967132 0 pg_replication_origin_status was created for compatibility and is currently unimplemented +4294967087 4294967132 0 pg_replication_slots was created for compatibility and is currently unimplemented +4294967086 4294967132 0 rewrite rules (only for referencing on pg_depend for table-view dependencies) +4294967085 4294967132 0 database roles +4294967084 4294967132 0 pg_rules was created for compatibility and is currently unimplemented +4294967082 4294967132 0 security labels (empty - feature does not exist) +4294967083 4294967132 0 security labels (empty) +4294967081 4294967132 0 sequences (see also information_schema.sequences) +4294967080 4294967132 0 pg_sequences is very similar as pg_sequence. +4294967079 4294967132 0 session variables (incomplete) +4294967078 4294967132 0 pg_shadow lists properties for roles that are marked as rolcanlogin in pg_authid +4294967075 4294967132 0 Shared Dependencies (Roles depending on objects). +4294967077 4294967132 0 shared object comments +4294967074 4294967132 0 pg_shmem_allocations was created for compatibility and is currently unimplemented +4294967076 4294967132 0 shared security labels (empty - feature not supported) +4294967073 4294967132 0 backend access statistics (empty - monitoring works differently in CockroachDB) +4294967072 4294967132 0 pg_stat_all_indexes was created for compatibility and is currently unimplemented +4294967071 4294967132 0 pg_stat_all_tables was created for compatibility and is currently unimplemented +4294967070 4294967132 0 pg_stat_archiver was created for compatibility and is currently unimplemented +4294967069 4294967132 0 pg_stat_bgwriter was created for compatibility and is currently unimplemented +4294967067 4294967132 0 pg_stat_database was created for compatibility and is currently unimplemented +4294967068 4294967132 0 pg_stat_database_conflicts was created for compatibility and is currently unimplemented +4294967066 4294967132 0 pg_stat_gssapi was created for compatibility and is currently unimplemented +4294967065 4294967132 0 pg_stat_progress_analyze was created for compatibility and is currently unimplemented +4294967064 4294967132 0 pg_stat_progress_basebackup was created for compatibility and is currently unimplemented +4294967063 4294967132 0 pg_stat_progress_cluster was created for compatibility and is currently unimplemented +4294967062 4294967132 0 pg_stat_progress_create_index was created for compatibility and is currently unimplemented +4294967061 4294967132 0 pg_stat_progress_vacuum was created for compatibility and is currently unimplemented +4294967060 4294967132 0 pg_stat_replication was created for compatibility and is currently unimplemented +4294967059 4294967132 0 pg_stat_slru was created for compatibility and is currently unimplemented +4294967058 4294967132 0 pg_stat_ssl was created for compatibility and is currently unimplemented +4294967057 4294967132 0 pg_stat_subscription was created for compatibility and is currently unimplemented +4294967056 4294967132 0 pg_stat_sys_indexes was created for compatibility and is currently unimplemented +4294967055 4294967132 0 pg_stat_sys_tables was created for compatibility and is currently unimplemented +4294967054 4294967132 0 pg_stat_user_functions was created for compatibility and is currently unimplemented +4294967053 4294967132 0 pg_stat_user_indexes was created for compatibility and is currently unimplemented +4294967052 4294967132 0 pg_stat_user_tables was created for compatibility and is currently unimplemented +4294967051 4294967132 0 pg_stat_wal_receiver was created for compatibility and is currently unimplemented +4294967050 4294967132 0 pg_stat_xact_all_tables was created for compatibility and is currently unimplemented +4294967049 4294967132 0 pg_stat_xact_sys_tables was created for compatibility and is currently unimplemented +4294967048 4294967132 0 pg_stat_xact_user_functions was created for compatibility and is currently unimplemented +4294967047 4294967132 0 pg_stat_xact_user_tables was created for compatibility and is currently unimplemented +4294967046 4294967132 0 pg_statio_all_indexes was created for compatibility and is currently unimplemented +4294967045 4294967132 0 pg_statio_all_sequences was created for compatibility and is currently unimplemented +4294967044 4294967132 0 pg_statio_all_tables was created for compatibility and is currently unimplemented +4294967043 4294967132 0 pg_statio_sys_indexes was created for compatibility and is currently unimplemented +4294967042 4294967132 0 pg_statio_sys_sequences was created for compatibility and is currently unimplemented +4294967041 4294967132 0 pg_statio_sys_tables was created for compatibility and is currently unimplemented +4294967040 4294967132 0 pg_statio_user_indexes was created for compatibility and is currently unimplemented +4294967039 4294967132 0 pg_statio_user_sequences was created for compatibility and is currently unimplemented +4294967038 4294967132 0 pg_statio_user_tables was created for compatibility and is currently unimplemented +4294967035 4294967132 0 pg_statistic was created for compatibility and is currently unimplemented +4294967036 4294967132 0 pg_statistic_ext has the statistics objects created with CREATE STATISTICS +4294967037 4294967132 0 pg_statistic_ext_data was created for compatibility and is currently unimplemented +4294967033 4294967132 0 pg_stats was created for compatibility and is currently unimplemented +4294967034 4294967132 0 pg_stats_ext was created for compatibility and is currently unimplemented +4294967031 4294967132 0 pg_subscription was created for compatibility and is currently unimplemented +4294967032 4294967132 0 pg_subscription_rel was created for compatibility and is currently unimplemented +4294967030 4294967132 0 tables summary (see also information_schema.tables, pg_catalog.pg_class) +4294967029 4294967132 0 available tablespaces (incomplete; concept inapplicable to CockroachDB) +4294967028 4294967132 0 pg_timezone_abbrevs was created for compatibility and is currently unimplemented +4294967027 4294967132 0 pg_timezone_names was created for compatibility and is currently unimplemented +4294967026 4294967132 0 pg_transform was created for compatibility and is currently unimplemented +4294967025 4294967132 0 triggers (empty - feature does not exist) +4294967023 4294967132 0 pg_ts_config was created for compatibility and is currently unimplemented +4294967024 4294967132 0 pg_ts_config_map was created for compatibility and is currently unimplemented +4294967022 4294967132 0 pg_ts_dict was created for compatibility and is currently unimplemented +4294967021 4294967132 0 pg_ts_parser was created for compatibility and is currently unimplemented +4294967020 4294967132 0 pg_ts_template was created for compatibility and is currently unimplemented +4294967019 4294967132 0 scalar types (incomplete) +4294967016 4294967132 0 database users +4294967018 4294967132 0 local to remote user mapping (empty - feature does not exist) +4294967017 4294967132 0 pg_user_mappings was created for compatibility and is currently unimplemented +4294967015 4294967132 0 view definitions (incomplete - see also information_schema.views) +4294967013 4294967132 0 Shows all defined geography columns. Matches PostGIS' geography_columns functionality. +4294967012 4294967132 0 Shows all defined geometry columns. Matches PostGIS' geometry_columns functionality. +4294967011 4294967132 0 Shows all defined Spatial Reference Identifiers (SRIDs). Matches PostGIS' spatial_ref_sys table. ## pg_catalog.pg_shdescription @@ -4025,101 +4019,100 @@ FROM WHERE name != 'optimizer' AND name != 'crdb_version' AND name != 'session_id' ---- -name setting category short_desc extra_desc vartype -application_name · NULL NULL NULL string -backslash_quote safe_encoding NULL NULL NULL string -bytea_output hex NULL NULL NULL string -client_encoding UTF8 NULL NULL NULL string -client_min_messages notice NULL NULL NULL string -database test NULL NULL NULL string -datestyle ISO, MDY NULL NULL NULL string -datestyle_enabled off NULL NULL NULL string -default_int_size 8 NULL NULL NULL string -default_tablespace · NULL NULL NULL string -default_transaction_isolation serializable NULL NULL NULL string -default_transaction_priority normal NULL NULL NULL string -default_transaction_read_only off NULL NULL NULL string -default_transaction_use_follower_reads off NULL NULL NULL string -disable_partially_distributed_plans off NULL NULL NULL string -disable_plan_gists off NULL NULL NULL string -disallow_full_table_scans off NULL NULL NULL string -distsql off NULL NULL NULL string -distsql_workmem 64 MiB NULL NULL NULL string -enable_copying_partitioning_when_deinterleaving_table off NULL NULL NULL string -enable_experimental_alter_column_type_general off NULL NULL NULL string -enable_experimental_stream_replication off NULL NULL NULL string -enable_implicit_select_for_update on NULL NULL NULL string -enable_insert_fast_path on NULL NULL NULL string -enable_multiregion_placement_policy off NULL NULL NULL string -enable_seqscan on NULL NULL NULL string -enable_zigzag_join on NULL NULL NULL string -escape_string_warning on NULL NULL NULL string -experimental_distsql_planning off NULL NULL NULL string -experimental_enable_auto_rehoming off NULL NULL NULL string -experimental_enable_hash_sharded_indexes off NULL NULL NULL string -experimental_enable_implicit_column_partitioning off NULL NULL NULL string -experimental_enable_temp_tables off NULL NULL NULL string -experimental_enable_unique_without_index_constraints on NULL NULL NULL string -experimental_use_new_schema_changer off NULL NULL NULL string -extra_float_digits 0 NULL NULL NULL string -force_savepoint_restart off NULL NULL NULL string -foreign_key_cascades_limit 10000 NULL NULL NULL string -idle_in_session_timeout 0 NULL NULL NULL string -idle_in_transaction_session_timeout 0 NULL NULL NULL string -inject_retry_errors_enabled off NULL NULL NULL string -integer_datetimes on NULL NULL NULL string -intervalstyle postgres NULL NULL NULL string -intervalstyle_enabled off NULL NULL NULL string -is_superuser on NULL NULL NULL string -large_full_scan_rows 1000 NULL NULL NULL string -lc_collate C.UTF-8 NULL NULL NULL string -lc_ctype C.UTF-8 NULL NULL NULL string -lc_messages C.UTF-8 NULL NULL NULL string -lc_monetary C.UTF-8 NULL NULL NULL string -lc_numeric C.UTF-8 NULL NULL NULL string -lc_time C.UTF-8 NULL NULL NULL string -locality region=test,dc=dc1 NULL NULL NULL string -locality_optimized_partitioned_index_scan on NULL NULL NULL string -lock_timeout 0 NULL NULL NULL string -max_identifier_length 128 NULL NULL NULL string -max_index_keys 32 NULL NULL NULL string -node_id 1 NULL NULL NULL string -null_ordered_last off NULL NULL NULL string -on_update_rehome_row_enabled on NULL NULL NULL string -optimizer_use_histograms on NULL NULL NULL string -optimizer_use_multicol_stats on NULL NULL NULL string -override_multi_region_zone_config off NULL NULL NULL string -prefer_lookup_joins_for_fks off NULL NULL NULL string -propagate_input_ordering off NULL NULL NULL string -reorder_joins_limit 8 NULL NULL NULL string -require_explicit_primary_keys off NULL NULL NULL string -results_buffer_size 16384 NULL NULL NULL string -role none NULL NULL NULL string -row_security off NULL NULL NULL string -search_path "$user", public NULL NULL NULL string -serial_normalization rowid NULL NULL NULL string -server_encoding UTF8 NULL NULL NULL string -server_version 13.0.0 NULL NULL NULL string -server_version_num 130000 NULL NULL NULL string -session_user root NULL NULL NULL string -sql_safe_updates off NULL NULL NULL string -standard_conforming_strings on NULL NULL NULL string -statement_timeout 0 NULL NULL NULL string -stub_catalog_tables on NULL NULL NULL string -synchronize_seqscans on NULL NULL NULL string -synchronous_commit on NULL NULL NULL string -testing_vectorize_inject_panics off NULL NULL NULL string -timezone UTC NULL NULL NULL string -tracing off NULL NULL NULL string -transaction_isolation serializable NULL NULL NULL string -transaction_priority normal NULL NULL NULL string -transaction_read_only off NULL NULL NULL string -transaction_rows_read_err 0 NULL NULL NULL string -transaction_rows_read_log 0 NULL NULL NULL string -transaction_rows_written_err 0 NULL NULL NULL string -transaction_rows_written_log 0 NULL NULL NULL string -transaction_status NoTxn NULL NULL NULL string -vectorize on NULL NULL NULL string +name setting category short_desc extra_desc vartype +application_name · NULL NULL NULL string +backslash_quote safe_encoding NULL NULL NULL string +bytea_output hex NULL NULL NULL string +client_encoding UTF8 NULL NULL NULL string +client_min_messages notice NULL NULL NULL string +database test NULL NULL NULL string +datestyle ISO, MDY NULL NULL NULL string +datestyle_enabled off NULL NULL NULL string +default_int_size 8 NULL NULL NULL string +default_tablespace · NULL NULL NULL string +default_transaction_isolation serializable NULL NULL NULL string +default_transaction_priority normal NULL NULL NULL string +default_transaction_read_only off NULL NULL NULL string +default_transaction_use_follower_reads off NULL NULL NULL string +disable_partially_distributed_plans off NULL NULL NULL string +disable_plan_gists off NULL NULL NULL string +disallow_full_table_scans off NULL NULL NULL string +distsql off NULL NULL NULL string +distsql_workmem 64 MiB NULL NULL NULL string +enable_experimental_alter_column_type_general off NULL NULL NULL string +enable_experimental_stream_replication off NULL NULL NULL string +enable_implicit_select_for_update on NULL NULL NULL string +enable_insert_fast_path on NULL NULL NULL string +enable_multiregion_placement_policy off NULL NULL NULL string +enable_seqscan on NULL NULL NULL string +enable_zigzag_join on NULL NULL NULL string +escape_string_warning on NULL NULL NULL string +experimental_distsql_planning off NULL NULL NULL string +experimental_enable_auto_rehoming off NULL NULL NULL string +experimental_enable_hash_sharded_indexes off NULL NULL NULL string +experimental_enable_implicit_column_partitioning off NULL NULL NULL string +experimental_enable_temp_tables off NULL NULL NULL string +experimental_enable_unique_without_index_constraints on NULL NULL NULL string +experimental_use_new_schema_changer off NULL NULL NULL string +extra_float_digits 0 NULL NULL NULL string +force_savepoint_restart off NULL NULL NULL string +foreign_key_cascades_limit 10000 NULL NULL NULL string +idle_in_session_timeout 0 NULL NULL NULL string +idle_in_transaction_session_timeout 0 NULL NULL NULL string +inject_retry_errors_enabled off NULL NULL NULL string +integer_datetimes on NULL NULL NULL string +intervalstyle postgres NULL NULL NULL string +intervalstyle_enabled off NULL NULL NULL string +is_superuser on NULL NULL NULL string +large_full_scan_rows 1000 NULL NULL NULL string +lc_collate C.UTF-8 NULL NULL NULL string +lc_ctype C.UTF-8 NULL NULL NULL string +lc_messages C.UTF-8 NULL NULL NULL string +lc_monetary C.UTF-8 NULL NULL NULL string +lc_numeric C.UTF-8 NULL NULL NULL string +lc_time C.UTF-8 NULL NULL NULL string +locality region=test,dc=dc1 NULL NULL NULL string +locality_optimized_partitioned_index_scan on NULL NULL NULL string +lock_timeout 0 NULL NULL NULL string +max_identifier_length 128 NULL NULL NULL string +max_index_keys 32 NULL NULL NULL string +node_id 1 NULL NULL NULL string +null_ordered_last off NULL NULL NULL string +on_update_rehome_row_enabled on NULL NULL NULL string +optimizer_use_histograms on NULL NULL NULL string +optimizer_use_multicol_stats on NULL NULL NULL string +override_multi_region_zone_config off NULL NULL NULL string +prefer_lookup_joins_for_fks off NULL NULL NULL string +propagate_input_ordering off NULL NULL NULL string +reorder_joins_limit 8 NULL NULL NULL string +require_explicit_primary_keys off NULL NULL NULL string +results_buffer_size 16384 NULL NULL NULL string +role none NULL NULL NULL string +row_security off NULL NULL NULL string +search_path "$user", public NULL NULL NULL string +serial_normalization rowid NULL NULL NULL string +server_encoding UTF8 NULL NULL NULL string +server_version 13.0.0 NULL NULL NULL string +server_version_num 130000 NULL NULL NULL string +session_user root NULL NULL NULL string +sql_safe_updates off NULL NULL NULL string +standard_conforming_strings on NULL NULL NULL string +statement_timeout 0 NULL NULL NULL string +stub_catalog_tables on NULL NULL NULL string +synchronize_seqscans on NULL NULL NULL string +synchronous_commit on NULL NULL NULL string +testing_vectorize_inject_panics off NULL NULL NULL string +timezone UTC NULL NULL NULL string +tracing off NULL NULL NULL string +transaction_isolation serializable NULL NULL NULL string +transaction_priority normal NULL NULL NULL string +transaction_read_only off NULL NULL NULL string +transaction_rows_read_err 0 NULL NULL NULL string +transaction_rows_read_log 0 NULL NULL NULL string +transaction_rows_written_err 0 NULL NULL NULL string +transaction_rows_written_log 0 NULL NULL NULL string +transaction_status NoTxn NULL NULL NULL string +vectorize on NULL NULL NULL string query TTTTTTT colnames SELECT @@ -4129,203 +4122,201 @@ FROM WHERE name != 'optimizer' AND name != 'crdb_version' AND name != 'session_id' ---- -name setting unit context enumvals boot_val reset_val -application_name · NULL user NULL · · -backslash_quote safe_encoding NULL user NULL safe_encoding safe_encoding -bytea_output hex NULL user NULL hex hex -client_encoding UTF8 NULL user NULL UTF8 UTF8 -client_min_messages notice NULL user NULL notice notice -database test NULL user NULL · test -datestyle ISO, MDY NULL user NULL ISO, MDY ISO, MDY -datestyle_enabled off NULL user NULL off off -default_int_size 8 NULL user NULL 8 8 -default_tablespace · NULL user NULL · · -default_transaction_isolation serializable NULL user NULL default default -default_transaction_priority normal NULL user NULL normal normal -default_transaction_read_only off NULL user NULL off off -default_transaction_use_follower_reads off NULL user NULL off off -disable_partially_distributed_plans off NULL user NULL off off -disable_plan_gists off NULL user NULL off off -disallow_full_table_scans off NULL user NULL off off -distsql off NULL user NULL off off -distsql_workmem 64 MiB NULL user NULL 64 MiB 64 MiB -enable_copying_partitioning_when_deinterleaving_table off NULL user NULL off off -enable_experimental_alter_column_type_general off NULL user NULL off off -enable_experimental_stream_replication off NULL user NULL off off -enable_implicit_select_for_update on NULL user NULL on on -enable_insert_fast_path on NULL user NULL on on -enable_multiregion_placement_policy off NULL user NULL off off -enable_seqscan on NULL user NULL on on -enable_zigzag_join on NULL user NULL on on -escape_string_warning on NULL user NULL on on -experimental_distsql_planning off NULL user NULL off off -experimental_enable_auto_rehoming off NULL user NULL off off -experimental_enable_hash_sharded_indexes off NULL user NULL off off -experimental_enable_implicit_column_partitioning off NULL user NULL off off -experimental_enable_temp_tables off NULL user NULL off off -experimental_enable_unique_without_index_constraints on NULL user NULL off off -experimental_use_new_schema_changer off NULL user NULL off off -extra_float_digits 0 NULL user NULL 0 2 -force_savepoint_restart off NULL user NULL off off -foreign_key_cascades_limit 10000 NULL user NULL 10000 10000 -idle_in_session_timeout 0 NULL user NULL 0s 0s -idle_in_transaction_session_timeout 0 NULL user NULL 0s 0s -inject_retry_errors_enabled off NULL user NULL off off -integer_datetimes on NULL user NULL on on -intervalstyle postgres NULL user NULL postgres postgres -intervalstyle_enabled off NULL user NULL off off -is_superuser on NULL user NULL on on -large_full_scan_rows 1000 NULL user NULL 1000 1000 -lc_collate C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 -lc_ctype C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 -lc_messages C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 -lc_monetary C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 -lc_numeric C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 -lc_time C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 -locality region=test,dc=dc1 NULL user NULL region=test,dc=dc1 region=test,dc=dc1 -locality_optimized_partitioned_index_scan on NULL user NULL on on -lock_timeout 0 NULL user NULL 0s 0s -max_identifier_length 128 NULL user NULL 128 128 -max_index_keys 32 NULL user NULL 32 32 -node_id 1 NULL user NULL 1 1 -null_ordered_last off NULL user NULL off off -on_update_rehome_row_enabled on NULL user NULL on on -optimizer_use_histograms on NULL user NULL on on -optimizer_use_multicol_stats on NULL user NULL on on -override_multi_region_zone_config off NULL user NULL off off -prefer_lookup_joins_for_fks off NULL user NULL off off -propagate_input_ordering off NULL user NULL off off -reorder_joins_limit 8 NULL user NULL 8 8 -require_explicit_primary_keys off NULL user NULL off off -results_buffer_size 16384 NULL user NULL 16384 16384 -role none NULL user NULL none none -row_security off NULL user NULL off off -search_path "$user", public NULL user NULL $user,public $user,public -serial_normalization rowid NULL user NULL rowid rowid -server_encoding UTF8 NULL user NULL UTF8 UTF8 -server_version 13.0.0 NULL user NULL 13.0.0 13.0.0 -server_version_num 130000 NULL user NULL 130000 130000 -session_user root NULL user NULL root root -sql_safe_updates off NULL user NULL off off -standard_conforming_strings on NULL user NULL on on -statement_timeout 0 NULL user NULL 0s 0s -stub_catalog_tables on NULL user NULL on on -synchronize_seqscans on NULL user NULL on on -synchronous_commit on NULL user NULL on on -testing_vectorize_inject_panics off NULL user NULL off off -timezone UTC NULL user NULL UTC UTC -tracing off NULL user NULL off off -transaction_isolation serializable NULL user NULL serializable serializable -transaction_priority normal NULL user NULL normal normal -transaction_read_only off NULL user NULL off off -transaction_rows_read_err 0 NULL user NULL 0 0 -transaction_rows_read_log 0 NULL user NULL 0 0 -transaction_rows_written_err 0 NULL user NULL 0 0 -transaction_rows_written_log 0 NULL user NULL 0 0 -transaction_status NoTxn NULL user NULL NoTxn NoTxn -vectorize on NULL user NULL on on +name setting unit context enumvals boot_val reset_val +application_name · NULL user NULL · · +backslash_quote safe_encoding NULL user NULL safe_encoding safe_encoding +bytea_output hex NULL user NULL hex hex +client_encoding UTF8 NULL user NULL UTF8 UTF8 +client_min_messages notice NULL user NULL notice notice +database test NULL user NULL · test +datestyle ISO, MDY NULL user NULL ISO, MDY ISO, MDY +datestyle_enabled off NULL user NULL off off +default_int_size 8 NULL user NULL 8 8 +default_tablespace · NULL user NULL · · +default_transaction_isolation serializable NULL user NULL default default +default_transaction_priority normal NULL user NULL normal normal +default_transaction_read_only off NULL user NULL off off +default_transaction_use_follower_reads off NULL user NULL off off +disable_partially_distributed_plans off NULL user NULL off off +disable_plan_gists off NULL user NULL off off +disallow_full_table_scans off NULL user NULL off off +distsql off NULL user NULL off off +distsql_workmem 64 MiB NULL user NULL 64 MiB 64 MiB +enable_experimental_alter_column_type_general off NULL user NULL off off +enable_experimental_stream_replication off NULL user NULL off off +enable_implicit_select_for_update on NULL user NULL on on +enable_insert_fast_path on NULL user NULL on on +enable_multiregion_placement_policy off NULL user NULL off off +enable_seqscan on NULL user NULL on on +enable_zigzag_join on NULL user NULL on on +escape_string_warning on NULL user NULL on on +experimental_distsql_planning off NULL user NULL off off +experimental_enable_auto_rehoming off NULL user NULL off off +experimental_enable_hash_sharded_indexes off NULL user NULL off off +experimental_enable_implicit_column_partitioning off NULL user NULL off off +experimental_enable_temp_tables off NULL user NULL off off +experimental_enable_unique_without_index_constraints on NULL user NULL off off +experimental_use_new_schema_changer off NULL user NULL off off +extra_float_digits 0 NULL user NULL 0 2 +force_savepoint_restart off NULL user NULL off off +foreign_key_cascades_limit 10000 NULL user NULL 10000 10000 +idle_in_session_timeout 0 NULL user NULL 0s 0s +idle_in_transaction_session_timeout 0 NULL user NULL 0s 0s +inject_retry_errors_enabled off NULL user NULL off off +integer_datetimes on NULL user NULL on on +intervalstyle postgres NULL user NULL postgres postgres +intervalstyle_enabled off NULL user NULL off off +is_superuser on NULL user NULL on on +large_full_scan_rows 1000 NULL user NULL 1000 1000 +lc_collate C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_ctype C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_messages C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_monetary C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_numeric C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +lc_time C.UTF-8 NULL user NULL C.UTF-8 C.UTF-8 +locality region=test,dc=dc1 NULL user NULL region=test,dc=dc1 region=test,dc=dc1 +locality_optimized_partitioned_index_scan on NULL user NULL on on +lock_timeout 0 NULL user NULL 0s 0s +max_identifier_length 128 NULL user NULL 128 128 +max_index_keys 32 NULL user NULL 32 32 +node_id 1 NULL user NULL 1 1 +null_ordered_last off NULL user NULL off off +on_update_rehome_row_enabled on NULL user NULL on on +optimizer_use_histograms on NULL user NULL on on +optimizer_use_multicol_stats on NULL user NULL on on +override_multi_region_zone_config off NULL user NULL off off +prefer_lookup_joins_for_fks off NULL user NULL off off +propagate_input_ordering off NULL user NULL off off +reorder_joins_limit 8 NULL user NULL 8 8 +require_explicit_primary_keys off NULL user NULL off off +results_buffer_size 16384 NULL user NULL 16384 16384 +role none NULL user NULL none none +row_security off NULL user NULL off off +search_path "$user", public NULL user NULL $user,public $user,public +serial_normalization rowid NULL user NULL rowid rowid +server_encoding UTF8 NULL user NULL UTF8 UTF8 +server_version 13.0.0 NULL user NULL 13.0.0 13.0.0 +server_version_num 130000 NULL user NULL 130000 130000 +session_user root NULL user NULL root root +sql_safe_updates off NULL user NULL off off +standard_conforming_strings on NULL user NULL on on +statement_timeout 0 NULL user NULL 0s 0s +stub_catalog_tables on NULL user NULL on on +synchronize_seqscans on NULL user NULL on on +synchronous_commit on NULL user NULL on on +testing_vectorize_inject_panics off NULL user NULL off off +timezone UTC NULL user NULL UTC UTC +tracing off NULL user NULL off off +transaction_isolation serializable NULL user NULL serializable serializable +transaction_priority normal NULL user NULL normal normal +transaction_read_only off NULL user NULL off off +transaction_rows_read_err 0 NULL user NULL 0 0 +transaction_rows_read_log 0 NULL user NULL 0 0 +transaction_rows_written_err 0 NULL user NULL 0 0 +transaction_rows_written_log 0 NULL user NULL 0 0 +transaction_status NoTxn NULL user NULL NoTxn NoTxn +vectorize on NULL user NULL on on query TTTTTT colnames SELECT name, source, min_val, max_val, sourcefile, sourceline FROM pg_catalog.pg_settings ---- -name source min_val max_val sourcefile sourceline -application_name NULL NULL NULL NULL NULL -backslash_quote NULL NULL NULL NULL NULL -bytea_output NULL NULL NULL NULL NULL -client_encoding NULL NULL NULL NULL NULL -client_min_messages NULL NULL NULL NULL NULL -crdb_version NULL NULL NULL NULL NULL -database NULL NULL NULL NULL NULL -datestyle NULL NULL NULL NULL NULL -datestyle_enabled NULL NULL NULL NULL NULL -default_int_size NULL NULL NULL NULL NULL -default_tablespace NULL NULL NULL NULL NULL -default_transaction_isolation NULL NULL NULL NULL NULL -default_transaction_priority NULL NULL NULL NULL NULL -default_transaction_read_only NULL NULL NULL NULL NULL -default_transaction_use_follower_reads NULL NULL NULL NULL NULL -disable_partially_distributed_plans NULL NULL NULL NULL NULL -disable_plan_gists NULL NULL NULL NULL NULL -disallow_full_table_scans NULL NULL NULL NULL NULL -distsql NULL NULL NULL NULL NULL -distsql_workmem NULL NULL NULL NULL NULL -enable_copying_partitioning_when_deinterleaving_table NULL NULL NULL NULL NULL -enable_experimental_alter_column_type_general NULL NULL NULL NULL NULL -enable_experimental_stream_replication NULL NULL NULL NULL NULL -enable_implicit_select_for_update NULL NULL NULL NULL NULL -enable_insert_fast_path NULL NULL NULL NULL NULL -enable_multiregion_placement_policy NULL NULL NULL NULL NULL -enable_seqscan NULL NULL NULL NULL NULL -enable_zigzag_join NULL NULL NULL NULL NULL -escape_string_warning NULL NULL NULL NULL NULL -experimental_distsql_planning NULL NULL NULL NULL NULL -experimental_enable_auto_rehoming NULL NULL NULL NULL NULL -experimental_enable_hash_sharded_indexes NULL NULL NULL NULL NULL -experimental_enable_implicit_column_partitioning NULL NULL NULL NULL NULL -experimental_enable_temp_tables NULL NULL NULL NULL NULL -experimental_enable_unique_without_index_constraints NULL NULL NULL NULL NULL -experimental_use_new_schema_changer NULL NULL NULL NULL NULL -extra_float_digits NULL NULL NULL NULL NULL -force_savepoint_restart NULL NULL NULL NULL NULL -foreign_key_cascades_limit NULL NULL NULL NULL NULL -idle_in_session_timeout NULL NULL NULL NULL NULL -idle_in_transaction_session_timeout NULL NULL NULL NULL NULL -inject_retry_errors_enabled NULL NULL NULL NULL NULL -integer_datetimes NULL NULL NULL NULL NULL -intervalstyle NULL NULL NULL NULL NULL -intervalstyle_enabled NULL NULL NULL NULL NULL -is_superuser NULL NULL NULL NULL NULL -large_full_scan_rows NULL NULL NULL NULL NULL -lc_collate NULL NULL NULL NULL NULL -lc_ctype NULL NULL NULL NULL NULL -lc_messages NULL NULL NULL NULL NULL -lc_monetary NULL NULL NULL NULL NULL -lc_numeric NULL NULL NULL NULL NULL -lc_time NULL NULL NULL NULL NULL -locality NULL NULL NULL NULL NULL -locality_optimized_partitioned_index_scan NULL NULL NULL NULL NULL -lock_timeout NULL NULL NULL NULL NULL -max_identifier_length NULL NULL NULL NULL NULL -max_index_keys NULL NULL NULL NULL NULL -node_id NULL NULL NULL NULL NULL -null_ordered_last NULL NULL NULL NULL NULL -on_update_rehome_row_enabled NULL NULL NULL NULL NULL -optimizer NULL NULL NULL NULL NULL -optimizer_use_histograms NULL NULL NULL NULL NULL -optimizer_use_multicol_stats NULL NULL NULL NULL NULL -override_multi_region_zone_config NULL NULL NULL NULL NULL -prefer_lookup_joins_for_fks NULL NULL NULL NULL NULL -propagate_input_ordering NULL NULL NULL NULL NULL -reorder_joins_limit NULL NULL NULL NULL NULL -require_explicit_primary_keys NULL NULL NULL NULL NULL -results_buffer_size NULL NULL NULL NULL NULL -role NULL NULL NULL NULL NULL -row_security NULL NULL NULL NULL NULL -search_path NULL NULL NULL NULL NULL -serial_normalization NULL NULL NULL NULL NULL -server_encoding NULL NULL NULL NULL NULL -server_version NULL NULL NULL NULL NULL -server_version_num NULL NULL NULL NULL NULL -session_id NULL NULL NULL NULL NULL -session_user NULL NULL NULL NULL NULL -sql_safe_updates NULL NULL NULL NULL NULL -standard_conforming_strings NULL NULL NULL NULL NULL -statement_timeout NULL NULL NULL NULL NULL -stub_catalog_tables NULL NULL NULL NULL NULL -synchronize_seqscans NULL NULL NULL NULL NULL -synchronous_commit NULL NULL NULL NULL NULL -testing_vectorize_inject_panics NULL NULL NULL NULL NULL -timezone NULL NULL NULL NULL NULL -tracing NULL NULL NULL NULL NULL -transaction_isolation NULL NULL NULL NULL NULL -transaction_priority NULL NULL NULL NULL NULL -transaction_read_only NULL NULL NULL NULL NULL -transaction_rows_read_err NULL NULL NULL NULL NULL -transaction_rows_read_log NULL NULL NULL NULL NULL -transaction_rows_written_err NULL NULL NULL NULL NULL -transaction_rows_written_log NULL NULL NULL NULL NULL -transaction_status NULL NULL NULL NULL NULL -vectorize NULL NULL NULL NULL NULL +name source min_val max_val sourcefile sourceline +application_name NULL NULL NULL NULL NULL +backslash_quote NULL NULL NULL NULL NULL +bytea_output NULL NULL NULL NULL NULL +client_encoding NULL NULL NULL NULL NULL +client_min_messages NULL NULL NULL NULL NULL +crdb_version NULL NULL NULL NULL NULL +database NULL NULL NULL NULL NULL +datestyle NULL NULL NULL NULL NULL +datestyle_enabled NULL NULL NULL NULL NULL +default_int_size NULL NULL NULL NULL NULL +default_tablespace NULL NULL NULL NULL NULL +default_transaction_isolation NULL NULL NULL NULL NULL +default_transaction_priority NULL NULL NULL NULL NULL +default_transaction_read_only NULL NULL NULL NULL NULL +default_transaction_use_follower_reads NULL NULL NULL NULL NULL +disable_partially_distributed_plans NULL NULL NULL NULL NULL +disable_plan_gists NULL NULL NULL NULL NULL +disallow_full_table_scans NULL NULL NULL NULL NULL +distsql NULL NULL NULL NULL NULL +distsql_workmem NULL NULL NULL NULL NULL +enable_experimental_alter_column_type_general NULL NULL NULL NULL NULL +enable_experimental_stream_replication NULL NULL NULL NULL NULL +enable_implicit_select_for_update NULL NULL NULL NULL NULL +enable_insert_fast_path NULL NULL NULL NULL NULL +enable_multiregion_placement_policy NULL NULL NULL NULL NULL +enable_seqscan NULL NULL NULL NULL NULL +enable_zigzag_join NULL NULL NULL NULL NULL +escape_string_warning NULL NULL NULL NULL NULL +experimental_distsql_planning NULL NULL NULL NULL NULL +experimental_enable_auto_rehoming NULL NULL NULL NULL NULL +experimental_enable_hash_sharded_indexes NULL NULL NULL NULL NULL +experimental_enable_implicit_column_partitioning NULL NULL NULL NULL NULL +experimental_enable_temp_tables NULL NULL NULL NULL NULL +experimental_enable_unique_without_index_constraints NULL NULL NULL NULL NULL +experimental_use_new_schema_changer NULL NULL NULL NULL NULL +extra_float_digits NULL NULL NULL NULL NULL +force_savepoint_restart NULL NULL NULL NULL NULL +foreign_key_cascades_limit NULL NULL NULL NULL NULL +idle_in_session_timeout NULL NULL NULL NULL NULL +idle_in_transaction_session_timeout NULL NULL NULL NULL NULL +inject_retry_errors_enabled NULL NULL NULL NULL NULL +integer_datetimes NULL NULL NULL NULL NULL +intervalstyle NULL NULL NULL NULL NULL +intervalstyle_enabled NULL NULL NULL NULL NULL +is_superuser NULL NULL NULL NULL NULL +large_full_scan_rows NULL NULL NULL NULL NULL +lc_collate NULL NULL NULL NULL NULL +lc_ctype NULL NULL NULL NULL NULL +lc_messages NULL NULL NULL NULL NULL +lc_monetary NULL NULL NULL NULL NULL +lc_numeric NULL NULL NULL NULL NULL +lc_time NULL NULL NULL NULL NULL +locality NULL NULL NULL NULL NULL +locality_optimized_partitioned_index_scan NULL NULL NULL NULL NULL +lock_timeout NULL NULL NULL NULL NULL +max_identifier_length NULL NULL NULL NULL NULL +max_index_keys NULL NULL NULL NULL NULL +node_id NULL NULL NULL NULL NULL +null_ordered_last NULL NULL NULL NULL NULL +on_update_rehome_row_enabled NULL NULL NULL NULL NULL +optimizer NULL NULL NULL NULL NULL +optimizer_use_histograms NULL NULL NULL NULL NULL +optimizer_use_multicol_stats NULL NULL NULL NULL NULL +override_multi_region_zone_config NULL NULL NULL NULL NULL +prefer_lookup_joins_for_fks NULL NULL NULL NULL NULL +propagate_input_ordering NULL NULL NULL NULL NULL +reorder_joins_limit NULL NULL NULL NULL NULL +require_explicit_primary_keys NULL NULL NULL NULL NULL +results_buffer_size NULL NULL NULL NULL NULL +role NULL NULL NULL NULL NULL +row_security NULL NULL NULL NULL NULL +search_path NULL NULL NULL NULL NULL +serial_normalization NULL NULL NULL NULL NULL +server_encoding NULL NULL NULL NULL NULL +server_version NULL NULL NULL NULL NULL +server_version_num NULL NULL NULL NULL NULL +session_id NULL NULL NULL NULL NULL +session_user NULL NULL NULL NULL NULL +sql_safe_updates NULL NULL NULL NULL NULL +standard_conforming_strings NULL NULL NULL NULL NULL +statement_timeout NULL NULL NULL NULL NULL +stub_catalog_tables NULL NULL NULL NULL NULL +synchronize_seqscans NULL NULL NULL NULL NULL +synchronous_commit NULL NULL NULL NULL NULL +testing_vectorize_inject_panics NULL NULL NULL NULL NULL +timezone NULL NULL NULL NULL NULL +tracing NULL NULL NULL NULL NULL +transaction_isolation NULL NULL NULL NULL NULL +transaction_priority NULL NULL NULL NULL NULL +transaction_read_only NULL NULL NULL NULL NULL +transaction_rows_read_err NULL NULL NULL NULL NULL +transaction_rows_read_log NULL NULL NULL NULL NULL +transaction_rows_written_err NULL NULL NULL NULL NULL +transaction_rows_written_log NULL NULL NULL NULL NULL +transaction_status NULL NULL NULL NULL NULL +vectorize NULL NULL NULL NULL NULL # pg_catalog.pg_sequence @@ -5167,7 +5158,7 @@ indoption query TTI SELECT database_name, descriptor_name, descriptor_id from test.crdb_internal.create_statements where descriptor_name = 'pg_views' ---- -test pg_views 4294967014 +test pg_views 4294967015 # Verify INCLUDED columns appear in pg_index. See issue #59563 statement ok diff --git a/pkg/sql/logictest/testdata/logic_test/show_source b/pkg/sql/logictest/testdata/logic_test/show_source index 3af3a22abe54..da2d9214dd5e 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_source +++ b/pkg/sql/logictest/testdata/logic_test/show_source @@ -45,7 +45,6 @@ disable_plan_gists off disallow_full_table_scans off distsql off distsql_workmem 64 MiB -enable_copying_partitioning_when_deinterleaving_table off enable_experimental_alter_column_type_general off enable_experimental_stream_replication off enable_implicit_select_for_update on diff --git a/pkg/sql/logictest/testdata/logic_test/table b/pkg/sql/logictest/testdata/logic_test/table index 96a29d7e7f98..4e93cf1125c6 100644 --- a/pkg/sql/logictest/testdata/logic_test/table +++ b/pkg/sql/logictest/testdata/logic_test/table @@ -580,7 +580,6 @@ gossip_network NULL gossip_nodes NULL index_columns NULL index_usage_statistics NULL -interleaved NULL invalid_objects NULL jobs NULL kv_node_liveness NULL diff --git a/pkg/sql/opt/cat/BUILD.bazel b/pkg/sql/opt/cat/BUILD.bazel index cd58913190ae..8ccdc2ef326f 100644 --- a/pkg/sql/opt/cat/BUILD.bazel +++ b/pkg/sql/opt/cat/BUILD.bazel @@ -30,7 +30,6 @@ go_library( "//pkg/sql/sem/tree", "//pkg/sql/sessiondata", "//pkg/sql/types", - "//pkg/util", "//pkg/util/treeprinter", "@com_github_cockroachdb_errors//:errors", "@com_github_lib_pq//oid", diff --git a/pkg/sql/opt/cat/index.go b/pkg/sql/opt/cat/index.go index 2806c983e546..94b6e235c00b 100644 --- a/pkg/sql/opt/cat/index.go +++ b/pkg/sql/opt/cat/index.go @@ -174,49 +174,6 @@ type Index interface { // list, and ImplicitPartitioningColumnCount < LaxKeyColumnCount. ImplicitPartitioningColumnCount() int - // InterleaveAncestorCount returns the number of interleave ancestors for this - // index (or zero if this is not an interleaved index). Each ancestor is an - // index (usually from another table) with a key that shares a prefix with - // the key of this index. - // - // Each ancestor contributes one or more key columns; together these pieces - // form a prefix of an index key. - // - // The ancestors appear in the order they appear in an encoded key. This means - // they are always in the far-to-near ancestor order (e.g. - // grand-grand-parent, grand-parent, parent). - // - // - // Example: - // Index 1 -> /a/b - // Index 2 -> /a/b/c - // Index 3 -> /a/b/c/d - // - // Index 3 has two ancestors; the first is index 1 (contributing 2 key - // columns) and the second is index 2 (contributing 1 key column). - InterleaveAncestorCount() int - - // InterleaveAncestor returns information about an ancestor index. - // - // numKeyCols is the number of key columns that this ancestor contributes to - // an encoded key. In other words: each ancestor has a shared key prefix - // with this index; numKeyCols is the difference the shared prefix length for - // this ancestor and the shared prefix length for the previous ancestor. - // See InterleaveAncestorCount for an example. - InterleaveAncestor(i int) (table, index StableID, numKeyCols int) - - // InterleavedByCount returns the number of indexes (usually from other - // tables) that are interleaved into this index. - // - // Note that these indexes can themselves be interleaved by other indexes, but - // this list contains only those for which this index is a direct interleave - // parent. - InterleavedByCount() int - - // InterleavedBy returns information about an index that is interleaved into - // this index; see InterleavedByCount. - InterleavedBy(i int) (table, index StableID) - // GeoConfig returns a geospatial index configuration. If non-nil, it // describes the configuration for this geospatial inverted index. GeoConfig() *geoindex.Config diff --git a/pkg/sql/opt/cat/utils.go b/pkg/sql/opt/cat/utils.go index 29397031de9c..ff1ddca49076 100644 --- a/pkg/sql/opt/cat/utils.go +++ b/pkg/sql/opt/cat/utils.go @@ -19,7 +19,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/treeprinter" "github.com/cockroachdb/errors" ) @@ -239,23 +238,6 @@ func formatCatalogIndex(tab Table, ord int, tp treeprinter.Node) { FormatZone(p.Zone(), part) } } - if n := idx.InterleaveAncestorCount(); n > 0 { - c := child.Child("interleave ancestors") - for i := 0; i < n; i++ { - table, index, numKeyCols := idx.InterleaveAncestor(i) - c.Childf( - "table=%d index=%d (%d key column%s)", - table, index, numKeyCols, util.Pluralize(int64(numKeyCols)), - ) - } - } - if n := idx.InterleavedByCount(); n > 0 { - c := child.Child("interleaved by") - for i := 0; i < n; i++ { - table, index := idx.InterleavedBy(i) - c.Childf("table=%d index=%d", table, index) - } - } if pred, isPartial := idx.Predicate(); isPartial { child.Childf("WHERE %s", pred) } diff --git a/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema b/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema index 66c38d98449e..573cf92f68ea 100644 --- a/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema +++ b/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema @@ -29,7 +29,7 @@ CREATE TABLE district d_next_o_id integer, primary key (d_w_id, d_id), foreign key (d_w_id) references warehouse (w_id) -) interleave in parent warehouse (d_w_id) +) ---- exec-ddl @@ -94,7 +94,7 @@ CREATE TABLE "order" primary key (o_w_id, o_d_id, o_id DESC), unique index order_idx (o_w_id, o_d_id, o_c_id, o_id DESC) storing (o_entry_d, o_carrier_id), foreign key (o_w_id, o_d_id, o_c_id) references customer (c_w_id, c_d_id, c_id) -) interleave in parent district (o_w_id, o_d_id) +) ---- exec-ddl @@ -105,7 +105,7 @@ CREATE TABLE new_order no_w_id integer not null, primary key (no_w_id, no_d_id, no_o_id), foreign key (no_w_id, no_d_id, no_o_id) references "order" (o_w_id, o_d_id, o_id) -) interleave in parent "order" (no_w_id, no_d_id, no_o_id) +) ---- exec-ddl @@ -162,5 +162,5 @@ CREATE TABLE order_line primary key (ol_w_id, ol_d_id, ol_o_id DESC, ol_number), foreign key (ol_w_id, ol_d_id, ol_o_id) references "order" (o_w_id, o_d_id, o_id), foreign key (ol_supply_w_id, ol_i_id) references stock (s_w_id, s_i_id) -) interleave in parent "order" (ol_w_id, ol_d_id, ol_o_id) +) ---- diff --git a/pkg/sql/opt/testutils/testcat/create_table.go b/pkg/sql/opt/testutils/testcat/create_table.go index b5e65220bfa8..6fae6a02ba13 100644 --- a/pkg/sql/opt/testutils/testcat/create_table.go +++ b/pkg/sql/opt/testutils/testcat/create_table.go @@ -76,12 +76,6 @@ func (tc *Catalog) CreateTable(stmt *tree.CreateTable) *Table { tab := &Table{TabID: tc.nextStableID(), TabName: stmt.Table, Catalog: tc} - // TODO(andyk): For now, just remember that the table was interleaved. In the - // future, it may be necessary to extract additional metadata. - if stmt.Interleave != nil { - tab.interleaved = true - } - // Find the PK columns; we have to force these to be non-nullable. pkCols := make(map[tree.Name]struct{}) for _, def := range stmt.Defs { diff --git a/pkg/sql/opt/testutils/testcat/test_catalog.go b/pkg/sql/opt/testutils/testcat/test_catalog.go index b092c570bf65..5cbda4006e9e 100644 --- a/pkg/sql/opt/testutils/testcat/test_catalog.go +++ b/pkg/sql/opt/testutils/testcat/test_catalog.go @@ -602,10 +602,6 @@ type Table struct { writeOnlyIdxCount int deleteOnlyIdxCount int - // interleaved is true if the table's rows are interleaved with rows from - // other table(s). - interleaved bool - outboundFKs []ForeignKeyConstraint inboundFKs []ForeignKeyConstraint @@ -952,26 +948,6 @@ func (ti *Index) ImplicitPartitioningColumnCount() int { return 0 } -// InterleaveAncestorCount is part of the cat.Index interface. -func (ti *Index) InterleaveAncestorCount() int { - return 0 -} - -// InterleaveAncestor is part of the cat.Index interface. -func (ti *Index) InterleaveAncestor(i int) (table, index cat.StableID, numKeyCols int) { - panic("no interleavings") -} - -// InterleavedByCount is part of the cat.Index interface. -func (ti *Index) InterleavedByCount() int { - return 0 -} - -// InterleavedBy is part of the cat.Index interface. -func (ti *Index) InterleavedBy(i int) (table, index cat.StableID) { - panic("no interleavings") -} - // GeoConfig is part of the cat.Index interface. func (ti *Index) GeoConfig() *geoindex.Config { return ti.geoConfig diff --git a/pkg/sql/opt/xform/testdata/external/customer b/pkg/sql/opt/xform/testdata/external/customer index 3d7fbdb89b34..87ea560bba3a 100644 --- a/pkg/sql/opt/xform/testdata/external/customer +++ b/pkg/sql/opt/xform/testdata/external/customer @@ -72,7 +72,7 @@ CREATE TABLE article ( UNIQUE INDEX article_hash_key (hash ASC), UNIQUE INDEX article_idx_read_key (id ASC) STORING (read), FAMILY "primary" (id, feed, folder, hash, title, summary, content, link, read, date, retrieved) -) INTERLEAVE IN PARENT feed (folder, feed) +) ---- opt diff --git a/pkg/sql/opt/xform/testdata/rules/join b/pkg/sql/opt/xform/testdata/rules/join index 3aa2d0265b70..817157d0810d 100644 --- a/pkg/sql/opt/xform/testdata/rules/join +++ b/pkg/sql/opt/xform/testdata/rules/join @@ -240,160 +240,6 @@ memo (optimized, ~37KB, required=[presentation: a:1,b:2,c:3,s:7,t:8,u:9,x:12,y:1 ├── G16: (eq G14 G17) └── G17: (variable x) -# Regression test for #36226. -exec-ddl -CREATE TABLE parent1 (pid1 INT PRIMARY KEY, pa1 INT) ----- - -exec-ddl -CREATE TABLE child1 ( - pid1 INT, - cid1 INT, - ca1 INT, - PRIMARY KEY(pid1, cid1) -) -INTERLEAVE IN PARENT parent1 (pid1) ----- - -exec-ddl -CREATE TABLE grandchild1 ( - pid1 INT, - cid1 INT, - gcid1 INT, - gca1 INT, - PRIMARY KEY(pid1, cid1, gcid1) -) -INTERLEAVE IN PARENT child1 (pid1, cid1) ----- - -opt expect=ReorderJoins join-limit=4 -SELECT * FROM grandchild1 -JOIN child1 USING (pid1, cid1) -JOIN parent1 USING (pid1) -ORDER BY pid1 ----- -project - ├── columns: pid1:1!null cid1:2!null gcid1:3!null gca1:4 ca1:9 pa1:13 - ├── key: (1-3) - ├── fd: (1-3)-->(4), (1,2)-->(9), (1)-->(13) - ├── ordering: +1 - └── inner-join (lookup parent1) - ├── columns: grandchild1.pid1:1!null grandchild1.cid1:2!null gcid1:3!null gca1:4 child1.pid1:7!null child1.cid1:8!null ca1:9 parent1.pid1:12!null pa1:13 - ├── key columns: [1] = [12] - ├── lookup columns are key - ├── key: (3,8,12) - ├── fd: (1-3)-->(4), (7,8)-->(9), (1)==(7,12), (7)==(1,12), (2)==(8), (8)==(2), (12)-->(13), (12)==(1,7) - ├── ordering: +(1|7|12) [actual: +1] - ├── inner-join (merge) - │ ├── columns: grandchild1.pid1:1!null grandchild1.cid1:2!null gcid1:3!null gca1:4 child1.pid1:7!null child1.cid1:8!null ca1:9 - │ ├── left ordering: +1,+2 - │ ├── right ordering: +7,+8 - │ ├── key: (3,7,8) - │ ├── fd: (1-3)-->(4), (7,8)-->(9), (1)==(7), (7)==(1), (2)==(8), (8)==(2) - │ ├── ordering: +(1|7) [actual: +1] - │ ├── scan grandchild1 - │ │ ├── columns: grandchild1.pid1:1!null grandchild1.cid1:2!null gcid1:3!null gca1:4 - │ │ ├── key: (1-3) - │ │ ├── fd: (1-3)-->(4) - │ │ └── ordering: +1,+2 - │ ├── scan child1 - │ │ ├── columns: child1.pid1:7!null child1.cid1:8!null ca1:9 - │ │ ├── key: (7,8) - │ │ ├── fd: (7,8)-->(9) - │ │ └── ordering: +7,+8 - │ └── filters (true) - └── filters (true) - -memo expect=ReorderJoins join-limit=4 -SELECT * FROM grandchild1 -JOIN child1 USING (pid1, cid1) -JOIN parent1 USING (pid1) -ORDER BY pid1 ----- -memo (optimized, ~34KB, required=[presentation: pid1:1,cid1:2,gcid1:3,gca1:4,ca1:9,pa1:13] [ordering: +1]) - ├── G1: (project G2 G3 pid1 cid1 gcid1 gca1 ca1 pa1) - │ ├── [presentation: pid1:1,cid1:2,gcid1:3,gca1:4,ca1:9,pa1:13] [ordering: +1] - │ │ ├── best: (project G2="[ordering: +(1|7|12)]" G3 pid1 cid1 gcid1 gca1 ca1 pa1) - │ │ └── cost: 2816.50 - │ └── [] - │ ├── best: (project G2 G3 pid1 cid1 gcid1 gca1 ca1 pa1) - │ └── cost: 2816.50 - ├── G2: (inner-join G4 G5 G6) (inner-join G7 G8 G9) (inner-join G8 G7 G9) (inner-join G10 G11 G9) (inner-join G11 G10 G9) (inner-join G5 G4 G6) (merge-join G4 G5 G12 inner-join,+1,+12) (lookup-join G4 G12 parent1,keyCols=[1],outCols=(1-4,7-9,12,13)) (merge-join G7 G8 G12 inner-join,+1,+2,+7,+8) (merge-join G8 G7 G12 inner-join,+7,+8,+1,+2) (lookup-join G8 G12 grandchild1,keyCols=[7 8],outCols=(1-4,7-9,12,13)) (merge-join G10 G11 G12 inner-join,+7,+8,+1,+2) (merge-join G11 G10 G12 inner-join,+1,+2,+7,+8) (lookup-join G11 G12 child1,keyCols=[1 2],outCols=(1-4,7-9,12,13)) (merge-join G5 G4 G12 inner-join,+12,+1) - │ ├── [ordering: +(1|7|12)] - │ │ ├── best: (lookup-join G4="[ordering: +(1|7)]" G12 parent1,keyCols=[1],outCols=(1-4,7-9,12,13)) - │ │ └── cost: 2815.48 - │ └── [] - │ ├── best: (lookup-join G4 G12 parent1,keyCols=[1],outCols=(1-4,7-9,12,13)) - │ └── cost: 2815.48 - ├── G3: (projections) - ├── G4: (inner-join G7 G10 G9) (inner-join G10 G7 G9) (merge-join G7 G10 G12 inner-join,+1,+2,+7,+8) (lookup-join G7 G12 child1,keyCols=[1 2],outCols=(1-4,7-9)) (merge-join G10 G7 G12 inner-join,+7,+8,+1,+2) (lookup-join G10 G12 grandchild1,keyCols=[7 8],outCols=(1-4,7-9)) - │ ├── [ordering: +(1|7)] - │ │ ├── best: (merge-join G7="[ordering: +1,+2]" G10="[ordering: +7,+8]" G12 inner-join,+1,+2,+7,+8) - │ │ └── cost: 2210.46 - │ └── [] - │ ├── best: (merge-join G7="[ordering: +1,+2]" G10="[ordering: +7,+8]" G12 inner-join,+1,+2,+7,+8) - │ └── cost: 2210.46 - ├── G5: (scan parent1,cols=(12,13)) - │ ├── [ordering: +12] - │ │ ├── best: (scan parent1,cols=(12,13)) - │ │ └── cost: 1064.42 - │ └── [] - │ ├── best: (scan parent1,cols=(12,13)) - │ └── cost: 1064.42 - ├── G6: (filters G13) - ├── G7: (scan grandchild1,cols=(1-4)) - │ ├── [ordering: +1,+2] - │ │ ├── best: (scan grandchild1,cols=(1-4)) - │ │ └── cost: 1104.82 - │ ├── [ordering: +1] - │ │ ├── best: (scan grandchild1,cols=(1-4)) - │ │ └── cost: 1104.82 - │ └── [] - │ ├── best: (scan grandchild1,cols=(1-4)) - │ └── cost: 1104.82 - ├── G8: (inner-join G10 G5 G14) (inner-join G5 G10 G14) (merge-join G10 G5 G12 inner-join,+7,+12) (lookup-join G10 G12 parent1,keyCols=[7],outCols=(7-9,12,13)) (merge-join G5 G10 G12 inner-join,+12,+7) (lookup-join G5 G12 child1,keyCols=[12],outCols=(7-9,12,13)) - │ ├── [ordering: +(7|12),+8] - │ │ ├── best: (sort G8) - │ │ └── cost: 2459.51 - │ ├── [ordering: +(7|12)] - │ │ ├── best: (merge-join G10="[ordering: +7]" G5="[ordering: +12]" G12 inner-join,+7,+12) - │ │ └── cost: 2179.06 - │ └── [] - │ ├── best: (merge-join G10="[ordering: +7]" G5="[ordering: +12]" G12 inner-join,+7,+12) - │ └── cost: 2179.06 - ├── G9: (filters G15 G16) - ├── G10: (scan child1,cols=(7-9)) - │ ├── [ordering: +7,+8] - │ │ ├── best: (scan child1,cols=(7-9)) - │ │ └── cost: 1084.62 - │ ├── [ordering: +7] - │ │ ├── best: (scan child1,cols=(7-9)) - │ │ └── cost: 1084.62 - │ └── [] - │ ├── best: (scan child1,cols=(7-9)) - │ └── cost: 1084.62 - ├── G11: (inner-join G7 G5 G6) (inner-join G5 G7 G6) (merge-join G7 G5 G12 inner-join,+1,+12) (lookup-join G7 G12 parent1,keyCols=[1],outCols=(1-4,12,13)) (merge-join G5 G7 G12 inner-join,+12,+1) (lookup-join G5 G12 grandchild1,keyCols=[12],outCols=(1-4,12,13)) - │ ├── [ordering: +(1|12),+2] - │ │ ├── best: (sort G11) - │ │ └── cost: 2489.71 - │ ├── [ordering: +(1|12)] - │ │ ├── best: (merge-join G7="[ordering: +1]" G5="[ordering: +12]" G12 inner-join,+1,+12) - │ │ └── cost: 2199.26 - │ └── [] - │ ├── best: (merge-join G7="[ordering: +1]" G5="[ordering: +12]" G12 inner-join,+1,+12) - │ └── cost: 2199.26 - ├── G12: (filters) - ├── G13: (eq G17 G18) - ├── G14: (filters G19) - ├── G15: (eq G17 G20) - ├── G16: (eq G21 G22) - ├── G17: (variable grandchild1.pid1) - ├── G18: (variable parent1.pid1) - ├── G19: (eq G20 G18) - ├── G20: (variable child1.pid1) - ├── G21: (variable grandchild1.cid1) - └── G22: (variable child1.cid1) - # Cross joins are not reordered apart from commutation. memo SELECT * FROM abc, stu, xyz, pqr WHERE a = 1 diff --git a/pkg/sql/opt_catalog.go b/pkg/sql/opt_catalog.go index 48a49029fd49..cda502e15bc0 100644 --- a/pkg/sql/opt_catalog.go +++ b/pkg/sql/opt_catalog.go @@ -1408,28 +1408,6 @@ func (oi *optIndex) ImplicitPartitioningColumnCount() int { return oi.idx.GetPartitioning().NumImplicitColumns() } -// InterleaveAncestorCount is part of the cat.Index interface. -func (oi *optIndex) InterleaveAncestorCount() int { - return oi.idx.NumInterleaveAncestors() -} - -// InterleaveAncestor is part of the cat.Index interface. -func (oi *optIndex) InterleaveAncestor(i int) (table, index cat.StableID, numKeyCols int) { - a := oi.idx.GetInterleaveAncestor(i) - return cat.StableID(a.TableID), cat.StableID(a.IndexID), int(a.SharedPrefixLen) -} - -// InterleavedByCount is part of the cat.Index interface. -func (oi *optIndex) InterleavedByCount() int { - return oi.idx.NumInterleavedBy() -} - -// InterleavedBy is part of the cat.Index interface. -func (oi *optIndex) InterleavedBy(i int) (table, index cat.StableID) { - ref := oi.idx.GetInterleavedBy(i) - return cat.StableID(ref.Table), cat.StableID(ref.Index) -} - // GeoConfig is part of the cat.Index interface. func (oi *optIndex) GeoConfig() *geoindex.Config { return &oi.idx.IndexDesc().GeoConfig @@ -2177,26 +2155,6 @@ func (oi *optVirtualIndex) ImplicitPartitioningColumnCount() int { return 0 } -// InterleaveAncestorCount is part of the cat.Index interface. -func (oi *optVirtualIndex) InterleaveAncestorCount() int { - return 0 -} - -// InterleaveAncestor is part of the cat.Index interface. -func (oi *optVirtualIndex) InterleaveAncestor(i int) (table, index cat.StableID, numKeyCols int) { - panic(errors.AssertionFailedf("no interleavings")) -} - -// InterleavedByCount is part of the cat.Index interface. -func (oi *optVirtualIndex) InterleavedByCount() int { - return 0 -} - -// InterleavedBy is part of the cat.Index interface. -func (oi *optVirtualIndex) InterleavedBy(i int) (table, index cat.StableID) { - panic(errors.AssertionFailedf("no interleavings")) -} - // GeoConfig is part of the cat.Index interface. func (oi *optVirtualIndex) GeoConfig() *geoindex.Config { return nil diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 844f79fba8ac..4aaa25fe062e 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -795,7 +795,7 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { %token HAVING HASH HIGH HISTOGRAM HOUR %token IDENTITY -%token IF IFERROR IFNULL IGNORE_FOREIGN_KEYS ILIKE IMMEDIATE IMPORT IN INCLUDE INCLUDE_DEPRECATED_INTERLEAVES INCLUDING INCREMENT INCREMENTAL +%token IF IFERROR IFNULL IGNORE_FOREIGN_KEYS ILIKE IMMEDIATE IMPORT IN INCLUDE INCLUDING INCREMENT INCREMENTAL %token INET INET_CONTAINED_BY_OR_EQUALS %token INET_CONTAINS_OR_EQUALS INDEX INDEXES INHERITS INJECT INTERLEAVE INITIALLY %token INNER INSERT INT INTEGER @@ -2596,7 +2596,6 @@ opt_clear_data: // encryption_passphrase="secret": encrypt backups // kms="[kms_provider]://[kms_host]/[master_key_identifier]?[parameters]" : encrypt backups using KMS // detached: execute backup job asynchronously, without waiting for its completion -// include_deprecated_interleaves: allow backing up interleaved tables, even if future versions will be unable to restore. // // %SeeAlso: RESTORE, WEBDOCS/backup.html backup_stmt: @@ -2702,10 +2701,6 @@ backup_options: { $$.val = &tree.BackupOptions{EncryptionKMSURI: $3.stringOrPlaceholderOptList()} } -| INCLUDE_DEPRECATED_INTERLEAVES - { - $$.val = &tree.BackupOptions{IncludeDeprecatedInterleaves: true} - } // %Help: CREATE SCHEDULE FOR BACKUP - backup data periodically @@ -13254,7 +13249,6 @@ unreserved_keyword: | IMMEDIATE | IMPORT | INCLUDE -| INCLUDE_DEPRECATED_INTERLEAVES | INCLUDING | INCREMENT | INCREMENTAL diff --git a/pkg/sql/parser/testdata/backup_restore b/pkg/sql/parser/testdata/backup_restore index de734d0e1646..6bd44dbcc447 100644 --- a/pkg/sql/parser/testdata/backup_restore +++ b/pkg/sql/parser/testdata/backup_restore @@ -281,14 +281,6 @@ BACKUP TABLE foo TO '_' WITH revision_history, encryption_passphrase = '*****' - BACKUP TABLE _ TO 'bar' WITH revision_history, encryption_passphrase = '*****' -- identifiers removed BACKUP TABLE foo TO 'bar' WITH revision_history, encryption_passphrase = 'secret' -- passwords exposed -parse -BACKUP foo TO 'bar' WITH KMS = 'foo', revision_history, include_deprecated_interleaves ----- -BACKUP TABLE foo TO 'bar' WITH revision_history, kms = 'foo', include_deprecated_interleaves -- normalized! -BACKUP TABLE (foo) TO ('bar') WITH revision_history, kms = ('foo'), include_deprecated_interleaves -- fully parenthesized -BACKUP TABLE foo TO '_' WITH revision_history, kms = '_', include_deprecated_interleaves -- literals removed -BACKUP TABLE _ TO 'bar' WITH revision_history, kms = 'foo', include_deprecated_interleaves -- identifiers removed - parse BACKUP foo TO 'bar' WITH KMS = ('foo', 'bar'), revision_history ---- diff --git a/pkg/sql/pg_catalog.go b/pkg/sql/pg_catalog.go index 85c925d7a312..888c82925ac9 100644 --- a/pkg/sql/pg_catalog.go +++ b/pkg/sql/pg_catalog.go @@ -1807,11 +1807,11 @@ https://www.postgresql.org/docs/9.5/view-pg-indexes.html`, populate: func(ctx context.Context, p *planner, dbContext catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error { h := makeOidHasher() return forEachTableDescWithTableLookup(ctx, p, dbContext, hideVirtual, /* virtual tables do not have indexes */ - func(db catalog.DatabaseDescriptor, scName string, table catalog.TableDescriptor, tableLookup tableLookupFn) error { + func(db catalog.DatabaseDescriptor, scName string, table catalog.TableDescriptor, _ tableLookupFn) error { scNameName := tree.NewDName(scName) tblName := tree.NewDName(table.GetName()) return catalog.ForEachIndex(table, catalog.IndexOpts{}, func(index catalog.Index) error { - def, err := indexDefFromDescriptor(ctx, p, db, scName, table, index, tableLookup) + def, err := indexDefFromDescriptor(ctx, p, db, scName, table, index) if err != nil { return err } @@ -1838,7 +1838,6 @@ func indexDefFromDescriptor( schemaName string, table catalog.TableDescriptor, index catalog.Index, - tableLookup tableLookupFn, ) (string, error) { colNames := index.IndexDesc().KeyColumnNames[index.ExplicitColumnStartIdx():] indexDef := tree.CreateIndex{ @@ -1884,34 +1883,6 @@ func indexDefFromDescriptor( name := index.GetStoredColumnName(i) indexDef.Storing[i] = tree.Name(name) } - if index.NumInterleaveAncestors() > 0 { - intl := index.IndexDesc().Interleave - parentTable, err := tableLookup.getTableByID(intl.Ancestors[len(intl.Ancestors)-1].TableID) - if err != nil { - return "", err - } - parentSchemaName := tableLookup.getSchemaName(parentTable) - parentDb, err := tableLookup.getDatabaseByID(parentTable.GetParentID()) - if err != nil { - return "", err - } - var sharedPrefixLen int - for _, ancestor := range intl.Ancestors { - sharedPrefixLen += int(ancestor.SharedPrefixLen) - } - fields := colNames[:sharedPrefixLen] - intlDef := &tree.InterleaveDef{ - Parent: tree.MakeTableNameWithSchema( - tree.Name(parentDb.GetName()), - tree.Name(parentSchemaName), - tree.Name(parentTable.GetName())), - Fields: make(tree.NameList, len(fields)), - } - for i, field := range fields { - intlDef.Fields[i] = tree.Name(field) - } - indexDef.Interleave = intlDef - } if index.IsPartial() { // Format the raw predicate for display in order to resolve user-defined // types to a human readable form. diff --git a/pkg/sql/pgwire/testdata/pgtest/notice b/pkg/sql/pgwire/testdata/pgtest/notice index d6a9ee5c8925..906613ce9ed2 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","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":547,"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":536,"Routine":"dropIndexByName","UnknownFields":null} {"Type":"CommandComplete","CommandTag":"DROP INDEX"} until noncrdb_only diff --git a/pkg/sql/randgen/schema.go b/pkg/sql/randgen/schema.go index 6db9ad36feb6..49e95d8062da 100644 --- a/pkg/sql/randgen/schema.go +++ b/pkg/sql/randgen/schema.go @@ -77,8 +77,7 @@ func RandCreateTables( // Make some random tables. tables := make([]tree.Statement, num) for i := 0; i < num; i++ { - var interleave *tree.CreateTable - t := RandCreateTableWithInterleave(rng, prefix, i+1, interleave, nil) + t := RandCreateTable(rng, prefix, i+1) tables[i] = t } @@ -91,25 +90,13 @@ func RandCreateTables( // RandCreateTable creates a random CreateTable definition. func RandCreateTable(rng *rand.Rand, prefix string, tableIdx int) *tree.CreateTable { - return RandCreateTableWithInterleave(rng, prefix, tableIdx, nil, nil) + return RandCreateTableWithColumnIndexNumberGenerator(rng, prefix, tableIdx, nil /* generateColumnIndexNumber */) } // RandCreateTableWithColumnIndexNumberGenerator creates a random CreateTable definition // using the passed function to generate column index numbers for column names. func RandCreateTableWithColumnIndexNumberGenerator( rng *rand.Rand, prefix string, tableIdx int, generateColumnIndexNumber func() int64, -) *tree.CreateTable { - return RandCreateTableWithInterleave(rng, prefix, tableIdx, nil, generateColumnIndexNumber) -} - -// RandCreateTableWithInterleave creates a random CreateTable definition, -// interleaved into the given other CreateTable definition. -func RandCreateTableWithInterleave( - rng *rand.Rand, - prefix string, - tableIdx int, - interleaveInto *tree.CreateTable, - generateColumnIndexNumber func() int64, ) *tree.CreateTable { // columnDefs contains the list of Columns we'll add to our table. nColumns := randutil.RandIntInRange(rng, 1, 20) @@ -118,22 +105,6 @@ func RandCreateTableWithInterleave( // families, etc) we'll add to our table. defs := make(tree.TableDefs, 0, len(columnDefs)) - // Find columnDefs from previous create table. - interleaveIntoColumnDefs := make(map[tree.Name]*tree.ColumnTableDef) - var interleaveIntoPK *tree.UniqueConstraintTableDef - if interleaveInto != nil { - for i := range interleaveInto.Defs { - switch d := interleaveInto.Defs[i].(type) { - case *tree.ColumnTableDef: - interleaveIntoColumnDefs[d.Name] = d - case *tree.UniqueConstraintTableDef: - if d.PrimaryKey { - interleaveIntoPK = d - } - } - } - } - // colIdx generates numbers that are incorporated into column names. colIdx := func(ordinal int) int { if generateColumnIndexNumber != nil { @@ -142,98 +113,43 @@ func RandCreateTableWithInterleave( return ordinal } - var interleaveDef *tree.InterleaveDef - if interleaveIntoPK != nil && len(interleaveIntoPK.Columns) > 0 { - // Make the interleave prefix, which has to be exactly the columns in the - // parent's primary index. - prefixLength := len(interleaveIntoPK.Columns) - fields := make(tree.NameList, prefixLength) - for i := range interleaveIntoPK.Columns[:prefixLength] { - def := interleaveIntoColumnDefs[interleaveIntoPK.Columns[i].Column] - columnDefs = append(columnDefs, def) - defs = append(defs, def) - fields[i] = def.Name - } - - extraCols := make([]*tree.ColumnTableDef, nColumns) - // Add more columns to the table. - for i := range extraCols { - // Loop until we generate an indexable column type. - var extraCol *tree.ColumnTableDef - for { - extraCol = randColumnTableDef(rng, tableIdx, colIdx(i+prefixLength)) - extraColType := tree.MustBeStaticallyKnownType(extraCol.Type) - if colinfo.ColumnTypeIsIndexable(extraColType) { - break - } - } - extraCols[i] = extraCol - columnDefs = append(columnDefs, extraCol) - defs = append(defs, extraCol) - } + // Make new defs from scratch. + nComputedColumns := randutil.RandIntInRange(rng, 0, (nColumns+1)/2) + nNormalColumns := nColumns - nComputedColumns + for i := 0; i < nNormalColumns; i++ { + columnDef := randColumnTableDef(rng, tableIdx, colIdx(i)) + columnDefs = append(columnDefs, columnDef) + defs = append(defs, columnDef) + } - rng.Shuffle(nColumns, func(i, j int) { - extraCols[i], extraCols[j] = extraCols[j], extraCols[i] - }) - - // Create the primary key to interleave, maybe add some new columns to the - // one we're interleaving. - pk := &tree.UniqueConstraintTableDef{ - PrimaryKey: true, - IndexTableDef: tree.IndexTableDef{ - Columns: interleaveIntoPK.Columns[:prefixLength:prefixLength], - }, - } - for i := range extraCols[:rng.Intn(len(extraCols))] { - pk.Columns = append(pk.Columns, tree.IndexElem{ - Column: extraCols[i].Name, - Direction: tree.Direction(rng.Intn(int(tree.Descending) + 1)), + // Make a random primary key with high likelihood. + if rng.Intn(8) != 0 { + indexDef, ok := randIndexTableDefFromCols(rng, columnDefs, false /* allowExpressions */) + if ok && !indexDef.Inverted { + defs = append(defs, &tree.UniqueConstraintTableDef{ + PrimaryKey: true, + IndexTableDef: indexDef, }) } - defs = append(defs, pk) - interleaveDef = &tree.InterleaveDef{ - Parent: interleaveInto.Table, - Fields: fields, - } - } else { - // Make new defs from scratch. - nComputedColumns := randutil.RandIntInRange(rng, 0, (nColumns+1)/2) - nNormalColumns := nColumns - nComputedColumns - for i := 0; i < nNormalColumns; i++ { - columnDef := randColumnTableDef(rng, tableIdx, colIdx(i)) - columnDefs = append(columnDefs, columnDef) - defs = append(defs, columnDef) - } - - // Make a random primary key with high likelihood. - if rng.Intn(8) != 0 { - indexDef, ok := randIndexTableDefFromCols(rng, columnDefs, false /* allowExpressions */) - if ok && !indexDef.Inverted { - defs = append(defs, &tree.UniqueConstraintTableDef{ - PrimaryKey: true, - IndexTableDef: indexDef, - }) - } - // Although not necessary for Cockroach to function correctly, - // but for ease of use for any code that introspects on the - // AST data structure (instead of the descriptor which doesn't - // exist yet), explicitly set all PK cols as NOT NULL. - for _, col := range columnDefs { - for _, elem := range indexDef.Columns { - if col.Name == elem.Column { - col.Nullable.Nullability = tree.NotNull - } + // Although not necessary for Cockroach to function correctly, + // but for ease of use for any code that introspects on the + // AST data structure (instead of the descriptor which doesn't + // exist yet), explicitly set all PK cols as NOT NULL. + for _, col := range columnDefs { + for _, elem := range indexDef.Columns { + if col.Name == elem.Column { + col.Nullable.Nullability = tree.NotNull } } } + } - // Make defs for computed columns. - normalColDefs := columnDefs - for i := nNormalColumns; i < nColumns; i++ { - columnDef := randComputedColumnTableDef(rng, normalColDefs, tableIdx, colIdx(i)) - columnDefs = append(columnDefs, columnDef) - defs = append(defs, columnDef) - } + // Make defs for computed columns. + normalColDefs := columnDefs + for i := nNormalColumns; i < nColumns; i++ { + columnDef := randComputedColumnTableDef(rng, normalColDefs, tableIdx, colIdx(i)) + columnDefs = append(columnDefs, columnDef) + defs = append(defs, columnDef) } // Make indexes. @@ -256,9 +172,8 @@ func RandCreateTableWithInterleave( } ret := &tree.CreateTable{ - Table: tree.MakeUnqualifiedTableName(tree.Name(fmt.Sprintf("%s%d", prefix, tableIdx))), - Defs: defs, - Interleave: interleaveDef, + Table: tree.MakeUnqualifiedTableName(tree.Name(fmt.Sprintf("%s%d", prefix, tableIdx))), + Defs: defs, } // Create some random column families. diff --git a/pkg/sql/resolver.go b/pkg/sql/resolver.go index 8e3e194db4a9..9dc0df193f76 100644 --- a/pkg/sql/resolver.go +++ b/pkg/sql/resolver.go @@ -1232,37 +1232,6 @@ func (l *internalLookupCtx) getSchemaName(table catalog.TableDescriptor) string return schemaName } -// getParentAsTableName returns a TreeTable object of the parent table for a -// given table ID. Used to get the parent table of a table with interleaved -// indexes. -func getParentAsTableName( - l simpleSchemaResolver, parentTableID descpb.ID, dbPrefix string, -) (tree.TableName, error) { - var parentName tree.TableName - parentTable, err := l.getTableByID(parentTableID) - if err != nil { - return tree.TableName{}, err - } - var parentSchemaName tree.Name - if parentTable.GetParentSchemaID() == keys.PublicSchemaID { - parentSchemaName = tree.PublicSchemaName - } else { - parentSchema, err := l.getSchemaByID(parentTable.GetParentSchemaID()) - if err != nil { - return tree.TableName{}, err - } - parentSchemaName = tree.Name(parentSchema.GetName()) - } - parentDbDesc, err := l.getDatabaseByID(parentTable.GetParentID()) - if err != nil { - return tree.TableName{}, err - } - parentName = tree.MakeTableNameWithSchema(tree.Name(parentDbDesc.GetName()), - parentSchemaName, tree.Name(parentTable.GetName())) - parentName.ExplicitCatalog = parentDbDesc.GetName() != dbPrefix - return parentName, nil -} - // getTableNameFromTableDescriptor returns a TableName object for a given // TableDescriptor. func getTableNameFromTableDescriptor( diff --git a/pkg/sql/revert.go b/pkg/sql/revert.go index a13c4ae913b8..97a8e701fdb0 100644 --- a/pkg/sql/revert.go +++ b/pkg/sql/revert.go @@ -56,8 +56,7 @@ func RevertTables( spans := make([]roachpb.Span, 0, len(tables)) - // Check that all the tables are revertable -- i.e. offline and that their - // full interleave hierarchy is being reverted. + // Check that all the tables are revertable -- i.e. offline. for i := range tables { if tables[i].GetState() != descpb.DescriptorState_OFFLINE { return errors.New("only offline tables can be reverted") @@ -66,20 +65,6 @@ func RevertTables( if !tables[i].IsPhysicalTable() { return errors.Errorf("cannot revert virtual table %s", tables[i].GetName()) } - for _, idx := range tables[i].NonDropIndexes() { - for j := 0; j < idx.NumInterleaveAncestors(); j++ { - parent := idx.GetInterleaveAncestor(j) - if !reverting[parent.TableID] { - return errors.New("cannot revert table without reverting all interleaved tables and indexes") - } - } - for j := 0; j < idx.NumInterleavedBy(); j++ { - child := idx.GetInterleavedBy(j) - if !reverting[child.Table] { - return errors.New("cannot revert table without reverting all interleaved tables and indexes") - } - } - } spans = append(spans, tables[i].TableSpan(execCfg.Codec)) } diff --git a/pkg/sql/row/deleter.go b/pkg/sql/row/deleter.go index cbe770340fcf..b6f6d6165a47 100644 --- a/pkg/sql/row/deleter.go +++ b/pkg/sql/row/deleter.go @@ -162,33 +162,3 @@ func (rd *Deleter) DeleteRow( return nil }) } - -// DeleteIndexRow adds to the batch the kv operations necessary to delete a -// table row from the given index. -func (rd *Deleter) DeleteIndexRow( - ctx context.Context, b *kv.Batch, idx catalog.Index, values []tree.Datum, traceKV bool, -) error { - // We want to include empty k/v pairs because we want - // to delete all k/v's for this row. By setting includeEmpty - // to true, we will get a k/v pair for each family in the row, - // which will guarantee that we delete all the k/v's in this row. - secondaryIndexEntry, err := rowenc.EncodeSecondaryIndex( - rd.Helper.Codec, - rd.Helper.TableDesc, - idx, - rd.FetchColIDtoRowIndex, - values, - true, /* includeEmpty */ - ) - if err != nil { - return err - } - - for _, entry := range secondaryIndexEntry { - if traceKV { - log.VEventf(ctx, 2, "Del %s", entry.Key) - } - b.Del(entry.Key) - } - return nil -} diff --git a/pkg/sql/row/fetcher.go b/pkg/sql/row/fetcher.go index c0e7fc2004a4..a8897eaa3b62 100644 --- a/pkg/sql/row/fetcher.go +++ b/pkg/sql/row/fetcher.go @@ -786,14 +786,16 @@ func (rf *Fetcher) prettyEncDatums(types []*types.T, vals []rowenc.EncDatum) str func (rf *Fetcher) ReadIndexKey( key roachpb.Key, ) (remaining []byte, ok bool, foundNull bool, err error) { - return rowenc.DecodeIndexKeyWithoutTableIDIndexIDPrefix( - rf.table.desc, - rf.table.index, + remaining, foundNull, err = rowenc.DecodeKeyVals( rf.table.keyValTypes, rf.table.keyVals, rf.table.indexColumnDirs, key[rf.table.knownPrefixLength:], ) + if err != nil { + return nil, false, false, err + } + return remaining, true, foundNull, nil } // KeyToDesc implements the KeyToDescTranslator interface. The implementation is diff --git a/pkg/sql/rowenc/index_encoding.go b/pkg/sql/rowenc/index_encoding.go index cbc2fbf8fd8a..196b17b672a3 100644 --- a/pkg/sql/rowenc/index_encoding.go +++ b/pkg/sql/rowenc/index_encoding.go @@ -43,10 +43,6 @@ import ( func MakeIndexKeyPrefix( codec keys.SQLCodec, desc catalog.TableDescriptor, indexID descpb.IndexID, ) []byte { - if i, err := desc.FindIndexWithID(indexID); err == nil && i.NumInterleaveAncestors() > 0 { - ancestor := i.GetInterleaveAncestor(0) - return codec.IndexPrefix(uint32(ancestor.TableID), uint32(ancestor.IndexID)) - } return codec.IndexPrefix(uint32(desc.GetID()), uint32(indexID)) } @@ -148,45 +144,11 @@ func EncodePartialIndexKey( // We know we will append to the key which will cause the capacity to grow so // make it bigger from the get-go. // Add the length of the key prefix as an initial guess. - // Add 3 bytes for every ancestor: table,index id + interleave sentinel. // Add 2 bytes for every column value. An underestimate for all but low integers. - key = growKey(keyPrefix, len(keyPrefix)+3*index.NumInterleaveAncestors()+2*len(values)) + key = growKey(keyPrefix, len(keyPrefix)+2*len(values)) dirs := directions(index.IndexDesc().KeyColumnDirections) - if index.NumInterleaveAncestors() > 0 { - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - // The first ancestor is assumed to already be encoded in keyPrefix. - if i != 0 { - key = EncodePartialTableIDIndexID(key, ancestor.TableID, ancestor.IndexID) - } - - partial := false - length := int(ancestor.SharedPrefixLen) - if length > len(colIDs) { - length = len(colIDs) - partial = true - } - key, colIDWithNullVal, err = EncodeColumns(colIDs[:length], dirs[:length], colMap, values, key) - if err != nil { - return nil, colIDWithNullVal, err - } - if partial { - // Early stop. Note that if we had exactly SharedPrefixLen columns - // remaining, we want to append the next tableID/indexID pair because - // that results in a more specific key. - return key, colIDWithNullVal, nil - } - colIDs, dirs = colIDs[length:], dirs[length:] - // Each ancestor is separated by an interleaved - // sentinel (0xfe). - key = encoding.EncodeInterleavedSentinel(key) - } - - key = EncodePartialTableIDIndexID(key, tableDesc.GetID(), index.GetID()) - } - var keyColIDWithNullVal, keySuffixColIDWithNullVal descpb.ColumnID key, keyColIDWithNullVal, err = EncodeColumns(colIDs, dirs, colMap, values, key) if colIDWithNullVal == 0 { @@ -462,43 +424,6 @@ func MakeKeyFromEncDatums( key := make(roachpb.Key, len(keyPrefix), len(keyPrefix)*2) copy(key, keyPrefix) - if index.NumInterleaveAncestors() > 0 { - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - // The first ancestor is assumed to already be encoded in keyPrefix. - if i != 0 { - key = EncodePartialTableIDIndexID(key, ancestor.TableID, ancestor.IndexID) - } - - partial := false - length := int(ancestor.SharedPrefixLen) - if length > len(types) { - length = len(types) - partial = true - } - var ( - err error - n bool - ) - key, n, err = appendEncDatumsToKey(key, types[:length], values[:length], dirs[:length], alloc) - if err != nil { - return nil, false, false, err - } - containsNull = containsNull || n - if partial { - // Early stop - the number of desired columns was fewer than the number - // left in the current interleave. - return key, false, false, nil - } - types, values, dirs = types[length:], values[length:], dirs[length:] - - // Each ancestor is separated by an interleaved - // sentinel (0xfe). - key = encoding.EncodeInterleavedSentinel(key) - } - - key = EncodePartialTableIDIndexID(key, tableDesc.GetID(), index.GetID()) - } var ( err error n bool @@ -550,12 +475,6 @@ func appendEncDatumsToKey( return key, containsNull, nil } -// EncodePartialTableIDIndexID encodes a table id followed by an index id to an -// existing key. The key must already contain a tenant id. -func EncodePartialTableIDIndexID(key []byte, tableID descpb.ID, indexID descpb.IndexID) []byte { - return keys.MakeTableIDIndexID(key, uint32(tableID), uint32(indexID)) -} - // DecodePartialTableIDIndexID decodes a table id followed by an index id. The // input key must already have its tenant id removed. func DecodePartialTableIDIndexID(key []byte) ([]byte, descpb.ID, descpb.IndexID, error) { @@ -574,58 +493,15 @@ func DecodeIndexKeyPrefix( if err != nil { return 0, nil, err } - - // TODO(dan): This whole operation is n^2 because of the interleaves - // bookkeeping. We could improve it to n with a prefix tree of components. - - interleaves := append(make([]catalog.Index, 0, len(desc.ActiveIndexes())), desc.ActiveIndexes()...) - - for component := 0; ; component++ { - var tableID descpb.ID - key, tableID, indexID, err = DecodePartialTableIDIndexID(key) - if err != nil { - return 0, nil, err - } - if tableID == desc.GetID() { - // Once desc's table id has been decoded, there can be no more - // interleaves. - break - } - - for i := len(interleaves) - 1; i >= 0; i-- { - if interleaves[i].NumInterleaveAncestors() <= component || - interleaves[i].GetInterleaveAncestor(component).TableID != tableID || - interleaves[i].GetInterleaveAncestor(component).IndexID != indexID { - - // This component, and thus this interleave, doesn't match what was - // decoded, remove it. - copy(interleaves[i:], interleaves[i+1:]) - interleaves = interleaves[:len(interleaves)-1] - } - } - // The decoded key doesn't many any known interleaves - if len(interleaves) == 0 { - return 0, nil, errors.Errorf("no known interleaves for key") - } - - // Anything left has the same SharedPrefixLen at index `component`, so just - // use the first one. - for i := uint32(0); i < interleaves[0].GetInterleaveAncestor(component).SharedPrefixLen; i++ { - l, err := encoding.PeekLength(key) - if err != nil { - return 0, nil, err - } - key = key[l:] - } - - // Consume the interleaved sentinel. - var ok bool - key, ok = encoding.DecodeIfInterleavedSentinel(key) - if !ok { - return 0, nil, errors.Errorf("invalid interleave key") - } + var tableID descpb.ID + key, tableID, indexID, err = DecodePartialTableIDIndexID(key) + if err != nil { + return 0, nil, err + } + if tableID != desc.GetID() { + return 0, nil, errors.Errorf( + "unexpected table ID %d, expected %d instead", tableID, desc.GetID()) } - return indexID, key, err } @@ -639,8 +515,6 @@ func DecodeIndexKeyPrefix( // no error. func DecodeIndexKey( codec keys.SQLCodec, - desc catalog.TableDescriptor, - index catalog.Index, types []*types.T, vals []EncDatum, colDirs []descpb.IndexDescriptor_Direction, @@ -654,80 +528,11 @@ func DecodeIndexKey( if err != nil { return nil, false, false, err } - return DecodeIndexKeyWithoutTableIDIndexIDPrefix(desc, index, types, vals, colDirs, key) -} - -// DecodeIndexKeyWithoutTableIDIndexIDPrefix is the same as DecodeIndexKey, -// except it expects its index key is missing in its tenant id and first table -// id / index id key prefix. -func DecodeIndexKeyWithoutTableIDIndexIDPrefix( - desc catalog.TableDescriptor, - index catalog.Index, - types []*types.T, - vals []EncDatum, - colDirs []descpb.IndexDescriptor_Direction, - key []byte, -) (remainingKey []byte, matches bool, foundNull bool, _ error) { - var decodedTableID descpb.ID - var decodedIndexID descpb.IndexID - var err error - - if index.NumInterleaveAncestors() > 0 { - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - // Our input key had its first table id / index id chopped off, so - // don't try to decode those for the first ancestor. - if i != 0 { - key, decodedTableID, decodedIndexID, err = DecodePartialTableIDIndexID(key) - if err != nil { - return nil, false, false, err - } - if decodedTableID != ancestor.TableID || decodedIndexID != ancestor.IndexID { - return nil, false, false, nil - } - } - - length := int(ancestor.SharedPrefixLen) - var isNull bool - key, isNull, err = DecodeKeyVals(types[:length], vals[:length], colDirs[:length], key) - if err != nil { - return nil, false, false, err - } - types, vals, colDirs = types[length:], vals[length:], colDirs[length:] - foundNull = foundNull || isNull - - // Consume the interleaved sentinel. - var ok bool - key, ok = encoding.DecodeIfInterleavedSentinel(key) - if !ok { - return nil, false, false, nil - } - } - - key, decodedTableID, decodedIndexID, err = DecodePartialTableIDIndexID(key) - if err != nil { - return nil, false, false, err - } - if decodedTableID != desc.GetID() || decodedIndexID != index.GetID() { - return nil, false, false, nil - } - } - - var isNull bool - key, isNull, err = DecodeKeyVals(types, vals, colDirs, key) + remainingKey, foundNull, err = DecodeKeyVals(types, vals, colDirs, key) if err != nil { return nil, false, false, err } - foundNull = foundNull || isNull - - // We're expecting a column family id next (a varint). If - // interleavedSentinel is actually next, then this key is for a child - // table. - if _, ok := encoding.DecodeIfInterleavedSentinel(key); ok { - return nil, false, false, nil - } - - return key, true, foundNull, nil + return remainingKey, true, foundNull, nil } // DecodeKeyVals decodes the values that are part of the key. The decoded @@ -1528,177 +1333,14 @@ func EncodeSecondaryIndexes( return secondaryIndexEntries, memUsedEncodingSecondaryIdxs, nil } -// IndexKeyEquivSignature parses an index key if and only if the index key -// belongs to a table where its equivalence signature and all its interleave -// ancestors' signatures can be found in validEquivSignatures. Any tenant ID -// prefix should be removed before calling this function. -// -// Its validEquivSignatures argument is a map containing equivalence signatures -// of valid ancestors of the desired table and of the desired table itself. -// -// IndexKeyEquivSignature returns whether or not the index key satisfies the -// above condition, the value mapped to by the desired table (could be a table -// index), and the rest of the key that's not part of the signature. -// -// It also requires two []byte buffers: one for the signature (signatureBuf) and -// one for the rest of the key (keyRestBuf). -// -// The equivalence signature defines the equivalence classes for the signature -// of potentially interleaved tables. For example, the equivalence signatures -// for the following interleaved indexes: -// -// -// -// -// and index keys -// : //// -// : /////#//child index id>// -// -// correspond to the equivalence signatures -// : // -// : ///#// -// -// Equivalence signatures allow us to associate an index key with its table -// without having to invoke DecodeIndexKey multiple times. -// -// IndexKeyEquivSignature will return false if the a table's ancestor'ssignature -// or the table's signature (table which the index key belongs to) is not mapped -// in validEquivSignatures. -// -// For example, suppose the given key is -// -// ////#/// -// -// and validEquivSignatures contains -// -// //t1 index id> -// //t1 index id>/#// -// -// IndexKeyEquivSignature will short-circuit and return false once -// -// // -// -// is processed since t2's signature is not specified in validEquivSignatures. -func IndexKeyEquivSignature( - key []byte, validEquivSignatures map[string]int, signatureBuf []byte, restBuf []byte, -) (tableIdx int, restResult []byte, success bool, err error) { - signatureBuf = signatureBuf[:0] - restResult = restBuf[:0] - for { - // Well-formed key is guaranteed to to have 2 varints for every - // ancestor: the TableID and descpb.IndexID. - // We extract these out and add them to our buffer. - for i := 0; i < 2; i++ { - idLen, err := encoding.PeekLength(key) - if err != nil { - return 0, nil, false, err - } - signatureBuf = append(signatureBuf, key[:idLen]...) - key = key[idLen:] - } - - // The current signature (either an ancestor table's or the key's) - // is not one of the validEquivSignatures. - // We can short-circuit and return false. - recentTableIdx, found := validEquivSignatures[string(signatureBuf)] - if !found { - return 0, nil, false, nil - } - - var isSentinel bool - // Peek and discard encoded index values. - for { - key, isSentinel = encoding.DecodeIfInterleavedSentinel(key) - // We stop once the key is empty or if we encounter a - // sentinel for the next TableID-descpb.IndexID pair. - if len(key) == 0 || isSentinel { - break - } - len, err := encoding.PeekLength(key) - if err != nil { - return 0, nil, false, err - } - // Append any other bytes (column values initially, - // then family ID and timestamp) to return. - restResult = append(restResult, key[:len]...) - key = key[len:] - } - - if !isSentinel { - // The key has been fully decomposed and is valid up to - // this point. - // Return the most recent table index from - // validEquivSignatures. - return recentTableIdx, restResult, true, nil - } - // If there was a sentinel, we know there are more - // descendant(s). - // We insert an interleave sentinel and continue extracting the - // next descendant's IDs. - signatureBuf = encoding.EncodeInterleavedSentinel(signatureBuf) - } -} - -// TableEquivSignatures returns the equivalence signatures for each interleave -// ancestor and itself. See IndexKeyEquivSignature for more info. -func TableEquivSignatures( - desc catalog.TableDescriptor, index catalog.Index, -) (signatures [][]byte, err error) { - // signatures contains the slice reference to the signature of every - // ancestor of the current table-index. - // The last slice reference is the given table-index's signature. - signatures = make([][]byte, index.NumInterleaveAncestors()+1) - // fullSignature is the backing byte slice for each individual signature - // as it buffers each block of table and index IDs. - // We eagerly allocate 4 bytes for each of the two IDs per ancestor - // (which can fit Uvarint IDs up to 2^17-1 without another allocation), - // 1 byte for each interleave sentinel, and 4 bytes each for the given - // table's and index's ID. - fullSignature := make([]byte, 0, index.NumInterleaveAncestors()*9+8) - - // Encode the table's ancestors' TableIDs and descpb.IndexIDs. - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - fullSignature = EncodePartialTableIDIndexID(fullSignature, ancestor.TableID, ancestor.IndexID) - // Create a reference up to this point for the ancestor's - // signature. - signatures[i] = fullSignature - // Append Interleave sentinel after every ancestor. - fullSignature = encoding.EncodeInterleavedSentinel(fullSignature) - } - - // Encode the table's table and index IDs. - fullSignature = EncodePartialTableIDIndexID(fullSignature, desc.GetID(), index.GetID()) - // Create a reference for the given table's signature as the last - // element of signatures. - signatures[len(signatures)-1] = fullSignature - - return signatures, nil -} - // maxKeyTokens returns the maximum number of key tokens in an index's key, // including the table ID, index ID, and index column values (including extra // columns that may be stored in the key). // It requires knowledge of whether the key will or might contain a NULL value: // if uncertain, pass in true to 'overestimate' the maxKeyTokens. // -// In general, a key belonging to an interleaved index grandchild is encoded as: -// -// /table/index//...//#/table/index//...//#/table/index//.../ -// -// The part of the key with respect to the grandchild index would be -// the entire key since there are no grand-grandchild table/index IDs or -// . The maximal prefix of the key that belongs to child is -// -// /table/index//...//#/table/index//.../ -// -// and the maximal prefix of the key that belongs to parent is -// -// /table/index//.../ -// // This returns the maximum number of in this prefix. func maxKeyTokens(index catalog.Index, containsNull bool) int { - nTables := index.NumInterleaveAncestors() + 1 nKeyCols := index.NumKeyColumns() // Non-unique secondary indexes or unique secondary indexes with a NULL @@ -1709,20 +1351,7 @@ func maxKeyTokens(index catalog.Index, containsNull bool) int { nKeyCols += index.NumKeySuffixColumns() } - // To illustrate how we compute max # of key tokens, take the - // key in the example above and let the respective index be child. - // We'd like to return the number of bytes in - // - // /table/index//...//#/table/index//.../ - // For each table-index, there is - // 1. table ID - // 2. index ID - // 3. interleave sentinel - // or 3 * nTables. - // Each must be a part of the index's columns (nKeys). - // Finally, we do not want to include the interleave sentinel for the - // current index (-1). - return 3*nTables + nKeyCols - 1 + return 2 + nKeyCols } // AdjustEndKeyForInterleave returns an exclusive end key. It does two things: diff --git a/pkg/sql/rowenc/index_encoding_test.go b/pkg/sql/rowenc/index_encoding_test.go index 3872f7dadfeb..c777d72c3cb5 100644 --- a/pkg/sql/rowenc/index_encoding_test.go +++ b/pkg/sql/rowenc/index_encoding_test.go @@ -33,7 +33,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/testutils" - "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/cockroach/pkg/util/json" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/randutil" @@ -119,7 +118,7 @@ func decodeIndex( } values := make([]EncDatum, index.NumKeyColumns()) colDirs := index.IndexDesc().KeyColumnDirections - _, ok, _, err := DecodeIndexKey(codec, tableDesc, index, types, values, colDirs, key) + _, ok, _, err := DecodeIndexKey(codec, types, values, colDirs, key) if err != nil { return nil, err } @@ -950,320 +949,6 @@ func TestMarshalColumnValue(t *testing.T) { } } -type interleaveTableArgs struct { - indexKeyArgs indexKeyTest - values []tree.Datum -} - -type interleaveInfo struct { - tableID uint64 - values []tree.Datum - equivSig []byte - children map[string]*interleaveInfo -} - -func createHierarchy() map[string]*interleaveInfo { - return map[string]*interleaveInfo{ - "t1": { - tableID: 50, - values: []tree.Datum{tree.NewDInt(10)}, - children: map[string]*interleaveInfo{ - "t2": { - tableID: 100, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(15)}, - }, - "t3": { - tableID: 150, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(20)}, - children: map[string]*interleaveInfo{ - "t4": { - tableID: 20, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(30)}, - }, - }, - }, - }, - }, - } -} - -type equivSigTestCases struct { - name string - table interleaveTableArgs - expected [][]byte -} - -func createEquivTCs(hierarchy map[string]*interleaveInfo) []equivSigTestCases { - return []equivSigTestCases{ - { - name: "NoAncestors", - table: interleaveTableArgs{ - indexKeyArgs: indexKeyTest{tableID: 50}, - values: []tree.Datum{tree.NewDInt(10)}, - }, - expected: [][]byte{hierarchy["t1"].equivSig}, - }, - - { - name: "OneAncestor", - table: interleaveTableArgs{ - indexKeyArgs: indexKeyTest{tableID: 100, primaryInterleaves: []descpb.ID{50}}, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(20)}, - }, - expected: [][]byte{hierarchy["t1"].equivSig, hierarchy["t1"].children["t2"].equivSig}, - }, - - { - name: "TwoAncestors", - table: interleaveTableArgs{ - indexKeyArgs: indexKeyTest{tableID: 20, primaryInterleaves: []descpb.ID{50, 150}}, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(20), tree.NewDInt(30)}, - }, - expected: [][]byte{hierarchy["t1"].equivSig, hierarchy["t1"].children["t3"].equivSig, hierarchy["t1"].children["t3"].children["t4"].equivSig}, - }, - } -} - -// equivSignatures annotates the hierarchy with the equivalence signatures -// for each table and returns an in-order depth-first traversal of the -// equivalence signatures. -func equivSignatures( - hierarchy map[string]*interleaveInfo, parent []byte, signatures [][]byte, -) [][]byte { - for _, info := range hierarchy { - // Reset the reference to the parent for every child. - curParent := parent - curParent = encoding.EncodeUvarintAscending(curParent, info.tableID) - // Primary ID is always 1 - curParent = encoding.EncodeUvarintAscending(curParent, 1) - info.equivSig = make([]byte, len(curParent)) - copy(info.equivSig, curParent) - signatures = append(signatures, info.equivSig) - if len(info.children) > 0 { - curParent = encoding.EncodeInterleavedSentinel(curParent) - signatures = equivSignatures(info.children, curParent, signatures) - } - } - return signatures -} - -func TestIndexKeyEquivSignature(t *testing.T) { - hierarchy := createHierarchy() - hierarchySigs := equivSignatures(hierarchy, nil /*parent*/, nil /*signatures*/) - // validEquivSigs is necessary for IndexKeyEquivSignatures. - validEquivSigs := make(map[string]int) - for i, sig := range hierarchySigs { - validEquivSigs[string(sig)] = i - } - - // Required buffers when extracting the index key's equivalence signature. - var keySigBuf, keyRestBuf []byte - - for _, tc := range createEquivTCs(hierarchy) { - t.Run(tc.name, func(t *testing.T) { - // We need to initialize this for makeTableDescForTest. - tc.table.indexKeyArgs.primaryValues = tc.table.values - // Setup descriptors and form an index key. - desc, colMap := makeTableDescForTest(tc.table.indexKeyArgs) - primaryKeyPrefix := MakeIndexKeyPrefix(keys.SystemSQLCodec, desc, desc.GetPrimaryIndexID()) - primaryKey, _, err := EncodeIndexKey( - desc, desc.GetPrimaryIndex(), colMap, tc.table.values, primaryKeyPrefix) - if err != nil { - t.Fatal(err) - } - - tableIdx, restKey, match, err := IndexKeyEquivSignature(primaryKey, validEquivSigs, keySigBuf, keyRestBuf) - if err != nil { - t.Fatal(err) - } - if !match { - t.Fatalf("expected to extract equivalence signature from index key, instead false returned") - } - - tableSig := tc.expected[len(tc.expected)-1] - expectedTableIdx := validEquivSigs[string(tableSig)] - if expectedTableIdx != tableIdx { - t.Fatalf("table index returned does not match table index from validEquivSigs.\nexpected %d\nactual %d", expectedTableIdx, tableIdx) - } - - // Column values should be at the beginning of the - // remaining bytes of the key. - pkIndexDesc := desc.GetPrimaryIndex().IndexDesc() - colVals, nullColID, err := EncodeColumns( - pkIndexDesc.KeyColumnIDs, - pkIndexDesc.KeyColumnDirections, - colMap, - tc.table.values, - nil, /* keyPrefix */ - ) - if err != nil { - t.Fatal(err) - } - if nullColID != 0 { - t.Fatalf("unexpected null values when encoding expected column values") - } - - if !bytes.Equal(colVals, restKey[:len(colVals)]) { - t.Fatalf("missing column values from rest of key.\nexpected %v\nactual %v", colVals, restKey[:len(colVals)]) - } - - // The remaining bytes of the key should be the same - // length as the primary key minus the equivalence - // signature bytes. - if len(primaryKey)-len(tableSig) != len(restKey) { - t.Fatalf("unexpected rest of key length, expected %d, actual %d", len(primaryKey)-len(tableSig), len(restKey)) - } - }) - } -} - -// TestTableEquivSignatures verifies that TableEquivSignatures returns a slice -// of slice references to a table's interleave ancestors' equivalence -// signatures. -func TestTableEquivSignatures(t *testing.T) { - hierarchy := createHierarchy() - equivSignatures(hierarchy, nil /*parent*/, nil /*signatures*/) - - for _, tc := range createEquivTCs(hierarchy) { - t.Run(tc.name, func(t *testing.T) { - // We need to initialize this for makeTableDescForTest. - tc.table.indexKeyArgs.primaryValues = tc.table.values - // Setup descriptors and form an index key. - desc, _ := makeTableDescForTest(tc.table.indexKeyArgs) - equivSigs, err := TableEquivSignatures(desc, desc.GetPrimaryIndex()) - if err != nil { - t.Fatal(err) - } - - if len(equivSigs) != len(tc.expected) { - t.Fatalf("expected %d equivalence signatures from TableEquivSignatures, actual %d", len(tc.expected), len(equivSigs)) - } - for i, sig := range equivSigs { - if !bytes.Equal(sig, tc.expected[i]) { - t.Fatalf("equivalence signatures at index %d do not match.\nexpected\t%v\nactual\t%v", i, tc.expected[i], sig) - } - } - }) - } -} - -// TestEquivSignature verifies that invoking IndexKeyEquivSignature for an encoded index key -// for a given table-index pair returns the equivalent equivalence signature as -// that of the table-index from invoking TableEquivSignatures. -// It also checks that the equivalence signature is not equivalent to any other -// tables' equivalence signatures. -func TestEquivSignature(t *testing.T) { - for _, tc := range []struct { - name string - tables []interleaveTableArgs - }{ - { - name: "Simple", - tables: []interleaveTableArgs{ - { - indexKeyArgs: indexKeyTest{tableID: 50}, - values: []tree.Datum{tree.NewDInt(10)}, - }, - { - indexKeyArgs: indexKeyTest{tableID: 51}, - values: []tree.Datum{tree.NewDInt(20)}, - }, - }, - }, - - { - name: "ParentAndChild", - tables: []interleaveTableArgs{ - { - indexKeyArgs: indexKeyTest{tableID: 50}, - values: []tree.Datum{tree.NewDInt(10)}, - }, - { - indexKeyArgs: indexKeyTest{tableID: 51, primaryInterleaves: []descpb.ID{50}}, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(20)}, - }, - }, - }, - - { - name: "Siblings", - tables: []interleaveTableArgs{ - { - indexKeyArgs: indexKeyTest{tableID: 50}, - values: []tree.Datum{tree.NewDInt(10)}, - }, - { - indexKeyArgs: indexKeyTest{tableID: 51, primaryInterleaves: []descpb.ID{50}}, - values: []tree.Datum{tree.NewDInt(10), tree.NewDInt(20)}, - }, - { - indexKeyArgs: indexKeyTest{tableID: 52, primaryInterleaves: []descpb.ID{50}}, - values: []tree.Datum{tree.NewDInt(30), tree.NewDInt(40)}, - }, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - keyEquivSigs := make([][]byte, len(tc.tables)) - tableEquivSigs := make([][]byte, len(tc.tables)) - - for i, table := range tc.tables { - // We need to initialize this for makeTableDescForTest. - table.indexKeyArgs.primaryValues = table.values - - // Setup descriptors and form an index key. - desc, colMap := makeTableDescForTest(table.indexKeyArgs) - primaryKeyPrefix := MakeIndexKeyPrefix(keys.SystemSQLCodec, desc, desc.GetPrimaryIndexID()) - primaryKey, _, err := EncodeIndexKey( - desc, desc.GetPrimaryIndex(), colMap, table.values, primaryKeyPrefix) - if err != nil { - t.Fatal(err) - } - - // Extract out the table's equivalence signature. - tempEquivSigs, err := TableEquivSignatures(desc, desc.GetPrimaryIndex()) - if err != nil { - t.Fatal(err) - } - // The last signature is this table's. - tableEquivSigs[i] = tempEquivSigs[len(tempEquivSigs)-1] - - validEquivSigs := make(map[string]int) - for i, sig := range tempEquivSigs { - validEquivSigs[string(sig)] = i - } - // Extract out the corresponding table index - // of the index key's signature. - tableIdx, _, _, err := IndexKeyEquivSignature(primaryKey, validEquivSigs, nil /*keySigBuf*/, nil /*keyRestBuf*/) - if err != nil { - t.Fatal(err) - } - // Map the table index back to the signature. - keyEquivSigs[i] = tempEquivSigs[tableIdx] - } - - for i, keySig := range keyEquivSigs { - for j, tableSig := range tableEquivSigs { - if i == j { - // The corresponding table should have the same - // equivalence signature as the one derived from the key. - if !bytes.Equal(keySig, tableSig) { - t.Fatalf("IndexKeyEquivSignature differs from equivalence signature for its table.\nKeySignature: %v\nTableSignature: %v", keySig, tableSig) - } - } else { - // A different table should not have - // the same equivalence signature. - if bytes.Equal(keySig, tableSig) { - t.Fatalf("IndexKeyEquivSignature produces equivalent signature for a different table.\nKeySignature: %v\nTableSignature: %v", keySig, tableSig) - } - } - } - } - - }) - } -} - func TestDecodeTableValue(t *testing.T) { a := &DatumAlloc{} for _, tc := range []struct { @@ -1299,10 +984,6 @@ func TestDecodeTableValue(t *testing.T) { } } -// See CreateTestInterleavedHierarchy for the longest chain used for the short -// format. -var shortFormTables = [3]string{"parent1", "child1", "grandchild1"} - // ExtractIndexKey constructs the index (primary) key for a row from any index // key/value entry, including secondary indexes. // @@ -1330,23 +1011,9 @@ func ExtractIndexKey( } values := make([]EncDatum, index.NumKeyColumns()) dirs := index.IndexDesc().KeyColumnDirections - if index.NumInterleaveAncestors() > 0 { - // TODO(dan): In the interleaved index case, we parse the key twice; once to - // find the index id so we can look up the descriptor, and once to extract - // the values. Only parse once. - var ok bool - _, ok, _, err = DecodeIndexKey(codec, tableDesc, index, indexTypes, values, dirs, entry.Key) - if err != nil { - return nil, err - } - if !ok { - return nil, errors.Errorf("descriptor did not match key") - } - } else { - key, _, err = DecodeKeyVals(indexTypes, values, dirs, key) - if err != nil { - return nil, err - } + key, _, err = DecodeKeyVals(indexTypes, values, dirs, key) + if err != nil { + return nil, err } // Extract the values for index.KeySuffixColumnIDs diff --git a/pkg/sql/schema_changer.go b/pkg/sql/schema_changer.go index 49169c4e6b64..bd46f85ae355 100644 --- a/pkg/sql/schema_changer.go +++ b/pkg/sql/schema_changer.go @@ -1082,25 +1082,24 @@ func (sc *SchemaChanger) done(ctx context.Context) error { } isRollback = m.IsRollback() if idx := m.AsIndex(); m.Dropped() && idx != nil { - if canClearRangeForDrop(idx) { - // how we keep track of dropped index names (for, e.g., zone config - // lookups), even though in the absence of a GC job there's nothing to - // clean them up. - scTable.GCMutations = append( - scTable.GCMutations, - descpb.TableDescriptor_GCDescriptorMutation{ - IndexID: idx.GetID(), - }) + // how we keep track of dropped index names (for, e.g., zone config + // lookups), even though in the absence of a GC job there's nothing to + // clean them up. + scTable.GCMutations = append( + scTable.GCMutations, + descpb.TableDescriptor_GCDescriptorMutation{ + IndexID: idx.GetID(), + }) - description := sc.job.Payload().Description - if isRollback { - description = "ROLLBACK of " + description - } + description := sc.job.Payload().Description + if isRollback { + description = "ROLLBACK of " + description + } - if err := sc.createIndexGCJob(ctx, idx.GetID(), txn, description); err != nil { - return err - } + if err := sc.createIndexGCJob(ctx, idx.GetID(), txn, description); err != nil { + return err } + } if constraint := m.AsConstraint(); constraint != nil && constraint.Adding() { if constraint.IsForeignKey() && constraint.ForeignKey().Validity == descpb.ConstraintValidity_Unvalidated { @@ -1221,14 +1220,6 @@ func (sc *SchemaChanger) done(ctx context.Context) error { } } - if err := maybeRemoveInterleaveBackreference(ctx, txn, descsCol, m, scTable, func( - ctx context.Context, ancestor *tabledesc.Mutable, - ) error { - return descsCol.WriteDescToBatch(ctx, kvTrace, ancestor, b) - }); err != nil { - return err - } - // If we performed MakeMutationComplete on a PrimaryKeySwap mutation, then we need to start // a job for the index deletion mutations that the primary key swap mutation added, if any. if err := sc.queueCleanupJobs(ctx, scTable, txn); err != nil { @@ -1354,61 +1345,6 @@ func (sc *SchemaChanger) done(ctx context.Context) error { return nil } -// If this mutation is a primary key swap that is not being rolled back, -// and the old index had an interleaved parent, remove the backreference -// from the parent. -func maybeRemoveInterleaveBackreference( - ctx context.Context, - txn *kv.Txn, - descsCol *descs.Collection, - mutation catalog.Mutation, - scTable *tabledesc.Mutable, - writeFunc func(ctx context.Context, ancestor *tabledesc.Mutable) error, -) error { - if !mutation.Adding() { - return nil - } - pkSwap := mutation.AsPrimaryKeySwap() - if pkSwap == nil { - return nil - } - return pkSwap.ForEachOldIndexIDs(func(id descpb.IndexID) error { - oldIndex, err := scTable.FindIndexWithID(id) - if err != nil { - return err - } - if oldIndex.NumInterleaveAncestors() != 0 { - ancestorInfo := oldIndex.GetInterleaveAncestor(oldIndex.NumInterleaveAncestors() - 1) - ancestor, err := descsCol.GetMutableTableVersionByID(ctx, ancestorInfo.TableID, txn) - if err != nil { - return err - } - ancestorIdxI, err := ancestor.FindIndexWithID(ancestorInfo.IndexID) - if err != nil { - return err - } - ancestorIdx := ancestorIdxI.IndexDesc() - foundAncestor := false - for k, ref := range ancestorIdx.InterleavedBy { - if ref.Table == scTable.ID && ref.Index == oldIndex.GetID() { - if foundAncestor { - return errors.AssertionFailedf( - "ancestor entry in %s for %s@%s found more than once", - ancestor.Name, scTable.Name, oldIndex.GetName()) - } - ancestorIdx.InterleavedBy = append( - ancestorIdx.InterleavedBy[:k], ancestorIdx.InterleavedBy[k+1:]...) - foundAncestor = true - if err := writeFunc(ctx, ancestor); err != nil { - return err - } - } - } - } - return nil - }) -} - // maybeUpdateZoneConfigsForPKChange moves zone configs for any rewritten // indexes from the old index over to the new index. Noop if run on behalf of a // tenant. diff --git a/pkg/sql/schemachanger/scbuild/table.go b/pkg/sql/schemachanger/scbuild/table.go index bc2c31a99878..48942e3d6c4a 100644 --- a/pkg/sql/schemachanger/scbuild/table.go +++ b/pkg/sql/schemachanger/scbuild/table.go @@ -683,16 +683,6 @@ func (b *buildContext) maybeCleanTableFKs( func (b *buildContext) dropTableDesc( ctx context.Context, table catalog.TableDescriptor, behavior tree.DropBehavior, ) { - // Interleaved tables not supported in new schema changer. - if table.IsInterleaved() { - panic(¬ImplementedError{ - n: &tree.DropTable{ - Names: []tree.TableName{ - tree.MakeUnqualifiedTableName(tree.Name(table.GetName())), - }, - }, - detail: "drop on interleaved table"}) - } // Drop dependent views onErrPanic(table.ForeachDependedOnBy(func(dep *descpb.TableDescriptor_Reference) error { diff --git a/pkg/sql/sem/tree/backup.go b/pkg/sql/sem/tree/backup.go index ba6c4ed8f40f..6ec8925fa606 100644 --- a/pkg/sql/sem/tree/backup.go +++ b/pkg/sql/sem/tree/backup.go @@ -36,11 +36,10 @@ const ( // BackupOptions describes options for the BACKUP execution. type BackupOptions struct { - CaptureRevisionHistory bool - EncryptionPassphrase Expr - Detached bool - EncryptionKMSURI StringOrPlaceholderOptList - IncludeDeprecatedInterleaves bool + CaptureRevisionHistory bool + EncryptionPassphrase Expr + Detached bool + EncryptionKMSURI StringOrPlaceholderOptList } var _ NodeFormatter = &BackupOptions{} @@ -238,11 +237,6 @@ func (o *BackupOptions) Format(ctx *FmtCtx) { ctx.WriteString("kms = ") ctx.FormatNode(&o.EncryptionKMSURI) } - - if o.IncludeDeprecatedInterleaves { - maybeAddSep() - ctx.WriteString("include_deprecated_interleaves") - } } // CombineWith merges other backup options into this backup options struct. @@ -276,14 +270,6 @@ func (o *BackupOptions) CombineWith(other *BackupOptions) error { return errors.New("kms specified multiple times") } - if o.IncludeDeprecatedInterleaves { - if other.IncludeDeprecatedInterleaves { - return errors.New("include_deprecated_interleaves option specified multiple times") - } - } else { - o.IncludeDeprecatedInterleaves = other.IncludeDeprecatedInterleaves - } - return nil } diff --git a/pkg/sql/sessiondatapb/local_only_session_data.pb.go b/pkg/sql/sessiondatapb/local_only_session_data.pb.go index 7fc6b2e79ef1..7db16611bd8a 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.pb.go +++ b/pkg/sql/sessiondatapb/local_only_session_data.pb.go @@ -145,11 +145,6 @@ type LocalOnlySessionData struct { // experimentalComputedColumnRewrites cluster setting for a description of the // format. ExperimentalComputedColumnRewrites string `protobuf:"bytes,35,opt,name=experimental_computed_column_rewrites,json=experimentalComputedColumnRewrites,proto3" json:"experimental_computed_column_rewrites,omitempty"` - // CopyPartitioningWhenDeinterleavingTable indicates that when running an - // ALTER PRIMARY KEY that retains the same columns but removes any - // interleaving that zone configurations and partitioning from the root - // of that interleave should be applied to the new primary index. - CopyPartitioningWhenDeinterleavingTable bool `protobuf:"varint,36,opt,name=copy_partitioning_when_deinterleaving_table,json=copyPartitioningWhenDeinterleavingTable,proto3" json:"copy_partitioning_when_deinterleaving_table,omitempty"` // EnableStreamReplication indicates whether to allow setting up a replication // stream. EnableStreamReplication bool `protobuf:"varint,37,opt,name=enable_stream_replication,json=enableStreamReplication,proto3" json:"enable_stream_replication,omitempty"` @@ -310,131 +305,129 @@ func init() { } var fileDescriptor_21ead158cf36da28 = []byte{ - // 1981 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x5f, 0x53, 0x1c, 0xc7, - 0x11, 0xd7, 0x59, 0xb6, 0x23, 0x06, 0x81, 0x60, 0x8d, 0xcc, 0x02, 0x82, 0x03, 0xfd, 0xb1, 0x90, - 0x65, 0x83, 0x2d, 0x3b, 0x8e, 0xec, 0x54, 0x12, 0x9b, 0x03, 0xac, 0x3f, 0xd8, 0xa0, 0x3d, 0x64, - 0x55, 0x9c, 0x54, 0x4d, 0x0d, 0xbb, 0x7d, 0x77, 0x63, 0x76, 0x67, 0xf6, 0x66, 0x66, 0x81, 0xe3, - 0x21, 0x9f, 0x21, 0x55, 0x79, 0xce, 0xf7, 0xf1, 0xa3, 0x1f, 0xfd, 0x44, 0x25, 0xe8, 0x43, 0xa4, - 0x4a, 0x4f, 0xa9, 0xee, 0xd9, 0xbd, 0x3b, 0x04, 0x76, 0xaa, 0xf2, 0x76, 0xd7, 0xbf, 0x5f, 0xff, - 0xa6, 0xa7, 0xa7, 0xbb, 0x67, 0x96, 0xad, 0xda, 0x6e, 0xba, 0x6a, 0xc1, 0x5a, 0xa9, 0x55, 0x22, - 0x9c, 0xc8, 0xf7, 0x56, 0x53, 0x1d, 0x8b, 0x94, 0x6b, 0x95, 0xf6, 0x78, 0x09, 0x70, 0x44, 0x56, - 0x72, 0xa3, 0x9d, 0x0e, 0xe6, 0x62, 0x1d, 0xef, 0x1b, 0x2d, 0xe2, 0xce, 0x8a, 0xed, 0xa6, 0x2b, - 0x67, 0x5c, 0x67, 0xa7, 0xda, 0xba, 0xad, 0x89, 0xb7, 0x8a, 0xbf, 0xbc, 0xcb, 0xcd, 0xff, 0xcc, - 0xb3, 0xa9, 0x2d, 0x14, 0xdd, 0x56, 0x69, 0xaf, 0xe9, 0x1d, 0xd6, 0x85, 0x13, 0xc1, 0x07, 0x2c, - 0xb0, 0xe2, 0x00, 0xb8, 0x13, 0x7b, 0x29, 0x58, 0x9e, 0x1b, 0x68, 0xc9, 0xa3, 0xb0, 0xb6, 0x58, - 0x5b, 0x1e, 0x89, 0x26, 0x10, 0xd9, 0x25, 0x60, 0x87, 0xec, 0xc1, 0x5f, 0xd8, 0x9c, 0xce, 0x9d, - 0xcc, 0xe4, 0x31, 0x18, 0xde, 0xda, 0xe7, 0xb1, 0xb0, 0xb1, 0x48, 0xc0, 0xf2, 0x54, 0x66, 0xd2, - 0x85, 0x6f, 0x2c, 0xd6, 0x96, 0x2f, 0xaf, 0xdd, 0x38, 0x3d, 0xa9, 0x87, 0xdb, 0x15, 0x6d, 0xf3, - 0x69, 0xa3, 0x24, 0x6d, 0x21, 0x27, 0x0a, 0xfb, 0x02, 0x9b, 0xfb, 0x67, 0x90, 0xe0, 0x53, 0x76, - 0xd5, 0xba, 0xcc, 0x71, 0x27, 0x33, 0xd0, 0x85, 0x0b, 0x2f, 0x93, 0xda, 0xe4, 0xab, 0x93, 0xfa, - 0x18, 0x9a, 0x56, 0xd6, 0x0b, 0x23, 0x9c, 0xd4, 0x2a, 0x1a, 0x45, 0xda, 0xae, 0x67, 0x05, 0x8f, - 0xd8, 0xb4, 0x4c, 0x52, 0xe0, 0x52, 0xf5, 0x53, 0x55, 0x09, 0xbc, 0xf9, 0x4b, 0x02, 0x53, 0xe8, - 0xf1, 0x58, 0x95, 0x79, 0xa8, 0x94, 0x38, 0xbb, 0x55, 0x29, 0x39, 0x23, 0x94, 0x15, 0x31, 0x92, - 0xcf, 0xa9, 0xbe, 0xf5, 0x4b, 0xaa, 0x75, 0xaf, 0xba, 0x3b, 0xf0, 0x7d, 0x6d, 0x81, 0xcf, 0xd8, - 0xb4, 0xd2, 0x4e, 0xc6, 0xc0, 0x13, 0x69, 0xf3, 0x54, 0xe0, 0xe1, 0x1e, 0x80, 0x91, 0xae, 0x17, - 0xbe, 0xbd, 0x58, 0x5b, 0x1e, 0x8b, 0xae, 0x7b, 0x78, 0xdd, 0xa3, 0xcd, 0x12, 0x0c, 0x56, 0xd8, - 0x3b, 0x06, 0xb4, 0x49, 0xc0, 0xf0, 0x1f, 0xb4, 0x54, 0x55, 0xb6, 0x7f, 0x83, 0x81, 0x44, 0x93, - 0x25, 0xf4, 0x04, 0x11, 0x9f, 0xc8, 0x8f, 0xd8, 0x54, 0x02, 0x2d, 0x51, 0xa4, 0x8e, 0xbb, 0x23, - 0xc5, 0x73, 0x23, 0x35, 0x2d, 0x72, 0x85, 0x1c, 0x82, 0x12, 0xdb, 0x3d, 0x52, 0x3b, 0x25, 0x12, - 0x7c, 0xcc, 0xae, 0x0f, 0x7b, 0x18, 0x10, 0x09, 0x55, 0x5f, 0x38, 0xb2, 0x58, 0x5b, 0xbe, 0x32, - 0xec, 0x12, 0x81, 0x48, 0xb0, 0x86, 0x82, 0x35, 0xb6, 0x30, 0xec, 0x52, 0x58, 0xe0, 0x2d, 0x9d, - 0xa6, 0xfa, 0x10, 0x0c, 0xf9, 0xdb, 0x90, 0x91, 0xef, 0xec, 0xc0, 0xf7, 0xb9, 0x85, 0xcd, 0x92, - 0x82, 0x32, 0x36, 0xd8, 0x66, 0xb7, 0x73, 0x61, 0x9c, 0x14, 0x69, 0xda, 0xc3, 0x9c, 0x38, 0x23, - 0xf7, 0x0a, 0x07, 0x09, 0xcf, 0x53, 0xa1, 0x2c, 0x5a, 0xb0, 0xf8, 0x92, 0x70, 0x94, 0x94, 0x96, - 0xfa, 0xdc, 0xf5, 0x01, 0x75, 0x07, 0x99, 0xeb, 0x25, 0x31, 0x78, 0xc8, 0x06, 0xe5, 0x45, 0x21, - 0x75, 0xa4, 0x75, 0xba, 0x6d, 0x44, 0x66, 0xc3, 0xab, 0x24, 0xf2, 0x6e, 0x1f, 0x7f, 0x6e, 0xe1, - 0x51, 0x1f, 0x0d, 0xbe, 0x64, 0xf3, 0x67, 0x3d, 0xb3, 0x22, 0x75, 0x92, 0xc7, 0x3a, 0xe5, 0xd6, - 0x09, 0x67, 0xc3, 0x31, 0x72, 0x9f, 0x19, 0x76, 0xff, 0x06, 0x29, 0x0d, 0x9d, 0x36, 0x91, 0x10, - 0x7c, 0xc1, 0x66, 0xa8, 0x6d, 0xa5, 0xeb, 0xf1, 0x8a, 0x95, 0x70, 0x0b, 0xc2, 0xc4, 0x9d, 0x70, - 0x9c, 0xbc, 0xa7, 0x2b, 0x42, 0xd5, 0x1d, 0x49, 0x93, 0xe0, 0x60, 0x89, 0x5d, 0xb5, 0xa2, 0x05, - 0xbc, 0xc8, 0x13, 0xe1, 0xc0, 0x86, 0xd7, 0x88, 0x3e, 0x8a, 0xb6, 0xe7, 0xde, 0x14, 0xfc, 0x99, - 0xcd, 0x61, 0x73, 0x82, 0xe1, 0xa9, 0xd6, 0xfb, 0x45, 0x5e, 0x96, 0x42, 0x4b, 0x63, 0x23, 0xda, - 0x70, 0x02, 0x3d, 0xd6, 0xe6, 0x4e, 0x4f, 0xea, 0xd3, 0x3b, 0x44, 0xdb, 0x22, 0x16, 0x55, 0xc5, - 0xa6, 0x36, 0x9b, 0x4f, 0x6d, 0x34, 0x9d, 0x5f, 0x04, 0xec, 0x5b, 0xac, 0xaf, 0x63, 0xd9, 0x3e, - 0x16, 0x6d, 0xd2, 0xe4, 0xa0, 0x7c, 0xd6, 0x27, 0x29, 0x88, 0x49, 0x0f, 0x21, 0x7f, 0xc3, 0x03, - 0xc1, 0x57, 0x6c, 0xde, 0x40, 0xb7, 0x90, 0x06, 0x38, 0x1c, 0xe5, 0xa9, 0x8c, 0xa5, 0xc3, 0x22, - 0xcb, 0x84, 0xe9, 0xf1, 0x7d, 0xe8, 0xd9, 0x30, 0xf0, 0x27, 0x5f, 0x92, 0x36, 0x4a, 0xce, 0x8e, - 0xa7, 0x3c, 0x85, 0x9e, 0xc5, 0x56, 0x68, 0x69, 0x13, 0x03, 0xc7, 0x11, 0x93, 0x6b, 0xa9, 0x1c, - 0x37, 0x60, 0x9d, 0x30, 0x2e, 0x7c, 0x87, 0x9c, 0xaf, 0x13, 0xdc, 0xac, 0xd0, 0xc8, 0x83, 0xc1, - 0x43, 0x36, 0x23, 0xb0, 0x82, 0x70, 0x50, 0xe5, 0xc2, 0x00, 0x17, 0x16, 0x93, 0x4d, 0x05, 0x13, - 0x4e, 0x79, 0x4f, 0x22, 0xec, 0x78, 0xfc, 0x2b, 0xbb, 0x9d, 0x3b, 0xac, 0x11, 0xdc, 0xa4, 0x83, - 0x2c, 0xaf, 0x06, 0x5d, 0xb5, 0xc9, 0xeb, 0x7e, 0x93, 0x08, 0xf9, 0x49, 0x57, 0x6d, 0x72, 0x9b, - 0xdd, 0x96, 0x59, 0xb9, 0xb9, 0x58, 0xa7, 0x45, 0xa6, 0x38, 0xd5, 0x1f, 0xf6, 0xb5, 0x54, 0xed, - 0xbe, 0xc0, 0xbb, 0xbe, 0x36, 0x2b, 0x6e, 0x83, 0xa8, 0x3b, 0x43, 0xcc, 0x4a, 0xf0, 0x05, 0xbb, - 0xa7, 0x0f, 0xc0, 0x18, 0x99, 0x54, 0xc5, 0x65, 0xa0, 0x8d, 0x83, 0xe5, 0x58, 0x2b, 0xe0, 0xb1, - 0x56, 0x2d, 0x39, 0x50, 0x0d, 0x49, 0xf5, 0x76, 0xe5, 0x40, 0x95, 0x16, 0x11, 0xfd, 0x7b, 0xad, - 0xa0, 0x41, 0xe4, 0x4a, 0xf8, 0x4f, 0xec, 0x46, 0x47, 0xd8, 0x0e, 0xb7, 0x1d, 0x61, 0x12, 0x48, - 0xb8, 0x54, 0x09, 0x1c, 0x0d, 0x6d, 0x71, 0xc6, 0x57, 0x2e, 0x72, 0x9a, 0x9e, 0xf2, 0xd8, 0x33, - 0x2a, 0x81, 0xcf, 0xd9, 0x0c, 0xb6, 0x1a, 0xe5, 0xb5, 0x55, 0xa4, 0xa9, 0xcf, 0x11, 0xb7, 0xb1, - 0x50, 0x36, 0x9c, 0xf5, 0x6d, 0x53, 0x11, 0x36, 0x8b, 0x34, 0xa5, 0x44, 0x35, 0x11, 0x0d, 0x7e, - 0xcf, 0x66, 0xfb, 0x59, 0xb2, 0x90, 0x42, 0xec, 0xa8, 0x22, 0x7d, 0x1d, 0x87, 0x73, 0xbe, 0xea, - 0x2b, 0x46, 0x93, 0x08, 0x9b, 0xda, 0xf8, 0x9a, 0x0e, 0x96, 0xd9, 0x84, 0x54, 0x16, 0x8c, 0xe3, - 0x2d, 0x61, 0x1d, 0xcf, 0x85, 0xeb, 0x84, 0x37, 0xc8, 0x65, 0xdc, 0xdb, 0x37, 0x85, 0x75, 0x3b, - 0xc2, 0x75, 0x82, 0x47, 0x6c, 0x49, 0xa4, 0x0e, 0x4c, 0x75, 0x12, 0xae, 0x97, 0x03, 0x6f, 0x83, - 0x02, 0x23, 0xd2, 0xfe, 0x3e, 0xe7, 0xc9, 0x75, 0x9e, 0x88, 0xfe, 0x18, 0x76, 0x7b, 0x39, 0x7c, - 0xed, 0x59, 0xd5, 0x5e, 0x3f, 0x64, 0x81, 0xed, 0xa9, 0xb8, 0x63, 0xb4, 0xd2, 0x85, 0xe5, 0xb1, - 0xce, 0x70, 0x94, 0x2e, 0xf8, 0x2a, 0x18, 0x42, 0x1a, 0x04, 0x04, 0xef, 0xb1, 0x6b, 0x5e, 0x9e, - 0x5b, 0xe8, 0x52, 0x46, 0xc2, 0x3a, 0x71, 0xc7, 0xbc, 0xb9, 0x09, 0x5d, 0x4c, 0x44, 0xb0, 0xcb, - 0xee, 0x96, 0xbc, 0x42, 0xc9, 0x6e, 0x01, 0xfc, 0x50, 0xba, 0x8e, 0x2e, 0x9c, 0x3f, 0x0c, 0x3c, - 0x5d, 0xeb, 0x8c, 0x90, 0xca, 0xd9, 0x70, 0x89, 0xfc, 0x6f, 0x79, 0xfa, 0x73, 0x62, 0xbf, 0xf0, - 0x64, 0x3a, 0x96, 0xc6, 0x80, 0x1a, 0xfc, 0x81, 0xcd, 0x59, 0x57, 0xec, 0xf1, 0x58, 0x38, 0x91, - 0xea, 0xf6, 0xeb, 0xb5, 0x7b, 0x93, 0x94, 0x42, 0xa4, 0x34, 0x3c, 0xe3, 0x6c, 0x09, 0x3f, 0x63, - 0x77, 0xe0, 0x28, 0x07, 0x23, 0x33, 0x50, 0x4e, 0xa4, 0xb8, 0xd9, 0x9c, 0xc6, 0x6b, 0x99, 0x45, - 0x03, 0x87, 0x46, 0xe2, 0xb8, 0xb9, 0x45, 0xd7, 0xfd, 0xcd, 0x61, 0x72, 0xa3, 0xe4, 0xfa, 0x44, - 0x46, 0x25, 0x33, 0xf8, 0x2b, 0xbb, 0x1f, 0xeb, 0xbc, 0x77, 0xb6, 0x15, 0x0e, 0x3b, 0xa0, 0x78, - 0x02, 0x52, 0x39, 0x30, 0x29, 0x88, 0x03, 0xb4, 0x51, 0xa8, 0xe1, 0x6d, 0x8a, 0xf0, 0x2e, 0xba, - 0x0c, 0xb7, 0xc4, 0x8b, 0x0e, 0xa8, 0xf5, 0x33, 0x7c, 0x0a, 0x1c, 0x47, 0x68, 0x95, 0x6d, 0x67, - 0x40, 0x64, 0xdc, 0x00, 0x56, 0x0e, 0x5d, 0xaf, 0xe1, 0x1d, 0x5f, 0x4c, 0x65, 0xde, 0x09, 0x8f, - 0x06, 0xb0, 0xbf, 0x24, 0x6d, 0x91, 0x3a, 0xcb, 0xf7, 0x8a, 0x16, 0xce, 0x49, 0x2b, 0x8f, 0x21, - 0x7c, 0xaf, 0xba, 0x24, 0x09, 0x5a, 0x23, 0xa4, 0x29, 0x8f, 0x01, 0xaf, 0x8a, 0xdc, 0xe8, 0x5c, - 0xb4, 0x85, 0xc3, 0x2b, 0x3f, 0x2f, 0x1c, 0xa7, 0x7b, 0x54, 0xaa, 0x76, 0x78, 0xd7, 0xd7, 0x7c, - 0x1f, 0x7f, 0x8c, 0xf0, 0x76, 0x89, 0x06, 0xff, 0xa8, 0xb1, 0x33, 0xa9, 0xa2, 0x9b, 0xcb, 0x76, - 0x53, 0x1a, 0x42, 0x94, 0x90, 0x4c, 0x27, 0x10, 0x2e, 0xd3, 0x3b, 0x61, 0xf3, 0xf4, 0xa4, 0x5e, - 0xdf, 0x18, 0x62, 0xe3, 0xdd, 0xd5, 0x7c, 0xb6, 0xb5, 0x53, 0x72, 0xbf, 0xd1, 0x09, 0xbc, 0xfa, - 0xdf, 0x94, 0xa8, 0x0e, 0xaf, 0x11, 0x6c, 0x37, 0x1d, 0x26, 0x04, 0x9b, 0x6c, 0x0c, 0xe3, 0xe0, - 0x18, 0x08, 0xad, 0x7f, 0x8f, 0xd6, 0xbf, 0x79, 0x7a, 0x52, 0x1f, 0x2d, 0x05, 0xcb, 0xb5, 0xae, - 0x95, 0x7f, 0x37, 0x8e, 0x20, 0x26, 0xed, 0x51, 0x74, 0x6c, 0x76, 0x53, 0xd2, 0x79, 0xc1, 0x66, - 0x2c, 0x18, 0x29, 0x52, 0xae, 0xb4, 0xc9, 0x44, 0x2a, 0x8f, 0x29, 0xbf, 0x5e, 0xf3, 0x7d, 0xd2, - 0x9c, 0x7b, 0x75, 0x52, 0x9f, 0x6e, 0x12, 0xe9, 0xdb, 0x61, 0x0e, 0x89, 0x4d, 0xdb, 0x8b, 0x81, - 0x60, 0x9b, 0x4d, 0x2b, 0x38, 0xe4, 0x36, 0xee, 0x40, 0x26, 0x78, 0xdc, 0x11, 0xaa, 0x0d, 0xc6, - 0xcb, 0xde, 0x27, 0xd9, 0xf0, 0xd5, 0x49, 0x7d, 0xea, 0x5b, 0x38, 0x6c, 0x12, 0xa3, 0xe1, 0x09, - 0xa4, 0x39, 0xa5, 0x2e, 0xb0, 0x06, 0x7f, 0x63, 0xe3, 0x16, 0xba, 0x05, 0xa8, 0x18, 0x78, 0x2c, - 0xe2, 0x0e, 0x84, 0x1f, 0x2c, 0x5e, 0x5e, 0x1e, 0x7d, 0xb0, 0xbe, 0xf2, 0x2b, 0xef, 0xe3, 0x95, - 0x8b, 0x5e, 0xc1, 0x2b, 0xcd, 0x52, 0xa7, 0x81, 0x32, 0x1b, 0xca, 0x99, 0x9e, 0x7f, 0xe0, 0x9d, - 0xb1, 0x47, 0x63, 0x76, 0xf8, 0x6f, 0x70, 0x9f, 0x4d, 0xe6, 0xa9, 0x88, 0x01, 0xcf, 0xa4, 0xdf, - 0x93, 0x1f, 0x52, 0xe9, 0x4c, 0xf4, 0x81, 0xaa, 0x17, 0x73, 0x16, 0x54, 0x0f, 0xc9, 0xc2, 0x82, - 0xe1, 0xf4, 0x2c, 0x0f, 0x57, 0xb0, 0xf1, 0xd6, 0xd6, 0x5e, 0x9d, 0xd4, 0xff, 0xd8, 0x96, 0xae, - 0x53, 0xec, 0xad, 0xc4, 0x3a, 0x5b, 0xed, 0x87, 0x9f, 0xec, 0x0d, 0x7e, 0xaf, 0xe6, 0xfb, 0xed, - 0x55, 0x0b, 0x71, 0x81, 0x0f, 0xb6, 0x95, 0xe6, 0xb3, 0xad, 0xe7, 0x16, 0x8c, 0x12, 0x19, 0xec, - 0xa0, 0x52, 0x34, 0x51, 0xaa, 0xa3, 0x95, 0x2c, 0xc1, 0x2a, 0x9b, 0xa2, 0xb7, 0x9c, 0x3e, 0xb4, - 0x1c, 0xbb, 0xd7, 0x81, 0xe2, 0xa9, 0x6e, 0x87, 0xab, 0xbe, 0x23, 0xdc, 0x91, 0x8a, 0xf4, 0xa1, - 0x7d, 0xe1, 0x91, 0x2d, 0xdd, 0xbe, 0xd0, 0x01, 0x8c, 0x09, 0x3f, 0xba, 0xc8, 0x61, 0xc3, 0x98, - 0xe0, 0x1e, 0x9b, 0xec, 0x3b, 0xd0, 0x93, 0x11, 0xe5, 0x3f, 0x26, 0xf6, 0x78, 0xc9, 0xc6, 0x77, - 0x1e, 0x6a, 0x9f, 0xa3, 0xa2, 0xf0, 0x83, 0x73, 0x54, 0x54, 0x7d, 0xc0, 0xae, 0x8b, 0xc2, 0x69, - 0x6e, 0xa0, 0xa3, 0xb3, 0xe1, 0x9b, 0xf6, 0x13, 0x4a, 0xed, 0x3b, 0x08, 0x46, 0x25, 0x56, 0x65, - 0x77, 0x89, 0x5d, 0x95, 0x96, 0xdb, 0x22, 0x07, 0x83, 0xd9, 0x0d, 0x3f, 0xf5, 0xef, 0x27, 0x69, - 0x9b, 0x95, 0x09, 0x77, 0x97, 0x0a, 0xd3, 0x06, 0x7f, 0xc3, 0xe1, 0x24, 0xa7, 0x68, 0xc2, 0xdf, - 0x2e, 0xd6, 0x96, 0x6b, 0xd1, 0x24, 0x61, 0x78, 0xb9, 0xe1, 0x38, 0xc7, 0x70, 0x70, 0xf8, 0x4a, - 0xf5, 0x03, 0xde, 0x68, 0x06, 0x9c, 0xe9, 0x61, 0xc4, 0xda, 0x0c, 0x86, 0xef, 0x67, 0x7e, 0xf8, - 0x7a, 0x4a, 0x84, 0x8c, 0x0d, 0x22, 0x54, 0x21, 0xbd, 0xcf, 0x26, 0x15, 0xae, 0x44, 0x43, 0x05, - 0x12, 0x9e, 0x0a, 0xeb, 0xc2, 0xdf, 0x91, 0xd3, 0x35, 0x04, 0xb6, 0xbd, 0x7d, 0x4b, 0x58, 0x87, - 0x1f, 0x61, 0xe5, 0x5b, 0x97, 0x66, 0x08, 0x6f, 0x63, 0x7f, 0x87, 0x0f, 0x7d, 0x29, 0x95, 0x08, - 0x36, 0xfb, 0xd7, 0x68, 0x0f, 0xf6, 0xd9, 0x78, 0x5c, 0x58, 0xa7, 0x33, 0x7a, 0x66, 0x6a, 0x65, - 0xc3, 0xcf, 0xff, 0xdf, 0xba, 0x6f, 0x90, 0xce, 0xb6, 0x97, 0xa1, 0xba, 0x8f, 0xc6, 0xe2, 0x61, - 0xdb, 0x6c, 0x97, 0x05, 0xe7, 0x9b, 0x23, 0x98, 0x60, 0x97, 0xf7, 0xa1, 0x47, 0x9f, 0x89, 0x63, - 0x11, 0xfe, 0x0c, 0x36, 0xd8, 0x5b, 0x07, 0x22, 0x2d, 0x80, 0xbe, 0x01, 0x47, 0x1f, 0xac, 0xfe, - 0x6a, 0x2c, 0xe7, 0x15, 0x23, 0xef, 0xfd, 0xc5, 0x1b, 0x0f, 0x6b, 0xb3, 0x5f, 0xb2, 0xe0, 0x7c, - 0x5c, 0xc3, 0x4b, 0x8e, 0xf8, 0x25, 0xa7, 0x86, 0x97, 0x1c, 0x19, 0x52, 0x78, 0xf2, 0xe6, 0x95, - 0xe9, 0x89, 0xf0, 0xc9, 0x9b, 0x57, 0x16, 0x27, 0x96, 0x6e, 0xfe, 0xb3, 0x76, 0xe1, 0x0e, 0xee, - 0xb0, 0x71, 0x9a, 0x19, 0x09, 0x3f, 0x00, 0x83, 0xa1, 0x95, 0x9b, 0x19, 0xf3, 0xd6, 0xef, 0xbc, - 0x31, 0xb8, 0xc5, 0xc6, 0xe2, 0xc2, 0x18, 0xec, 0xf0, 0xc1, 0x5a, 0x97, 0xa3, 0xab, 0xa5, 0xf1, - 0x3b, 0xb4, 0x05, 0x37, 0xd8, 0x88, 0x54, 0xb1, 0xa1, 0x7e, 0xf7, 0x5f, 0xad, 0xd1, 0xc0, 0x10, - 0xcc, 0x33, 0xa6, 0x8a, 0xcc, 0xbb, 0x5b, 0xff, 0x4d, 0x1a, 0x8d, 0xa8, 0x22, 0x23, 0x5f, 0xbb, - 0xb6, 0xfa, 0xe3, 0xbf, 0x17, 0x2e, 0xfd, 0x78, 0xba, 0x50, 0xfb, 0xe9, 0x74, 0xa1, 0xf6, 0xf3, - 0xe9, 0x42, 0xed, 0x5f, 0xa7, 0x0b, 0xb5, 0xbf, 0xbf, 0x5c, 0xb8, 0xf4, 0xd3, 0xcb, 0x85, 0x4b, - 0x3f, 0xbf, 0x5c, 0xb8, 0xf4, 0xfd, 0xd8, 0x99, 0xe4, 0xed, 0xbd, 0x4d, 0xa3, 0xe3, 0x93, 0xff, - 0x06, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xa0, 0xca, 0x9c, 0x37, 0x10, 0x00, 0x00, + // 1947 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x5d, 0x53, 0x1c, 0xc7, + 0xd5, 0xd6, 0x5a, 0xb2, 0x5f, 0xd1, 0x08, 0x04, 0x63, 0x64, 0x06, 0x10, 0x2c, 0xe8, 0xc3, 0x46, + 0xfe, 0x60, 0x6d, 0xd9, 0xaf, 0x23, 0x3b, 0x95, 0xc4, 0x66, 0x01, 0xeb, 0x03, 0x1b, 0x34, 0x8b, + 0xac, 0x8a, 0x73, 0xd1, 0xd5, 0xcc, 0x9c, 0xdd, 0x6d, 0x33, 0xd3, 0x3d, 0xdb, 0xdd, 0x23, 0x58, + 0x2e, 0xf2, 0x1b, 0x52, 0x95, 0xeb, 0x54, 0xfe, 0x8e, 0x2f, 0x7d, 0xe9, 0x2b, 0x2a, 0x41, 0xff, + 0x42, 0x57, 0xa9, 0x73, 0x7a, 0x66, 0x77, 0x11, 0xd8, 0xa9, 0xca, 0xdd, 0xee, 0x79, 0x9e, 0xf3, + 0x74, 0xf7, 0xe9, 0xf3, 0xd1, 0xc3, 0x1a, 0xb6, 0x97, 0x36, 0x2c, 0x58, 0x2b, 0xb5, 0x4a, 0x84, + 0x13, 0xf9, 0x7e, 0x23, 0xd5, 0xb1, 0x48, 0xb9, 0x56, 0x69, 0x9f, 0x97, 0x00, 0x47, 0x64, 0x2d, + 0x37, 0xda, 0xe9, 0x60, 0x21, 0xd6, 0xf1, 0x81, 0xd1, 0x22, 0xee, 0xae, 0xd9, 0x5e, 0xba, 0x76, + 0xc6, 0x75, 0x7e, 0xa6, 0xa3, 0x3b, 0x9a, 0x78, 0x0d, 0xfc, 0xe5, 0x5d, 0x6e, 0xfd, 0x73, 0x91, + 0xcd, 0x6c, 0xa3, 0xe8, 0x8e, 0x4a, 0xfb, 0x2d, 0xef, 0xb0, 0x21, 0x9c, 0x08, 0x3e, 0x64, 0x81, + 0x15, 0x2f, 0x80, 0x3b, 0xb1, 0x9f, 0x82, 0xe5, 0xb9, 0x81, 0xb6, 0x3c, 0x0a, 0x6b, 0xcb, 0xb5, + 0xd5, 0xb1, 0x68, 0x0a, 0x91, 0x3d, 0x02, 0x76, 0xc9, 0x1e, 0xfc, 0x85, 0x2d, 0xe8, 0xdc, 0xc9, + 0x4c, 0x1e, 0x83, 0xe1, 0xed, 0x03, 0x1e, 0x0b, 0x1b, 0x8b, 0x04, 0x2c, 0x4f, 0x65, 0x26, 0x5d, + 0xf8, 0xc6, 0x72, 0x6d, 0xf5, 0xf2, 0xfa, 0xcd, 0xd3, 0x93, 0x7a, 0xb8, 0x53, 0xd1, 0xb6, 0x9e, + 0x34, 0x4b, 0xd2, 0x36, 0x72, 0xa2, 0x70, 0x20, 0xb0, 0x75, 0x70, 0x06, 0x09, 0x3e, 0x63, 0xd7, + 0xac, 0xcb, 0x1c, 0x77, 0x32, 0x03, 0x5d, 0xb8, 0xf0, 0x32, 0xa9, 0x4d, 0xbf, 0x3a, 0xa9, 0x4f, + 0xa0, 0x69, 0x6d, 0xa3, 0x30, 0xc2, 0x49, 0xad, 0xa2, 0x71, 0xa4, 0xed, 0x79, 0x56, 0xf0, 0x90, + 0xcd, 0xca, 0x24, 0x05, 0x2e, 0xd5, 0x20, 0x54, 0x95, 0xc0, 0x95, 0x5f, 0x13, 0x98, 0x41, 0x8f, + 0x47, 0xaa, 0x8c, 0x43, 0xa5, 0xc4, 0xd9, 0xed, 0x4a, 0xc9, 0x19, 0xa1, 0xac, 0x88, 0x91, 0x7c, + 0x4e, 0xf5, 0xcd, 0x5f, 0x53, 0xad, 0x7b, 0xd5, 0xbd, 0xa1, 0xef, 0x6b, 0x0b, 0x7c, 0xce, 0x66, + 0x95, 0x76, 0x32, 0x06, 0x9e, 0x48, 0x9b, 0xa7, 0x02, 0x2f, 0xf7, 0x05, 0x18, 0xe9, 0xfa, 0xe1, + 0x5b, 0xcb, 0xb5, 0xd5, 0x89, 0xe8, 0x86, 0x87, 0x37, 0x3c, 0xda, 0x2a, 0xc1, 0x60, 0x8d, 0xbd, + 0x6d, 0x40, 0x9b, 0x04, 0x0c, 0xff, 0x51, 0x4b, 0x55, 0x45, 0xfb, 0xff, 0x70, 0x23, 0xd1, 0x74, + 0x09, 0x3d, 0x46, 0xc4, 0x07, 0xf2, 0x63, 0x36, 0x93, 0x40, 0x5b, 0x14, 0xa9, 0xe3, 0xee, 0x48, + 0xf1, 0xdc, 0x48, 0x4d, 0x8b, 0x5c, 0x25, 0x87, 0xa0, 0xc4, 0xf6, 0x8e, 0xd4, 0x6e, 0x89, 0x04, + 0x9f, 0xb0, 0x1b, 0xa3, 0x1e, 0x06, 0x44, 0x42, 0xd9, 0x17, 0x8e, 0x2d, 0xd7, 0x56, 0xaf, 0x8e, + 0xba, 0x44, 0x20, 0x12, 0xcc, 0xa1, 0x60, 0x9d, 0x2d, 0x8d, 0xba, 0x14, 0x16, 0x78, 0x5b, 0xa7, + 0xa9, 0x3e, 0x04, 0x43, 0xfe, 0x36, 0x64, 0xe4, 0x3b, 0x3f, 0xf4, 0x7d, 0x66, 0x61, 0xab, 0xa4, + 0xa0, 0x8c, 0x0d, 0x76, 0xd8, 0x9d, 0x5c, 0x18, 0x27, 0x45, 0x9a, 0xf6, 0x31, 0x26, 0xce, 0xc8, + 0xfd, 0xc2, 0x41, 0xc2, 0xf3, 0x54, 0x28, 0x8b, 0x16, 0x4c, 0xbe, 0x24, 0x1c, 0x27, 0xa5, 0x95, + 0x01, 0x77, 0x63, 0x48, 0xdd, 0x45, 0xe6, 0x46, 0x49, 0x0c, 0x1e, 0xb0, 0x61, 0x7a, 0xd1, 0x96, + 0xba, 0xd2, 0x3a, 0xdd, 0x31, 0x22, 0xb3, 0xe1, 0x35, 0x12, 0x79, 0x67, 0x80, 0x3f, 0xb3, 0xf0, + 0x70, 0x80, 0x06, 0x5f, 0xb1, 0xc5, 0xb3, 0x9e, 0x59, 0x91, 0x3a, 0xc9, 0x63, 0x9d, 0x72, 0xeb, + 0x84, 0xb3, 0xe1, 0x04, 0xb9, 0xcf, 0x8d, 0xba, 0x7f, 0x8b, 0x94, 0xa6, 0x4e, 0x5b, 0x48, 0x08, + 0xbe, 0x64, 0x73, 0x54, 0xb6, 0xd2, 0xf5, 0x79, 0xc5, 0x4a, 0xb8, 0x05, 0x61, 0xe2, 0x6e, 0x38, + 0x49, 0xde, 0xb3, 0x15, 0xa1, 0xaa, 0x8e, 0xa4, 0x45, 0x70, 0xb0, 0xc2, 0xae, 0x59, 0xd1, 0x06, + 0x5e, 0xe4, 0x89, 0x70, 0x60, 0xc3, 0xeb, 0x44, 0x1f, 0x47, 0xdb, 0x33, 0x6f, 0x0a, 0xfe, 0xcc, + 0x16, 0xb0, 0x38, 0xc1, 0xf0, 0x54, 0xeb, 0x83, 0x22, 0x2f, 0x53, 0xa1, 0xad, 0xb1, 0x10, 0x6d, + 0x38, 0x85, 0x1e, 0xeb, 0x0b, 0xa7, 0x27, 0xf5, 0xd9, 0x5d, 0xa2, 0x6d, 0x13, 0x8b, 0xb2, 0x62, + 0x4b, 0x9b, 0xad, 0x27, 0x36, 0x9a, 0xcd, 0x2f, 0x02, 0x0e, 0x2c, 0xe6, 0xd7, 0xb1, 0xec, 0x1c, + 0x8b, 0x0e, 0x69, 0x72, 0x50, 0x3e, 0xea, 0xd3, 0xb4, 0x89, 0x69, 0x0f, 0x21, 0x7f, 0xd3, 0x03, + 0xc1, 0xd7, 0x6c, 0xd1, 0x40, 0xaf, 0x90, 0x06, 0x38, 0x1c, 0xe5, 0xa9, 0x8c, 0xa5, 0xc3, 0x24, + 0xcb, 0x84, 0xe9, 0xf3, 0x03, 0xe8, 0xdb, 0x30, 0xf0, 0x37, 0x5f, 0x92, 0x36, 0x4b, 0xce, 0xae, + 0xa7, 0x3c, 0x81, 0xbe, 0xc5, 0x52, 0x68, 0x6b, 0x13, 0x03, 0xc7, 0x16, 0x93, 0x6b, 0xa9, 0x1c, + 0x37, 0x60, 0x9d, 0x30, 0x2e, 0x7c, 0x9b, 0x9c, 0x6f, 0x10, 0xdc, 0xaa, 0xd0, 0xc8, 0x83, 0xc1, + 0x03, 0x36, 0x27, 0x30, 0x83, 0xb0, 0x51, 0xe5, 0xc2, 0x00, 0x17, 0x16, 0x83, 0x4d, 0x09, 0x13, + 0xce, 0x78, 0x4f, 0x22, 0xec, 0x7a, 0xfc, 0x6b, 0xbb, 0x93, 0x3b, 0xcc, 0x11, 0x3c, 0xa4, 0x83, + 0x2c, 0xaf, 0x1a, 0x5d, 0x75, 0xc8, 0x1b, 0xfe, 0x90, 0x08, 0xf9, 0x4e, 0x57, 0x1d, 0x72, 0x87, + 0xdd, 0x91, 0x59, 0x79, 0xb8, 0x58, 0xa7, 0x45, 0xa6, 0x38, 0xe5, 0x1f, 0xd6, 0xb5, 0x54, 0x9d, + 0x81, 0xc0, 0x3b, 0x3e, 0x37, 0x2b, 0x6e, 0x93, 0xa8, 0xbb, 0x23, 0xcc, 0x4a, 0xf0, 0x39, 0xbb, + 0xa7, 0x5f, 0x80, 0x31, 0x32, 0xa9, 0x92, 0xcb, 0x40, 0x07, 0x1b, 0xcb, 0xb1, 0x56, 0xc0, 0x63, + 0xad, 0xda, 0x72, 0xa8, 0x1a, 0x92, 0xea, 0x9d, 0xca, 0x81, 0x32, 0x2d, 0x22, 0xfa, 0x0f, 0x5a, + 0x41, 0x93, 0xc8, 0x95, 0xf0, 0x9f, 0xd8, 0xcd, 0xae, 0xb0, 0x5d, 0x6e, 0xbb, 0xc2, 0x24, 0x90, + 0x70, 0xa9, 0x12, 0x38, 0x1a, 0x39, 0xe2, 0x9c, 0xcf, 0x5c, 0xe4, 0xb4, 0x3c, 0xe5, 0x91, 0x67, + 0x54, 0x02, 0x5f, 0xb0, 0x39, 0x2c, 0x35, 0x8a, 0x6b, 0xbb, 0x48, 0x53, 0x1f, 0x23, 0x6e, 0x63, + 0xa1, 0x6c, 0x38, 0xef, 0xcb, 0xa6, 0x22, 0x6c, 0x15, 0x69, 0x4a, 0x81, 0x6a, 0x21, 0x1a, 0xfc, + 0x9e, 0xcd, 0x0f, 0xa2, 0x64, 0x21, 0x85, 0xd8, 0x51, 0x46, 0xfa, 0x3c, 0x0e, 0x17, 0x7c, 0xd6, + 0x57, 0x8c, 0x16, 0x11, 0xb6, 0xb4, 0xf1, 0x39, 0x1d, 0xac, 0xb2, 0x29, 0xa9, 0x2c, 0x18, 0xc7, + 0xdb, 0xc2, 0x3a, 0x9e, 0x0b, 0xd7, 0x0d, 0x6f, 0x92, 0xcb, 0xa4, 0xb7, 0x6f, 0x09, 0xeb, 0x76, + 0x85, 0xeb, 0x06, 0x0f, 0xd9, 0x8a, 0x48, 0x1d, 0x98, 0xea, 0x26, 0x5c, 0x3f, 0x07, 0xde, 0x01, + 0x05, 0x46, 0xa4, 0x83, 0x73, 0x2e, 0x92, 0xeb, 0x22, 0x11, 0xfd, 0x35, 0xec, 0xf5, 0x73, 0xf8, + 0xc6, 0xb3, 0xaa, 0xb3, 0x7e, 0xc4, 0x02, 0xdb, 0x57, 0x71, 0xd7, 0x68, 0xa5, 0x0b, 0xcb, 0x63, + 0x9d, 0x61, 0x2b, 0x5d, 0xf2, 0x59, 0x30, 0x82, 0x34, 0x09, 0x08, 0xde, 0x65, 0xd7, 0xbd, 0x3c, + 0xb7, 0xd0, 0xa3, 0x88, 0x84, 0x75, 0xe2, 0x4e, 0x78, 0x73, 0x0b, 0x7a, 0x18, 0x88, 0x60, 0x8f, + 0xbd, 0x57, 0xf2, 0x0a, 0x25, 0x7b, 0x05, 0xf0, 0x43, 0xe9, 0xba, 0xba, 0x70, 0xfe, 0x32, 0xf0, + 0x76, 0xad, 0x33, 0x42, 0x2a, 0x67, 0xc3, 0x15, 0xf2, 0xbf, 0xed, 0xe9, 0xcf, 0x88, 0xfd, 0xdc, + 0x93, 0xe9, 0x5a, 0x9a, 0x43, 0x6a, 0xf0, 0x07, 0xb6, 0x60, 0x5d, 0xb1, 0xcf, 0x63, 0xe1, 0x44, + 0xaa, 0x3b, 0xaf, 0xe7, 0xee, 0x2d, 0x52, 0x0a, 0x91, 0xd2, 0xf4, 0x8c, 0xb3, 0x29, 0xfc, 0x94, + 0xdd, 0x85, 0xa3, 0x1c, 0x8c, 0xcc, 0x40, 0x39, 0x91, 0xe2, 0x61, 0x73, 0x6a, 0xaf, 0x65, 0x14, + 0x0d, 0x1c, 0x1a, 0x89, 0xed, 0xe6, 0x36, 0x8d, 0xfb, 0x5b, 0xa3, 0xe4, 0x66, 0xc9, 0xf5, 0x81, + 0x8c, 0x4a, 0x26, 0x36, 0xb9, 0x2a, 0x1e, 0xce, 0x80, 0xc8, 0xb8, 0x01, 0xbc, 0x5b, 0x1a, 0x80, + 0xe1, 0x5d, 0x7f, 0xdd, 0x65, 0x64, 0x08, 0x8f, 0x86, 0xb0, 0x1f, 0x63, 0xb6, 0x48, 0x9d, 0xe5, + 0xfb, 0x45, 0x1b, 0x3b, 0x99, 0x95, 0xc7, 0x10, 0xbe, 0x5b, 0x8d, 0x31, 0x82, 0xd6, 0x09, 0x69, + 0xc9, 0x63, 0xc0, 0x66, 0x9e, 0x1b, 0x9d, 0x8b, 0x8e, 0x70, 0x38, 0x94, 0xf3, 0xc2, 0x71, 0x9a, + 0x74, 0x52, 0x75, 0xc2, 0xf7, 0x7c, 0x56, 0x0e, 0xf0, 0x47, 0x08, 0xef, 0x94, 0x68, 0xf0, 0xf7, + 0x1a, 0x3b, 0x73, 0x18, 0x9a, 0x2d, 0xb6, 0x97, 0x52, 0x9b, 0xa0, 0xea, 0xcd, 0x74, 0x02, 0xe1, + 0x2a, 0x4d, 0xf2, 0xad, 0xd3, 0x93, 0x7a, 0x7d, 0x73, 0x84, 0x8d, 0xd3, 0xa5, 0xf5, 0x74, 0x7b, + 0xb7, 0xe4, 0x7e, 0xab, 0x13, 0x78, 0xf5, 0xdf, 0x29, 0x51, 0x1d, 0x5e, 0x23, 0xd8, 0x5e, 0x3a, + 0x4a, 0x08, 0xb6, 0xd8, 0x04, 0xee, 0x83, 0xe3, 0x46, 0x68, 0xfd, 0x7b, 0xb4, 0xfe, 0xad, 0xd3, + 0x93, 0xfa, 0x78, 0x29, 0x58, 0xae, 0x75, 0xbd, 0xfc, 0xbb, 0x79, 0x04, 0x31, 0x69, 0x8f, 0xa3, + 0x63, 0xab, 0x97, 0x92, 0xce, 0x73, 0x36, 0x67, 0xc1, 0x48, 0x91, 0x72, 0xa5, 0x4d, 0x26, 0x52, + 0x79, 0x4c, 0xf1, 0xf5, 0x9a, 0xef, 0x93, 0xe6, 0xc2, 0xab, 0x93, 0xfa, 0x6c, 0x8b, 0x48, 0xdf, + 0x8d, 0x72, 0x48, 0x6c, 0xd6, 0x5e, 0x0c, 0x04, 0x3b, 0x6c, 0x56, 0xc1, 0x21, 0xb7, 0x71, 0x17, + 0x32, 0xc1, 0xe3, 0xae, 0x50, 0x1d, 0x30, 0x5e, 0xf6, 0x03, 0x92, 0x0d, 0x5f, 0x9d, 0xd4, 0x67, + 0xbe, 0x83, 0xc3, 0x16, 0x31, 0x9a, 0x9e, 0x40, 0x9a, 0x33, 0xea, 0x02, 0x6b, 0xf0, 0x57, 0x36, + 0x69, 0xa1, 0x57, 0x80, 0x8a, 0x81, 0xc7, 0x22, 0xee, 0x42, 0xf8, 0xe1, 0xf2, 0xe5, 0xd5, 0xf1, + 0xfb, 0x1b, 0x6b, 0xbf, 0xf1, 0x82, 0x5d, 0xbb, 0xe8, 0x9d, 0xba, 0xd6, 0x2a, 0x75, 0x9a, 0x28, + 0xb3, 0xa9, 0x9c, 0xe9, 0xfb, 0x27, 0xd8, 0x19, 0x7b, 0x34, 0x61, 0x47, 0xff, 0x06, 0x1f, 0xb0, + 0xe9, 0x3c, 0x15, 0x31, 0xe0, 0x9d, 0x0c, 0xaa, 0xe6, 0x23, 0x4a, 0x9d, 0xa9, 0x01, 0x50, 0x55, + 0x4b, 0xce, 0x82, 0xea, 0xa9, 0x57, 0x58, 0x30, 0x9c, 0x1e, 0xce, 0xe1, 0x1a, 0x96, 0xc6, 0xfa, + 0xfa, 0xab, 0x93, 0xfa, 0x1f, 0x3b, 0xd2, 0x75, 0x8b, 0xfd, 0xb5, 0x58, 0x67, 0x8d, 0xc1, 0xf6, + 0x93, 0xfd, 0xe1, 0xef, 0x46, 0x7e, 0xd0, 0x69, 0x58, 0x88, 0x0b, 0x7c, 0x52, 0xad, 0xb5, 0x9e, + 0x6e, 0x3f, 0xb3, 0x60, 0x94, 0xc8, 0x60, 0x17, 0x95, 0xa2, 0xa9, 0x52, 0x1d, 0xad, 0x64, 0x09, + 0x1a, 0x6c, 0x86, 0x5e, 0x5b, 0xfa, 0xd0, 0x72, 0xac, 0x2f, 0x07, 0x8a, 0xa7, 0xba, 0x13, 0x36, + 0x7c, 0x45, 0xb8, 0x23, 0x15, 0xe9, 0x43, 0xfb, 0xdc, 0x23, 0xdb, 0xba, 0x73, 0xa1, 0x03, 0x18, + 0x13, 0x7e, 0x7c, 0x91, 0xc3, 0xa6, 0x31, 0xc1, 0x3d, 0x36, 0x3d, 0x70, 0xa0, 0x47, 0x1d, 0xca, + 0x7f, 0x42, 0xec, 0xc9, 0x92, 0x8d, 0x2f, 0x31, 0xd4, 0x3e, 0x47, 0x45, 0xe1, 0xfb, 0xe7, 0xa8, + 0xa8, 0x7a, 0x9f, 0xdd, 0x10, 0x85, 0xd3, 0xdc, 0x40, 0x57, 0x67, 0xa3, 0xb3, 0xf0, 0x53, 0x0a, + 0xed, 0xdb, 0x08, 0x46, 0x25, 0x56, 0x45, 0x77, 0x85, 0x5d, 0x93, 0x96, 0xdb, 0x22, 0x07, 0x83, + 0xd1, 0x0d, 0x3f, 0xf3, 0x2f, 0x1c, 0x69, 0x5b, 0x95, 0x09, 0x4f, 0x97, 0x0a, 0xd3, 0x01, 0x3f, + 0x83, 0xb0, 0xd7, 0xd2, 0x6e, 0xc2, 0xff, 0x5f, 0xae, 0xad, 0xd6, 0xa2, 0x69, 0xc2, 0x70, 0xfc, + 0x60, 0xc3, 0xc5, 0xed, 0x60, 0x7b, 0x94, 0xea, 0x47, 0x9c, 0x39, 0x06, 0x9c, 0xe9, 0xe3, 0x8e, + 0xb5, 0x19, 0xb6, 0xc7, 0xcf, 0x7d, 0x7b, 0xf4, 0x94, 0x08, 0x19, 0x9b, 0x44, 0xa8, 0xb6, 0xf4, + 0x3e, 0x9b, 0x56, 0xb8, 0x12, 0x35, 0x15, 0x48, 0x78, 0x2a, 0xac, 0x0b, 0x7f, 0x47, 0x4e, 0xd7, + 0x11, 0xd8, 0xf1, 0xf6, 0x6d, 0x61, 0x1d, 0x7e, 0x26, 0x95, 0xaf, 0x51, 0xea, 0x21, 0xbc, 0x83, + 0xf5, 0x1d, 0x3e, 0xf0, 0xa9, 0x54, 0x22, 0x58, 0xec, 0xdf, 0xa0, 0x3d, 0x38, 0x60, 0x93, 0x71, + 0x61, 0x9d, 0xce, 0xe8, 0x21, 0xa8, 0x95, 0x0d, 0xbf, 0xf8, 0x5f, 0xf3, 0xbe, 0x49, 0x3a, 0x3b, + 0x5e, 0x86, 0xf2, 0x3e, 0x9a, 0x88, 0x47, 0x6d, 0xf3, 0x3d, 0x16, 0x9c, 0x2f, 0x8e, 0x60, 0x8a, + 0x5d, 0x3e, 0x80, 0x3e, 0x7d, 0xc8, 0x4d, 0x44, 0xf8, 0x33, 0xd8, 0x64, 0x6f, 0xbe, 0x10, 0x69, + 0x01, 0xf4, 0x95, 0x36, 0x7e, 0xbf, 0xf1, 0x9b, 0x7b, 0x39, 0xaf, 0x18, 0x79, 0xef, 0x2f, 0xdf, + 0x78, 0x50, 0x9b, 0xff, 0x8a, 0x05, 0xe7, 0xf7, 0x35, 0xba, 0xe4, 0x98, 0x5f, 0x72, 0x66, 0x74, + 0xc9, 0xb1, 0x11, 0x85, 0xc7, 0x57, 0xae, 0xce, 0x4e, 0x85, 0x8f, 0xaf, 0x5c, 0x5d, 0x9e, 0x5a, + 0x79, 0x7c, 0xe5, 0xea, 0x9d, 0xa9, 0xbb, 0xb7, 0xfe, 0x51, 0xbb, 0xf0, 0x1c, 0x77, 0xd9, 0x24, + 0x75, 0x8e, 0x84, 0xbf, 0x00, 0x83, 0x1b, 0x2c, 0x8f, 0x34, 0xe1, 0xad, 0xdf, 0x7b, 0x63, 0x70, + 0x9b, 0x4d, 0xc4, 0x85, 0x31, 0x58, 0xe7, 0xc3, 0x15, 0x2f, 0x47, 0xd7, 0x4a, 0xe3, 0xf7, 0x68, + 0x0b, 0x6e, 0xb2, 0x31, 0xa9, 0x62, 0x43, 0x55, 0xef, 0xbf, 0x2e, 0xa3, 0xa1, 0x21, 0x58, 0x64, + 0x4c, 0x15, 0x99, 0x77, 0xb7, 0xfe, 0xdb, 0x31, 0x1a, 0x53, 0x45, 0x46, 0xbe, 0x76, 0xbd, 0xf1, + 0xd3, 0xbf, 0x97, 0x2e, 0xfd, 0x74, 0xba, 0x54, 0xfb, 0xf9, 0x74, 0xa9, 0xf6, 0xcb, 0xe9, 0x52, + 0xed, 0x5f, 0xa7, 0x4b, 0xb5, 0xbf, 0xbd, 0x5c, 0xba, 0xf4, 0xf3, 0xcb, 0xa5, 0x4b, 0xbf, 0xbc, + 0x5c, 0xba, 0xf4, 0xc3, 0xc4, 0x99, 0x10, 0xee, 0xbf, 0x45, 0x0d, 0xe4, 0xd3, 0xff, 0x04, 0x00, + 0x00, 0xff, 0xff, 0xd0, 0x11, 0x9c, 0xa0, 0xdf, 0x0f, 0x00, 0x00, } func (m *LocalOnlySessionData) Marshal() (dAtA []byte, err error) { @@ -690,18 +683,6 @@ func (m *LocalOnlySessionData) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0xa8 } - if m.CopyPartitioningWhenDeinterleavingTable { - i-- - if m.CopyPartitioningWhenDeinterleavingTable { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x2 - i-- - dAtA[i] = 0xa0 - } if len(m.ExperimentalComputedColumnRewrites) > 0 { i -= len(m.ExperimentalComputedColumnRewrites) copy(dAtA[i:], m.ExperimentalComputedColumnRewrites) @@ -1191,9 +1172,6 @@ func (m *LocalOnlySessionData) Size() (n int) { if l > 0 { n += 2 + l + sovLocalOnlySessionData(uint64(l)) } - if m.CopyPartitioningWhenDeinterleavingTable { - n += 3 - } if m.EnableStreamReplication { n += 3 } @@ -2009,26 +1987,6 @@ func (m *LocalOnlySessionData) Unmarshal(dAtA []byte) error { } m.ExperimentalComputedColumnRewrites = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 36: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CopyPartitioningWhenDeinterleavingTable", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLocalOnlySessionData - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.CopyPartitioningWhenDeinterleavingTable = bool(v != 0) case 37: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EnableStreamReplication", wireType) diff --git a/pkg/sql/sessiondatapb/local_only_session_data.proto b/pkg/sql/sessiondatapb/local_only_session_data.proto index 8c3be5b4f798..0373f20ab39c 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.proto +++ b/pkg/sql/sessiondatapb/local_only_session_data.proto @@ -133,11 +133,7 @@ message LocalOnlySessionData { // experimentalComputedColumnRewrites cluster setting for a description of the // format. string experimental_computed_column_rewrites = 35; - // CopyPartitioningWhenDeinterleavingTable indicates that when running an - // ALTER PRIMARY KEY that retains the same columns but removes any - // interleaving that zone configurations and partitioning from the root - // of that interleave should be applied to the new primary index. - bool copy_partitioning_when_deinterleaving_table = 36; + reserved 36; // EnableStreamReplication indicates whether to allow setting up a replication // stream. bool enable_stream_replication = 37; diff --git a/pkg/sql/show_create.go b/pkg/sql/show_create.go index 0f267b53ae6b..a7a942609b41 100644 --- a/pkg/sql/show_create.go +++ b/pkg/sql/show_create.go @@ -59,10 +59,9 @@ type ShowCreateDisplayOptions struct { // ShowCreateTable returns a valid SQL representation of the CREATE // TABLE statement used to create the given table. // -// The names of the tables references by foreign keys, and the -// interleaved parent if any, are prefixed by their own database name -// unless it is equal to the given dbPrefix. This allows us to elide -// the prefix when the given table references other tables in the +// The names of the tables referenced by foreign keys are prefixed by their own +// database name unless it is equal to the given dbPrefix. This allows us to +// elide the prefix when the given table references other tables in the // current database. func ShowCreateTable( ctx context.Context, @@ -134,61 +133,30 @@ func ShowCreateTable( return "", err } } - allIdx := append( - append([]catalog.Index{}, desc.PublicNonPrimaryIndexes()...), - desc.GetPrimaryIndex()) - for _, idx := range allIdx { - // Only add indexes to the create_statement column, and not to the - // create_nofks column if they are not associated with an INTERLEAVE - // statement. - // Initialize to false if Interleave has no ancestors, indicating that the - // index is not interleaved at all. - includeInterleaveClause := idx.NumInterleaveAncestors() == 0 - if displayOptions.FKDisplayMode != OmitFKClausesFromCreate { - // The caller is instructing us to not omit FK clauses from inside the CREATE. - // (i.e. the caller does not want them as separate DDL.) - // Since we're including FK clauses, we need to also include the PARTITION and INTERLEAVE - // clauses as well. - includeInterleaveClause = true + for _, idx := range desc.PublicNonPrimaryIndexes() { + // Showing the primary index is handled above. + + // Build the PARTITION BY clause. + var partitionBuf bytes.Buffer + if err := ShowCreatePartitioning( + a, p.ExecCfg().Codec, desc, idx, idx.GetPartitioning(), &partitionBuf, 1 /* indent */, 0, /* colOffset */ + ); err != nil { + return "", err } - if !idx.Primary() && includeInterleaveClause { - // Showing the primary index is handled above. - - // Build the PARTITION BY clause. - var partitionBuf bytes.Buffer - if err := ShowCreatePartitioning( - a, p.ExecCfg().Codec, desc, idx, idx.GetPartitioning(), &partitionBuf, 1 /* indent */, 0, /* colOffset */ - ); err != nil { - return "", err - } - - // Add interleave or Foreign Key indexes only to the create_table columns, - // and not the create_nofks column. - var interleaveBuf bytes.Buffer - if includeInterleaveClause { - // TODO(mgartner): The logic in showCreateInterleave can be - // moved to catformat.IndexForDisplay. - if err := showCreateInterleave(idx, &interleaveBuf, dbPrefix, lCtx); err != nil { - return "", err - } - } - - f.WriteString(",\n\t") - idxStr, err := catformat.IndexForDisplay( - ctx, - desc, - &descpb.AnonymousTable, - idx, - partitionBuf.String(), - interleaveBuf.String(), - p.RunParams(ctx).p.SemaCtx(), - ) - if err != nil { - return "", err - } - f.WriteString(idxStr) + f.WriteString(",\n\t") + idxStr, err := catformat.IndexForDisplay( + ctx, + desc, + &descpb.AnonymousTable, + idx, + partitionBuf.String(), + p.RunParams(ctx).p.SemaCtx(), + ) + if err != nil { + return "", err } + f.WriteString(idxStr) } // Create the FAMILY and CONSTRAINTs of the CREATE statement @@ -197,9 +165,6 @@ func ShowCreateTable( return "", err } - if err := showCreateInterleave(desc.GetPrimaryIndex(), &f.Buffer, dbPrefix, lCtx); err != nil { - return "", err - } if err := ShowCreatePartitioning( a, p.ExecCfg().Codec, desc, desc.GetPrimaryIndex(), desc.GetPrimaryIndex().GetPartitioning(), &f.Buffer, 0 /* indent */, 0, /* colOffset */ ); err != nil { diff --git a/pkg/sql/show_create_clauses.go b/pkg/sql/show_create_clauses.go index 646699ff0a9c..8eba73ec35d8 100644 --- a/pkg/sql/show_create_clauses.go +++ b/pkg/sql/show_create_clauses.go @@ -381,46 +381,6 @@ func showCreateLocality(desc catalog.TableDescriptor, f *tree.FmtCtx) error { return nil } -// showCreateInterleave returns an INTERLEAVE IN PARENT clause for the specified -// index, if applicable. -// -// The name of the parent table is prefixed by its database name unless -// it is equal to the given dbPrefix. This allows us to elide the prefix -// when the given index is interleaved in a table of the current database. -func showCreateInterleave( - idx catalog.Index, buf *bytes.Buffer, dbPrefix string, lCtx simpleSchemaResolver, -) error { - if idx.NumInterleaveAncestors() == 0 { - return nil - } - intl := idx.IndexDesc().Interleave - parentTableID := intl.Ancestors[len(intl.Ancestors)-1].TableID - var err error - var parentName tree.TableName - if lCtx != nil { - parentName, err = getParentAsTableName(lCtx, parentTableID, dbPrefix) - if err != nil { - return err - } - } else { - parentName = tree.MakeTableNameWithSchema(tree.Name(""), tree.PublicSchemaName, tree.Name(fmt.Sprintf("[%d as parent]", parentTableID))) - parentName.ExplicitCatalog = false - parentName.ExplicitSchema = false - } - var sharedPrefixLen int - for _, ancestor := range intl.Ancestors { - sharedPrefixLen += int(ancestor.SharedPrefixLen) - } - buf.WriteString(" INTERLEAVE IN PARENT ") - fmtCtx := tree.NewFmtCtx(tree.FmtSimple) - fmtCtx.FormatNode(&parentName) - buf.WriteString(fmtCtx.CloseAndGetString()) - buf.WriteString(" (") - formatQuoteNames(buf, idx.IndexDesc().KeyColumnNames[:sharedPrefixLen]...) - buf.WriteString(")") - return nil -} - // ShowCreatePartitioning returns a PARTITION BY clause for the specified // index, if applicable. func ShowCreatePartitioning( diff --git a/pkg/sql/span/span_builder.go b/pkg/sql/span/span_builder.go index 27175cbf796a..8498a7c96cee 100644 --- a/pkg/sql/span/span_builder.go +++ b/pkg/sql/span/span_builder.go @@ -84,24 +84,9 @@ func MakeBuilder( } // Set up the interstices for encoding interleaved tables later. + // + // TODO(yuzefovich): simplify this, interleaves are dead now. s.interstices[0] = s.KeyPrefix - if index.NumInterleaveAncestors() > 0 { - // TODO(rohany): too much of this code is copied from EncodePartialIndexKey. - sharedPrefixLen := 0 - for i := 0; i < index.NumInterleaveAncestors(); i++ { - ancestor := index.GetInterleaveAncestor(i) - // The first ancestor is already encoded in interstices[0]. - if i != 0 { - s.interstices[sharedPrefixLen] = rowenc.EncodePartialTableIDIndexID( - s.interstices[sharedPrefixLen], ancestor.TableID, ancestor.IndexID) - } - sharedPrefixLen += int(ancestor.SharedPrefixLen) - s.interstices[sharedPrefixLen] = encoding.EncodeInterleavedSentinel( - s.interstices[sharedPrefixLen]) - } - s.interstices[sharedPrefixLen] = rowenc.EncodePartialTableIDIndexID( - s.interstices[sharedPrefixLen], table.GetID(), index.GetID()) - } return s } diff --git a/pkg/sql/sqlinstance/instancestorage/row_codec.go b/pkg/sql/sqlinstance/instancestorage/row_codec.go index bec03699f019..2e3993dc5cbf 100644 --- a/pkg/sql/sqlinstance/instancestorage/row_codec.go +++ b/pkg/sql/sqlinstance/instancestorage/row_codec.go @@ -91,7 +91,7 @@ func (d *rowCodec) decodeRow( { types := []*types.T{tbl.PublicColumns()[0].GetType()} row := make([]rowenc.EncDatum, 1) - _, _, _, err := rowenc.DecodeIndexKey(d.codec, tbl, tbl.GetPrimaryIndex(), types, row, nil, kv.Key) + _, _, _, err := rowenc.DecodeIndexKey(d.codec, types, row, nil, kv.Key) if err != nil { return base.SQLInstanceID(0), "", "", hlc.Timestamp{}, false, errors.Wrap(err, "failed to decode key") } diff --git a/pkg/sql/stats/stats_cache.go b/pkg/sql/stats/stats_cache.go index 5a8443b01a49..bf6365cc102b 100644 --- a/pkg/sql/stats/stats_cache.go +++ b/pkg/sql/stats/stats_cache.go @@ -22,7 +22,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/systemschema" "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" "github.com/cockroachdb/cockroach/pkg/sql/rowenc" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" @@ -181,14 +180,11 @@ func NewTableStatisticsCache( func decodeTableStatisticsKV( codec keys.SQLCodec, kv *roachpb.RangeFeedValue, da *rowenc.DatumAlloc, ) (tableDesc descpb.ID, err error) { - tbl := systemschema.TableStatisticsTable // The primary key of table_statistics is (tableID INT, statisticID INT). types := []*types.T{types.Int, types.Int} dirs := []descpb.IndexDescriptor_Direction{descpb.IndexDescriptor_ASC, descpb.IndexDescriptor_ASC} keyVals := make([]rowenc.EncDatum, 2) - _, matches, _, err := rowenc.DecodeIndexKey( - codec, tbl, tbl.GetPrimaryIndex(), types, keyVals, dirs, kv.Key, - ) + _, matches, _, err := rowenc.DecodeIndexKey(codec, types, keyVals, dirs, kv.Key) if err != nil { return 0, err } diff --git a/pkg/sql/tablewriter_delete.go b/pkg/sql/tablewriter_delete.go index 3083ab4a6c57..f789e21282e5 100644 --- a/pkg/sql/tablewriter_delete.go +++ b/pkg/sql/tablewriter_delete.go @@ -17,12 +17,9 @@ import ( "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/sql/catalog" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/row" "github.com/cockroachdb/cockroach/pkg/sql/rowenc" - "github.com/cockroachdb/cockroach/pkg/sql/rowinfra" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" ) @@ -60,34 +57,21 @@ func (td *tableDeleter) row( } // deleteAllRows runs the kv operations necessary to delete all sql rows in the -// table passed at construction. This may require a scan. +// table passed at construction, using the DelRange KV request to delete data +// quickly. // // resume is the resume-span which should be used for the table deletion when // the table deletion is chunked. The first call to this method should use a // zero resume-span. After a chunk is deleted a new resume-span is returned. // -// limit is a limit on either the number of keys or table-rows (for -// interleaved tables) deleted in the operation. -func (td *tableDeleter) deleteAllRows( - ctx context.Context, resume roachpb.Span, limit int64, traceKV bool, -) (roachpb.Span, error) { - if td.tableDesc().IsInterleaved() { - log.VEvent(ctx, 2, "delete forced to scan: table is interleaved") - return td.deleteAllRowsScan(ctx, resume, limit, traceKV) - } - // TODO(pbardea): Is this ever called anymore? - return td.deleteAllRowsFast(ctx, resume, limit, traceKV) -} - -// deleteAllRowsFast uses the DelRange KV request to delete data quickly, -// relative to deleteAllRowsScan. +// limit is a limit on the number of keys deleted in the operation. // // Note that this method leaves a RocksDB deletion tombstone on every key in the // table, resulting in substantial write amplification. When possible, the // schema changer avoids using a tableDeleter entirely in favor of the // ClearRange KV request, which uses RocksDB range deletion tombstones to avoid // write amplification. -func (td *tableDeleter) deleteAllRowsFast( +func (td *tableDeleter) deleteAllRows( ctx context.Context, resume roachpb.Span, limit int64, traceKV bool, ) (roachpb.Span, error) { if resume.Key == nil { @@ -111,81 +95,8 @@ func (td *tableDeleter) deleteAllRowsFast( return td.b.Results[0].ResumeSpanAsValue(), nil } -func (td *tableDeleter) deleteAllRowsScan( - ctx context.Context, resume roachpb.Span, limit int64, traceKV bool, -) (roachpb.Span, error) { - if resume.Key == nil { - resume = td.tableDesc().PrimaryIndexSpan(td.rd.Helper.Codec) - } - - var valNeededForCol util.FastIntSet - for i := range td.rd.FetchCols { - col := td.rd.FetchCols[i].GetID() - valNeededForCol.Add(td.rd.FetchColIDtoRowIndex.GetDefault(col)) - } - - var rf row.Fetcher - tableArgs := row.FetcherTableArgs{ - Desc: td.tableDesc(), - Index: td.tableDesc().GetPrimaryIndex(), - ColIdxMap: td.rd.FetchColIDtoRowIndex, - Cols: td.rd.FetchCols, - ValNeededForCol: valNeededForCol, - } - if err := rf.Init( - ctx, - td.rd.Helper.Codec, - false, /* reverse */ - // TODO(nvanbenschoten): it might make sense to use a FOR_UPDATE locking - // strength here. Consider hooking this in to the same knob that will - // control whether we perform locking implicitly during DELETEs. - descpb.ScanLockingStrength_FOR_NONE, - descpb.ScanLockingWaitPolicy_BLOCK, - td.lockTimeout, - false, /* isCheck */ - td.alloc, - // TODO(bulkio): this might need a memory monitor for the slow case of truncate. - nil, /* memMonitor */ - tableArgs, - ); err != nil { - return resume, err - } - if err := rf.StartScan( - ctx, td.txn, roachpb.Spans{resume}, rowinfra.DefaultBatchBytesLimit, rowinfra.NoRowLimit, traceKV, td.forceProductionBatchSizes, - ); err != nil { - return resume, err - } - - for i := int64(0); i < limit; i++ { - datums, _, _, err := rf.NextRowDecoded(ctx) - if err != nil { - return resume, err - } - if datums == nil { - // Done deleting all rows. - resume = roachpb.Span{} - break - } - // An empty PartialIndexUpdateHelper is passed here, meaning that DEL - // operations will be issued for every partial index, regardless of - // whether or not the row is indexed by the partial index. - // TODO(mgartner): Try evaluating each partial index predicate - // expression for each row and benchmark to determine if it is faster - // than simply issuing DEL operations for entries that do not exist. - var pm row.PartialIndexUpdateHelper - if err = td.row(ctx, datums, pm, traceKV); err != nil { - return resume, err - } - } - if resume.Key != nil { - // Update the resume start key for the next iteration. - resume.Key = rf.Key() - } - return resume, td.finalize(ctx) -} - // deleteIndex runs the kv operations necessary to delete all kv entries in the -// given index. This may require a scan. +// given index. // // resume is the resume-span which should be used for the index deletion // when the index deletion is chunked. The first call to this method should @@ -195,18 +106,6 @@ func (td *tableDeleter) deleteAllRowsScan( // limit is a limit on the number of index entries deleted in the operation. func (td *tableDeleter) deleteIndex( ctx context.Context, idx catalog.Index, resume roachpb.Span, limit int64, traceKV bool, -) (roachpb.Span, error) { - if idx.IsInterleaved() { - if log.V(2) { - log.Info(ctx, "delete forced to scan: table is interleaved") - } - return td.deleteIndexScan(ctx, idx, resume, limit, traceKV) - } - return td.deleteIndexFast(ctx, idx, resume, limit, traceKV) -} - -func (td *tableDeleter) deleteIndexFast( - ctx context.Context, idx catalog.Index, resume roachpb.Span, limit int64, traceKV bool, ) (roachpb.Span, error) { if resume.Key == nil { resume = td.tableDesc().IndexSpan(td.rd.Helper.Codec, idx.GetID()) @@ -227,10 +126,6 @@ func (td *tableDeleter) deleteIndexFast( } func (td *tableDeleter) clearIndex(ctx context.Context, idx catalog.Index) error { - if idx.IsInterleaved() { - return errors.Errorf("unexpected interleaved index %d", idx.GetID()) - } - sp := td.tableDesc().IndexSpan(td.rd.Helper.Codec, idx.GetID()) // ClearRange cannot be run in a transaction, so create a @@ -248,72 +143,6 @@ func (td *tableDeleter) clearIndex(ctx context.Context, idx catalog.Index) error return td.txn.DB().Run(ctx, b) } -func (td *tableDeleter) deleteIndexScan( - ctx context.Context, idx catalog.Index, resume roachpb.Span, limit int64, traceKV bool, -) (roachpb.Span, error) { - if resume.Key == nil { - resume = td.tableDesc().PrimaryIndexSpan(td.rd.Helper.Codec) - } - - var valNeededForCol util.FastIntSet - for i := range td.rd.FetchCols { - col := td.rd.FetchCols[i].GetID() - valNeededForCol.Add(td.rd.FetchColIDtoRowIndex.GetDefault(col)) - } - - var rf row.Fetcher - tableArgs := row.FetcherTableArgs{ - Desc: td.tableDesc(), - Index: td.tableDesc().GetPrimaryIndex(), - ColIdxMap: td.rd.FetchColIDtoRowIndex, - Cols: td.rd.FetchCols, - ValNeededForCol: valNeededForCol, - } - if err := rf.Init( - ctx, - td.rd.Helper.Codec, - false, /* reverse */ - // TODO(nvanbenschoten): it might make sense to use a FOR_UPDATE locking - // strength here. Consider hooking this in to the same knob that will - // control whether we perform locking implicitly during DELETEs. - descpb.ScanLockingStrength_FOR_NONE, - descpb.ScanLockingWaitPolicy_BLOCK, - td.lockTimeout, - false, /* isCheck */ - td.alloc, - // TODO(bulkio): this might need a memory monitor. - nil, /* memMonitor */ - tableArgs, - ); err != nil { - return resume, err - } - if err := rf.StartScan( - ctx, td.txn, roachpb.Spans{resume}, rowinfra.DefaultBatchBytesLimit, rowinfra.NoRowLimit, traceKV, td.forceProductionBatchSizes, - ); err != nil { - return resume, err - } - - for i := int64(0); i < limit; i++ { - datums, _, _, err := rf.NextRowDecoded(ctx) - if err != nil { - return resume, err - } - if datums == nil { - // Done deleting all rows. - resume = roachpb.Span{} - break - } - if err := td.rd.DeleteIndexRow(ctx, td.b, idx, datums, traceKV); err != nil { - return resume, err - } - } - if resume.Key != nil { - // Update the resume start key for the next iteration. - resume.Key = rf.Key() - } - return resume, td.finalize(ctx) -} - func (td *tableDeleter) tableDesc() catalog.TableDescriptor { return td.rd.Helper.TableDesc } diff --git a/pkg/sql/truncate.go b/pkg/sql/truncate.go index 761047762801..6e84b9c6682c 100644 --- a/pkg/sql/truncate.go +++ b/pkg/sql/truncate.go @@ -35,7 +35,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/log/eventpb" - "github.com/cockroachdb/cockroach/pkg/util/protoutil" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" ) @@ -64,24 +63,6 @@ func (t *truncateNode) startExec(params runParams) error { // while constructing the list of tables that should be truncated. toTraverse := make([]tabledesc.Mutable, 0, len(n.Tables)) - // Collect copies of each interleaved descriptor being truncated before any - // modification has been done to them. We need this in order to truncate - // the interleaved indexes in the GC job. We have to collect these descriptors - // before the truncate modifications to know what are the correct index spans - // to delete. Once changes have been made, the index spans where k/v data for - // the table reside are no longer accessible from the table. - interleaveCopies := make(map[descpb.ID]*descpb.TableDescriptor) - maybeAddInterleave := func(desc catalog.TableDescriptor) { - if !desc.IsInterleaved() { - return - } - _, ok := interleaveCopies[desc.GetID()] - if ok { - return - } - interleaveCopies[desc.GetID()] = protoutil.Clone(desc.TableDesc()).(*descpb.TableDescriptor) - } - for i := range n.Tables { tn := &n.Tables[i] _, tableDesc, err := p.ResolveMutableTableDescriptor( @@ -96,7 +77,6 @@ func (t *truncateNode) startExec(params runParams) error { toTruncate[tableDesc.ID] = tn.FQString() toTraverse = append(toTraverse, *tableDesc) - maybeAddInterleave(tableDesc) } // Check that any referencing tables are contained in the set, or, if CASCADE @@ -129,7 +109,6 @@ func (t *truncateNode) startExec(params runParams) error { } toTruncate[other.ID] = otherName.FQString() toTraverse = append(toTraverse, *other) - maybeAddInterleave(other) return nil } @@ -139,14 +118,6 @@ func (t *truncateNode) startExec(params runParams) error { return err } } - for _, idx := range tableDesc.NonDropIndexes() { - for i := 0; i < idx.NumInterleavedBy(); i++ { - ref := idx.GetInterleavedBy(i) - if err := maybeEnqueue(ref.Table, "interleaved by"); err != nil { - return err - } - } - } } // Mark this query as non-cancellable if autocommitting. @@ -155,7 +126,7 @@ func (t *truncateNode) startExec(params runParams) error { } for id, name := range toTruncate { - if err := p.truncateTable(ctx, id, interleaveCopies, tree.AsStringWithFQNames(t.n, params.Ann())); err != nil { + if err := p.truncateTable(ctx, id, tree.AsStringWithFQNames(t.n, params.Ann())); err != nil { return err } @@ -190,15 +161,8 @@ var PreservedSplitCountMultiple = settings.RegisterIntSetting( // truncateTable truncates the data of a table in a single transaction. It does // so by dropping all existing indexes on the table and creating new ones without // backfilling any data into the new indexes. The old indexes are cleaned up -// asynchronously by the SchemaChangeGCJob. interleaveDescs is a set of -// interleaved TableDescriptors being truncated before any of the truncate -// mutations have been applied. -func (p *planner) truncateTable( - ctx context.Context, - id descpb.ID, - interleaveDescs map[descpb.ID]*descpb.TableDescriptor, - jobDesc string, -) error { +// asynchronously by the SchemaChangeGCJob. +func (p *planner) truncateTable(ctx context.Context, id descpb.ID, jobDesc string) error { // Read the table descriptor because it might have changed // while another table in the truncation list was truncated. tableDesc, err := p.Descriptors().GetMutableTableVersionByID(ctx, id, p.txn) @@ -206,12 +170,6 @@ func (p *planner) truncateTable( return err } - // Get all tables that might reference this one. - allRefs, err := p.findAllReferencingInterleaves(ctx, tableDesc) - if err != nil { - return err - } - if err := checkTableForDisallowedMutationsWithTruncate(tableDesc); err != nil { return err } @@ -263,28 +221,18 @@ func (p *planner) truncateTable( // Create schema change GC jobs for all of the indexes. dropTime := timeutil.Now().UnixNano() droppedIndexes := make([]jobspb.SchemaChangeGCDetails_DroppedIndex, 0, len(oldIndexes)) - var droppedInterleaves []descpb.IndexDescriptor for i := range oldIndexes { idx := oldIndexes[i] - if idx.IsInterleaved() { - droppedInterleaves = append(droppedInterleaves, idx) - } else { - droppedIndexes = append(droppedIndexes, jobspb.SchemaChangeGCDetails_DroppedIndex{ - IndexID: idx.ID, - DropTime: dropTime, - }) - } + droppedIndexes = append(droppedIndexes, jobspb.SchemaChangeGCDetails_DroppedIndex{ + IndexID: idx.ID, + DropTime: dropTime, + }) } details := jobspb.SchemaChangeGCDetails{ Indexes: droppedIndexes, ParentID: tableDesc.ID, } - // If we have any interleaved indexes, add that information to the job record. - if len(droppedInterleaves) > 0 { - details.InterleavedTable = interleaveDescs[tableDesc.ID] - details.InterleavedIndexes = droppedInterleaves - } record := CreateGCJobRecord(jobDesc, p.User(), details) if _, err := p.ExecCfg().JobRegistry.CreateAdoptableJobWithTxn( ctx, record, p.ExecCfg().JobRegistry.MakeJobID(), p.txn); err != nil { @@ -315,20 +263,6 @@ func (p *planner) truncateTable( return err } - // Reassign any referenced index ID's from other tables. - if err := p.reassignInterleaveIndexReferences(ctx, allRefs, tableDesc.ID, indexIDMapping); err != nil { - return err - } - // Reassign any self references. - if err := p.reassignInterleaveIndexReferences( - ctx, - []*tabledesc.Mutable{tableDesc}, - tableDesc.ID, - indexIDMapping, - ); err != nil { - return err - } - // Move any zone configs on indexes over to the new set of indexes. swapInfo := &descpb.PrimaryKeySwap{ OldPrimaryIndexId: oldIndexIDs[0], @@ -465,30 +399,6 @@ func ClearTableDataInChunks( return nil } -// findAllReferencingInterleaves finds all tables that might interleave or -// be interleaved by the input table. -func (p *planner) findAllReferencingInterleaves( - ctx context.Context, table *tabledesc.Mutable, -) ([]*tabledesc.Mutable, error) { - refs, err := table.FindAllReferences() - if err != nil { - return nil, err - } - tables := make([]*tabledesc.Mutable, 0, len(refs)) - for id := range refs { - if id == table.ID { - continue - } - t, err := p.Descriptors().GetMutableTableVersionByID(ctx, id, p.txn) - if err != nil { - return nil, err - } - tables = append(tables, t) - } - - return tables, nil -} - // copySplitPointsToNewIndexes copies any range split points from the indexes // given by the oldIndexIDs slice to the indexes given by the newIndexIDs slice // on the table given by the tableID. @@ -638,43 +548,6 @@ func (p *planner) copySplitPointsToNewIndexes( return p.txn.DB().Run(ctx, &b) } -// reassignInterleaveIndexReferences reassigns all index ID's present in -// interleave descriptor references according to indexIDMapping. -func (p *planner) reassignInterleaveIndexReferences( - ctx context.Context, - tables []*tabledesc.Mutable, - truncatedID descpb.ID, - indexIDMapping map[descpb.IndexID]descpb.IndexID, -) error { - for _, table := range tables { - changed := false - if err := catalog.ForEachNonDropIndex(table, func(indexI catalog.Index) error { - index := indexI.IndexDesc() - for j, a := range index.Interleave.Ancestors { - if a.TableID == truncatedID { - index.Interleave.Ancestors[j].IndexID = indexIDMapping[index.Interleave.Ancestors[j].IndexID] - changed = true - } - } - for j, c := range index.InterleavedBy { - if c.Table == truncatedID { - index.InterleavedBy[j].Index = indexIDMapping[index.InterleavedBy[j].Index] - changed = true - } - } - return nil - }); err != nil { - return err - } - if changed { - if err := p.writeSchemaChange(ctx, table, descpb.InvalidMutationID, "updating reference for truncated table"); err != nil { - return err - } - } - } - return nil -} - func (p *planner) reassignIndexComments( ctx context.Context, table *tabledesc.Mutable, indexIDMapping map[descpb.IndexID]descpb.IndexID, ) error { @@ -713,11 +586,3 @@ func (p *planner) reassignIndexComments( } return nil } - -// canClearRangeForDrop returns if an index can be deleted by deleting every -// key from a single span. -// This determines whether an index is dropped during a schema change, or if -// it is only deleted upon GC. -func canClearRangeForDrop(index catalog.Index) bool { - return !index.IsInterleaved() -} diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index b61c49535e6c..a5aa24af37cd 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -1634,24 +1634,6 @@ var varGen = map[string]sessionVar{ }, }, - `enable_copying_partitioning_when_deinterleaving_table`: { - GetStringVal: makePostgresBoolGetStringValFn(`enable_experimental_stream_replication`), - Set: func(_ context.Context, m sessionDataMutator, s string) error { - b, err := paramparse.ParseBoolVar(`enable_experimental_stream_replication`, s) - if err != nil { - return err - } - m.SetCopyPartitioningWhenDeinterleavingTable(b) - return nil - }, - Get: func(evalCtx *extendedEvalContext) (string, error) { - return formatBoolAsPostgresSetting(evalCtx.SessionData().CopyPartitioningWhenDeinterleavingTable), nil - }, - GlobalDefault: func(sv *settings.Values) string { - return formatBoolAsPostgresSetting(copyPartitioningWhenDeinterleavingTable.Get(sv)) - }, - }, - `null_ordered_last`: { GetStringVal: makePostgresBoolGetStringValFn(`null_ordered_last`), Set: func(_ context.Context, m sessionDataMutator, s string) error { diff --git a/pkg/testutils/sqlutils/table_gen.go b/pkg/testutils/sqlutils/table_gen.go index 762a333f83fe..7fe710536cb2 100644 --- a/pkg/testutils/sqlutils/table_gen.go +++ b/pkg/testutils/sqlutils/table_gen.go @@ -57,7 +57,7 @@ func genValues(w io.Writer, firstRow, lastRow int, fn GenRowFn, shouldPrint bool func CreateTable( tb testing.TB, sqlDB *gosql.DB, tableName, schema string, numRows int, fn GenRowFn, ) { - CreateTableInterleaved(tb, sqlDB, tableName, schema, "" /*interleaveSchema*/, numRows, fn) + CreateTableDebug(tb, sqlDB, tableName, schema, numRows, fn, false /* print */) } // CreateTableDebug is identical to debug, but allows for the added option of @@ -70,38 +70,9 @@ func CreateTableDebug( fn GenRowFn, shouldPrint bool, ) { - CreateTableInterleavedDebug(tb, sqlDB, tableName, schema, "" /*interleaveSchema*/, numRows, fn, shouldPrint) -} - -// CreateTableInterleaved is identical to CreateTable with the added option -// of specifying an interleave schema for interleaving the table. -func CreateTableInterleaved( - tb testing.TB, - sqlDB *gosql.DB, - tableName, schema, interleaveSchema string, - numRows int, - fn GenRowFn, -) { - CreateTableInterleavedDebug(tb, sqlDB, tableName, schema, interleaveSchema, numRows, fn, false /* print */) -} - -// CreateTableInterleavedDebug is identical to CreateTableInterleaved with the -// option of printing the table being created. -func CreateTableInterleavedDebug( - tb testing.TB, - sqlDB *gosql.DB, - tableName, schema, interleaveSchema string, - numRows int, - fn GenRowFn, - shouldPrint bool, -) { - if interleaveSchema != "" { - interleaveSchema = fmt.Sprintf(`INTERLEAVE IN PARENT %s.%s`, TestDB, interleaveSchema) - } - r := MakeSQLRunner(sqlDB) stmt := fmt.Sprintf(`CREATE DATABASE IF NOT EXISTS %s;`, TestDB) - stmt += fmt.Sprintf(`CREATE TABLE %s.%s (%s) %s;`, TestDB, tableName, schema, interleaveSchema) + stmt += fmt.Sprintf(`CREATE TABLE %s.%s (%s);`, TestDB, tableName, schema) r.Exec(tb, stmt) if shouldPrint { fmt.Printf("Creating table: %s\n%s\n", tableName, schema) diff --git a/pkg/workload/ledger/ledger.go b/pkg/workload/ledger/ledger.go index 1e68d06b3a54..1bd1eabb8543 100644 --- a/pkg/workload/ledger/ledger.go +++ b/pkg/workload/ledger/ledger.go @@ -21,7 +21,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/cockroach/pkg/workload" "github.com/cockroachdb/cockroach/pkg/workload/histogram" - "github.com/cockroachdb/errors" "github.com/spf13/pflag" ) @@ -31,7 +30,6 @@ type ledger struct { seed int64 customers int - interleaved bool inlineArgs bool splits int fks bool @@ -60,7 +58,6 @@ var ledgerMeta = workload.Meta{ g.connFlags = workload.NewConnFlags(&g.flags) g.flags.Int64Var(&g.seed, `seed`, 1, `Random number generator seed`) g.flags.IntVar(&g.customers, `customers`, 1000, `Number of customers`) - g.flags.BoolVar(&g.interleaved, `interleaved`, false, `Use interleaved tables`) g.flags.BoolVar(&g.inlineArgs, `inline-args`, false, `Use inline query arguments`) g.flags.IntVar(&g.splits, `splits`, 0, `Number of splits to perform before starting normal operations`) g.flags.BoolVar(&g.fks, `fks`, true, `Add the foreign keys`) @@ -87,9 +84,6 @@ func (w *ledger) Flags() workload.Flags { return w.flags } func (w *ledger) Hooks() workload.Hooks { return workload.Hooks{ Validate: func() error { - if w.interleaved { - return errors.Errorf("interleaved tables are not yet supported") - } return initializeMix(w) }, PostLoad: func(sqlDB *gosql.DB) error { diff --git a/pkg/workload/tpcc/ddls.go b/pkg/workload/tpcc/ddls.go index ec0e1d0ed686..b52afdde9974 100644 --- a/pkg/workload/tpcc/ddls.go +++ b/pkg/workload/tpcc/ddls.go @@ -53,8 +53,6 @@ const ( family static (d_w_id, d_id, d_name, d_street_1, d_street_2, d_city, d_state, d_zip), family dynamic_1 (d_ytd), family dynamic_2 (d_next_o_id, d_tax)` - tpccDistrictSchemaInterleaveSuffix = ` - interleave in parent warehouse (d_w_id)` // CUSTOMER table. tpccCustomerSchemaBase = `( @@ -87,8 +85,6 @@ const ( c_city, c_state, c_zip, c_phone, c_since, c_credit, c_credit_lim, c_discount ), family dynamic (c_balance, c_ytd_payment, c_payment_cnt, c_data, c_delivery_cnt)` - tpccCustomerSchemaInterleaveSuffix = ` - interleave in parent district (c_w_id, c_d_id)` // HISTORY table. tpccHistorySchemaBase = `( @@ -119,8 +115,6 @@ const ( primary key (o_w_id, o_d_id, o_id DESC), unique index order_idx (o_w_id, o_d_id, o_c_id, o_id DESC) storing (o_entry_d, o_carrier_id) ` - tpccOrderSchemaInterleaveSuffix = ` - interleave in parent district (o_w_id, o_d_id)` // NEW-ORDER table. tpccNewOrderSchema = `( @@ -129,11 +123,6 @@ const ( no_w_id integer not null, primary key (no_w_id, no_d_id, no_o_id) ` - // This natural-seeming interleave makes performance worse, because this - // table has a ton of churn and produces a lot of MVCC tombstones, which - // then will gum up the works of scans over the parent table. - // tpccNewOrderSchemaInterleaveSuffix = ` - // interleave in parent "order" (no_w_id, no_d_id, no_o_id)` // ITEM table. tpccItemSchema = `( @@ -167,8 +156,6 @@ const ( primary key (s_w_id, s_i_id)` deprecatedTpccStockSchemaFkSuffix = ` index stock_item_fk_idx (s_i_id)` - tpccStockSchemaInterleaveSuffix = ` - interleave in parent warehouse (s_w_id)` // ORDER-LINE table. tpccOrderLineSchemaBase = `( @@ -185,8 +172,6 @@ const ( primary key (ol_w_id, ol_d_id, ol_o_id DESC, ol_number)` deprecatedTpccOrderLineSchemaFkSuffix = ` index order_line_stock_fk_idx (ol_supply_w_id, ol_i_id)` - tpccOrderLineSchemaInterleaveSuffix = ` - interleave in parent "order" (ol_w_id, ol_d_id, ol_o_id)` localityRegionalByRowSuffix = ` locality regional by row` @@ -197,11 +182,10 @@ const ( ) type schemaOptions struct { - fkClause string - familyClause string - columnClause string - localityClause string - interleaveClause string + fkClause string + familyClause string + columnClause string + localityClause string } type makeSchemaOption func(o *schemaOptions) @@ -222,14 +206,6 @@ func maybeAddColumnFamiliesSuffix(separateColumnFamilies bool, suffix string) ma } } -func maybeAddInterleaveSuffix(interleave bool, suffix string) makeSchemaOption { - return func(o *schemaOptions) { - if interleave { - o.interleaveClause = suffix - } - } -} - func maybeAddLocalityRegionalByRow( multiRegionCfg multiRegionConfig, partColName string, ) makeSchemaOption { @@ -271,9 +247,6 @@ func makeSchema(base string, opts ...makeSchemaOption) string { if o.columnClause != "" { ret += "," + o.columnClause } - if o.interleaveClause != "" { - ret += "," + o.interleaveClause - } ret += endSchema if o.localityClause != "" { ret += o.localityClause diff --git a/pkg/workload/tpcc/tpcc.go b/pkg/workload/tpcc/tpcc.go index cf602ff43472..6ea0c7ffc11f 100644 --- a/pkg/workload/tpcc/tpcc.go +++ b/pkg/workload/tpcc/tpcc.go @@ -40,7 +40,6 @@ type tpcc struct { seed uint64 warehouses int activeWarehouses int - interleaved bool nowString []byte numConns int @@ -184,10 +183,6 @@ var tpccMeta = workload.Meta{ g.flags.IntVar(&g.warehouses, `warehouses`, 1, `Number of warehouses for loading`) g.flags.BoolVar(&g.fks, `fks`, true, `Add the foreign keys`) g.flags.BoolVar(&g.deprecatedFkIndexes, `deprecated-fk-indexes`, false, `Add deprecated foreign keys (needed when running against v20.1 or below clusters)`) - g.flags.BoolVar(&g.interleaved, `interleaved`, false, `Use interleaved tables`) - if err := g.Flags().MarkHidden("interleaved"); err != nil { - panic(errors.Wrap(err, "no interleaved flag?")) - } g.flags.StringVar(&g.mix, `mix`, `newOrder=10,payment=10,orderStatus=1,delivery=1,stockLevel=1`, @@ -595,10 +590,6 @@ func (w *tpcc) Tables() []workload.Table { w.separateColumnFamilies, tpccDistrictColumnFamiliesSuffix, ), - maybeAddInterleaveSuffix( - w.interleaved, - tpccDistrictSchemaInterleaveSuffix, - ), maybeAddLocalityRegionalByRow(w.multiRegionCfg, `d_w_id`), ), InitialRows: workload.BatchedTuples{ @@ -617,10 +608,6 @@ func (w *tpcc) Tables() []workload.Table { Name: `customer`, Schema: makeSchema( tpccCustomerSchemaBase, - maybeAddInterleaveSuffix( - w.interleaved, - tpccCustomerSchemaInterleaveSuffix, - ), maybeAddColumnFamiliesSuffix( w.separateColumnFamilies, tpccCustomerColumnFamiliesSuffix, @@ -659,10 +646,6 @@ func (w *tpcc) Tables() []workload.Table { Name: `order`, Schema: makeSchema( tpccOrderSchemaBase, - maybeAddInterleaveSuffix( - w.interleaved, - tpccOrderSchemaInterleaveSuffix, - ), maybeAddLocalityRegionalByRow(w.multiRegionCfg, `o_w_id`), ), InitialRows: workload.BatchedTuples{ @@ -704,10 +687,6 @@ func (w *tpcc) Tables() []workload.Table { Name: `stock`, Schema: makeSchema( tpccStockSchemaBase, - maybeAddInterleaveSuffix( - w.interleaved, - tpccStockSchemaInterleaveSuffix, - ), maybeAddFkSuffix( w.deprecatedFkIndexes, deprecatedTpccStockSchemaFkSuffix, @@ -724,10 +703,6 @@ func (w *tpcc) Tables() []workload.Table { Name: `order_line`, Schema: makeSchema( tpccOrderLineSchemaBase, - maybeAddInterleaveSuffix( - w.interleaved, - tpccOrderLineSchemaInterleaveSuffix, - ), maybeAddFkSuffix( w.deprecatedFkIndexes, deprecatedTpccOrderLineSchemaFkSuffix, From 4c0a5c708aa80dfcca5439aec186c200649402f3 Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Thu, 23 Sep 2021 11:23:06 -0400 Subject: [PATCH 196/205] sql: remove INTERLEAVE IN PARENT from grammar This commit follows up on the previous one by removing INTERLEAVE clauses from the SQL grammar itself. Release note (sql change): Removed INTERLEAVE IN PARENT. --- docs/generated/sql/bnf/BUILD.bazel | 3 - docs/generated/sql/bnf/alter_primary_key.bnf | 8 +- docs/generated/sql/bnf/alter_table.bnf | 4 +- .../sql/bnf/alter_table_partition_by.bnf | 4 +- .../sql/bnf/create_index_interleaved_stmt.bnf | 17 -- docs/generated/sql/bnf/create_index_stmt.bnf | 4 +- .../sql/bnf/create_inverted_index_stmt.bnf | 8 +- docs/generated/sql/bnf/create_table_stmt.bnf | 4 +- docs/generated/sql/bnf/index_def.bnf | 32 ++-- docs/generated/sql/bnf/interleave.bnf | 5 - docs/generated/sql/bnf/opt_interleave.bnf | 2 - docs/generated/sql/bnf/stmt_block.bnf | 27 ++-- docs/generated/sql/bnf/table_constraint.bnf | 24 +-- docs/tech-notes/encoding.md | 73 --------- pkg/ccl/importccl/import_table_creation.go | 3 - pkg/ccl/importccl/read_import_pgdump.go | 1 - .../testdata/logic_test/partitioning | 19 --- .../testdata/logic_test/regional_by_row | 16 -- pkg/cli/testdata/dump/interleave_index | 121 -------------- pkg/cmd/cr2pg/main.go | 1 - pkg/cmd/docgen/diagrams.go | 31 ---- pkg/sql/alter_primary_key.go | 8 - pkg/sql/alter_table.go | 7 +- pkg/sql/create_index.go | 23 --- pkg/sql/create_table.go | 31 ---- .../opttester/testfixtures/tpcc_schema | 4 +- pkg/sql/opt/testutils/testcat/create_index.go | 1 - pkg/sql/parser/sql.y | 147 ++++++------------ pkg/sql/parser/testdata/alter_table | 16 -- pkg/sql/parser/testdata/create_index | 40 ----- pkg/sql/parser/testdata/create_table | 49 ------ pkg/sql/randgen/mutator.go | 12 -- pkg/sql/sem/tree/alter_table.go | 10 +- pkg/sql/sem/tree/create.go | 41 ----- pkg/sql/sem/tree/pretty.go | 29 +--- .../pretty/20.align-deindent.golden.short | 3 +- .../pretty/20.align-only.golden.short | 3 +- .../tree/testdata/pretty/20.ref.golden.short | 1 - pkg/sql/sem/tree/testdata/pretty/20.sql | 2 +- ...ate_interleaved_drop.align-deindent.golden | 50 ------ ...terleaved_drop.align-deindent.golden.short | 12 -- .../create_interleaved_drop.align-only.golden | 50 ------ ...e_interleaved_drop.align-only.golden.short | 12 -- .../pretty/create_interleaved_drop.ref.golden | 41 ----- .../create_interleaved_drop.ref.golden.short | 11 -- .../pretty/create_interleaved_drop.sql | 1 - pkg/sql/sqltelemetry/schema.go | 4 - pkg/sql/testdata/telemetry/error | 8 - 48 files changed, 112 insertions(+), 911 deletions(-) delete mode 100644 docs/generated/sql/bnf/create_index_interleaved_stmt.bnf delete mode 100644 docs/generated/sql/bnf/interleave.bnf delete mode 100644 docs/generated/sql/bnf/opt_interleave.bnf delete mode 100644 pkg/cli/testdata/dump/interleave_index delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.sql diff --git a/docs/generated/sql/bnf/BUILD.bazel b/docs/generated/sql/bnf/BUILD.bazel index 622d8d443f04..41413203ce8e 100644 --- a/docs/generated/sql/bnf/BUILD.bazel +++ b/docs/generated/sql/bnf/BUILD.bazel @@ -67,7 +67,6 @@ FILES = [ "create_database_stmt", "create_ddl_stmt", "create_extension_stmt", - "create_index_interleaved_stmt", "create_index_stmt", "create_inverted_index_stmt", "create_replication_stream_stmt", @@ -117,7 +116,6 @@ FILES = [ "index_def", "insert_rest", "insert_stmt", - "interleave", "iso_level", "joined_table", "like_table_option_list", @@ -126,7 +124,6 @@ FILES = [ "offset_clause", "on_conflict", "opt_frame_clause", - "opt_interleave", "opt_locality", "opt_persistence_temp_table", "opt_with_storage_parameter_list", diff --git a/docs/generated/sql/bnf/alter_primary_key.bnf b/docs/generated/sql/bnf/alter_primary_key.bnf index 4a82c0d74dd9..68801c218748 100644 --- a/docs/generated/sql/bnf/alter_primary_key.bnf +++ b/docs/generated/sql/bnf/alter_primary_key.bnf @@ -1,5 +1,5 @@ alter_onetable_stmt ::= - 'ALTER' 'TABLE' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave - | 'ALTER' 'TABLE' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_interleave - | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave - | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_interleave + 'ALTER' 'TABLE' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets + | 'ALTER' 'TABLE' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' + | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets + | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' diff --git a/docs/generated/sql/bnf/alter_table.bnf b/docs/generated/sql/bnf/alter_table.bnf index 21647c06028d..d61ecaed55df 100644 --- a/docs/generated/sql/bnf/alter_table.bnf +++ b/docs/generated/sql/bnf/alter_table.bnf @@ -1,3 +1,3 @@ alter_onetable_stmt ::= - 'ALTER' 'TABLE' table_name ( ( ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) ( ( ',' ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) )* ) - | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name ( ( ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) ( ( ',' ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) )* ) + 'ALTER' 'TABLE' table_name ( ( ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) ( ( ',' ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) )* ) + | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name ( ( ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) ( ( ',' ( 'RENAME' ( 'COLUMN' | ) column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' ( column_name typename col_qual_list ) | 'ADD' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' ( column_name typename col_qual_list ) | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' ( column_name typename col_qual_list ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' ) | 'ALTER' ( 'COLUMN' | ) column_name alter_column_on_update | 'ALTER' ( 'COLUMN' | ) column_name alter_column_visible | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'NOT' 'NULL' | 'ALTER' ( 'COLUMN' | ) column_name 'DROP' 'STORED' | 'ALTER' ( 'COLUMN' | ) column_name 'SET' 'NOT' 'NULL' | 'DROP' ( 'COLUMN' | ) 'IF' 'EXISTS' column_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' ( 'COLUMN' | ) column_name ( 'CASCADE' | 'RESTRICT' | ) | 'ALTER' ( 'COLUMN' | ) column_name ( 'SET' 'DATA' | ) 'TYPE' typename ( 'COLLATE' collation_name | ) ( 'USING' a_expr | ) | 'ADD' ( 'CONSTRAINT' constraint_name constraint_elem | constraint_elem ) | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'DROP' 'CONSTRAINT' constraint_name ( 'CASCADE' | 'RESTRICT' | ) | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | partition_by_table ) ) )* ) diff --git a/docs/generated/sql/bnf/alter_table_partition_by.bnf b/docs/generated/sql/bnf/alter_table_partition_by.bnf index 82fe3f23fb07..491af0c610a5 100644 --- a/docs/generated/sql/bnf/alter_table_partition_by.bnf +++ b/docs/generated/sql/bnf/alter_table_partition_by.bnf @@ -1,3 +1,3 @@ alter_onetable_stmt ::= - 'ALTER' 'TABLE' table_name 'PARTITION' 'ALL' 'BY' partition_by_inner ( ( ',' ( 'RENAME' opt_column column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' column_def | 'ADD' 'IF' 'NOT' 'EXISTS' column_def | 'ADD' 'COLUMN' column_def | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' column_def | 'ALTER' opt_column column_name alter_column_default | 'ALTER' opt_column column_name alter_column_on_update | 'ALTER' opt_column column_name alter_column_visible | 'ALTER' opt_column column_name 'DROP' 'NOT' 'NULL' | 'ALTER' opt_column column_name 'DROP' 'STORED' | 'ALTER' opt_column column_name 'SET' 'NOT' 'NULL' | 'DROP' opt_column 'IF' 'EXISTS' column_name opt_drop_behavior | 'DROP' opt_column column_name opt_drop_behavior | 'ALTER' opt_column column_name opt_set_data 'TYPE' typename opt_collate opt_alter_column_using | 'ADD' table_constraint opt_validate_behavior | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name opt_drop_behavior | 'DROP' 'CONSTRAINT' constraint_name opt_drop_behavior | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | ( partition_by | 'PARTITION' 'ALL' 'BY' partition_by_inner ) ) ) )* - | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name 'PARTITION' 'ALL' 'BY' partition_by_inner ( ( ',' ( 'RENAME' opt_column column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' column_def | 'ADD' 'IF' 'NOT' 'EXISTS' column_def | 'ADD' 'COLUMN' column_def | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' column_def | 'ALTER' opt_column column_name alter_column_default | 'ALTER' opt_column column_name alter_column_on_update | 'ALTER' opt_column column_name alter_column_visible | 'ALTER' opt_column column_name 'DROP' 'NOT' 'NULL' | 'ALTER' opt_column column_name 'DROP' 'STORED' | 'ALTER' opt_column column_name 'SET' 'NOT' 'NULL' | 'DROP' opt_column 'IF' 'EXISTS' column_name opt_drop_behavior | 'DROP' opt_column column_name opt_drop_behavior | 'ALTER' opt_column column_name opt_set_data 'TYPE' typename opt_collate opt_alter_column_using | 'ADD' table_constraint opt_validate_behavior | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name opt_drop_behavior | 'DROP' 'CONSTRAINT' constraint_name opt_drop_behavior | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | ( partition_by | 'PARTITION' 'ALL' 'BY' partition_by_inner ) ) ) )* + 'ALTER' 'TABLE' table_name 'PARTITION' 'ALL' 'BY' partition_by_inner ( ( ',' ( 'RENAME' opt_column column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' column_def | 'ADD' 'IF' 'NOT' 'EXISTS' column_def | 'ADD' 'COLUMN' column_def | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' column_def | 'ALTER' opt_column column_name alter_column_default | 'ALTER' opt_column column_name alter_column_on_update | 'ALTER' opt_column column_name alter_column_visible | 'ALTER' opt_column column_name 'DROP' 'NOT' 'NULL' | 'ALTER' opt_column column_name 'DROP' 'STORED' | 'ALTER' opt_column column_name 'SET' 'NOT' 'NULL' | 'DROP' opt_column 'IF' 'EXISTS' column_name opt_drop_behavior | 'DROP' opt_column column_name opt_drop_behavior | 'ALTER' opt_column column_name opt_set_data 'TYPE' typename opt_collate opt_alter_column_using | 'ADD' table_constraint opt_validate_behavior | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name opt_drop_behavior | 'DROP' 'CONSTRAINT' constraint_name opt_drop_behavior | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | ( partition_by | 'PARTITION' 'ALL' 'BY' partition_by_inner ) ) ) )* + | 'ALTER' 'TABLE' 'IF' 'EXISTS' table_name 'PARTITION' 'ALL' 'BY' partition_by_inner ( ( ',' ( 'RENAME' opt_column column_name 'TO' column_name | 'RENAME' 'CONSTRAINT' column_name 'TO' column_name | 'ADD' column_def | 'ADD' 'IF' 'NOT' 'EXISTS' column_def | 'ADD' 'COLUMN' column_def | 'ADD' 'COLUMN' 'IF' 'NOT' 'EXISTS' column_def | 'ALTER' opt_column column_name alter_column_default | 'ALTER' opt_column column_name alter_column_on_update | 'ALTER' opt_column column_name alter_column_visible | 'ALTER' opt_column column_name 'DROP' 'NOT' 'NULL' | 'ALTER' opt_column column_name 'DROP' 'STORED' | 'ALTER' opt_column column_name 'SET' 'NOT' 'NULL' | 'DROP' opt_column 'IF' 'EXISTS' column_name opt_drop_behavior | 'DROP' opt_column column_name opt_drop_behavior | 'ALTER' opt_column column_name opt_set_data 'TYPE' typename opt_collate opt_alter_column_using | 'ADD' table_constraint opt_validate_behavior | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name opt_drop_behavior | 'DROP' 'CONSTRAINT' constraint_name opt_drop_behavior | 'EXPERIMENTAL_AUDIT' 'SET' audit_mode | ( partition_by | 'PARTITION' 'ALL' 'BY' partition_by_inner ) ) ) )* diff --git a/docs/generated/sql/bnf/create_index_interleaved_stmt.bnf b/docs/generated/sql/bnf/create_index_interleaved_stmt.bnf deleted file mode 100644 index d56defd29eba..000000000000 --- a/docs/generated/sql/bnf/create_index_interleaved_stmt.bnf +++ /dev/null @@ -1,17 +0,0 @@ -create_index_stmt ::= - 'CREATE' 'UNIQUE' 'INDEX' 'CONCURRENTLY' '...' opt_hash_sharded 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INDEX' 'CONCURRENTLY' '...' opt_hash_sharded 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INDEX' '...' opt_hash_sharded 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INDEX' '...' opt_hash_sharded 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INDEX' 'CONCURRENTLY' '...' opt_hash_sharded 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INDEX' 'CONCURRENTLY' '...' opt_hash_sharded 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INDEX' '...' opt_hash_sharded 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INDEX' '...' opt_hash_sharded 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INVERTED' 'INDEX' 'CONCURRENTLY' '...' 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INVERTED' 'INDEX' 'CONCURRENTLY' '...' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INVERTED' 'INDEX' '...' 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'UNIQUE' 'INVERTED' 'INDEX' '...' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INVERTED' 'INDEX' 'CONCURRENTLY' '...' 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INVERTED' 'INDEX' 'CONCURRENTLY' '...' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INVERTED' 'INDEX' '...' 'STORING' '(' stored_columns ')' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' 'INVERTED' 'INDEX' '...' 'INTERLEAVE' 'IN' 'PARENT' parent_table '(' interleave_prefix ')'_index opt_with_storage_parameter_list opt_where_clause diff --git a/docs/generated/sql/bnf/create_index_stmt.bnf b/docs/generated/sql/bnf/create_index_stmt.bnf index 0a20488e87e8..219d9b3f4c44 100644 --- a/docs/generated/sql/bnf/create_index_stmt.bnf +++ b/docs/generated/sql/bnf/create_index_stmt.bnf @@ -1,5 +1,5 @@ create_index_stmt ::= - 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) opt_index_name 'ON' table_name ( 'USING' name | ) '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets | ) ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_interleave opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause - | 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name ( 'USING' name | ) '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets | ) ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_interleave opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause + 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) opt_index_name 'ON' table_name ( 'USING' name | ) '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets | ) ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause + | 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name ( 'USING' name | ) '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets | ) ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause diff --git a/docs/generated/sql/bnf/create_inverted_index_stmt.bnf b/docs/generated/sql/bnf/create_inverted_index_stmt.bnf index 0147a5b4f1ec..8aed6d67271e 100644 --- a/docs/generated/sql/bnf/create_inverted_index_stmt.bnf +++ b/docs/generated/sql/bnf/create_inverted_index_stmt.bnf @@ -1,5 +1,5 @@ create_index_stmt ::= - 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) opt_index_name 'ON' table_name opt_index_access_method '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' opt_hash_sharded ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_interleave opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause - | 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name opt_index_access_method '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' opt_hash_sharded ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_interleave opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause - | 'CREATE' ( 'UNIQUE' | ) 'INVERTED' 'INDEX' ( 'CONCURRENTLY' | ) opt_index_name 'ON' table_name '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_interleave opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause - | 'CREATE' ( 'UNIQUE' | ) 'INVERTED' 'INDEX' ( 'CONCURRENTLY' | ) 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_interleave opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause + 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) opt_index_name 'ON' table_name opt_index_access_method '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' opt_hash_sharded ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause + | 'CREATE' ( 'UNIQUE' | ) 'INDEX' ( 'CONCURRENTLY' | ) 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name opt_index_access_method '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' opt_hash_sharded ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause + | 'CREATE' ( 'UNIQUE' | ) 'INVERTED' 'INDEX' ( 'CONCURRENTLY' | ) opt_index_name 'ON' table_name '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause + | 'CREATE' ( 'UNIQUE' | ) 'INVERTED' 'INDEX' ( 'CONCURRENTLY' | ) 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name '(' ( ( ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) ( ( ',' ( func_expr_windowless index_elem_options | '(' a_expr ')' index_elem_options | name index_elem_options ) ) )* ) ')' ( ( 'COVERING' | 'STORING' | 'INCLUDE' ) '(' name_list ')' | ) opt_partition_by_index ( 'WITH' '(' ( ( storage_parameter ) ( ( ',' storage_parameter ) )* ) ')' ) opt_where_clause diff --git a/docs/generated/sql/bnf/create_table_stmt.bnf b/docs/generated/sql/bnf/create_table_stmt.bnf index 2bab8fb5f56d..46a04cc2cd88 100644 --- a/docs/generated/sql/bnf/create_table_stmt.bnf +++ b/docs/generated/sql/bnf/create_table_stmt.bnf @@ -1,3 +1,3 @@ create_table_stmt ::= - 'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' ( ( ( ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) ( ( ',' ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) )* ) | ) ')' opt_interleave opt_partition_by_table ( opt_with_storage_parameter_list ) ( 'ON' 'COMMIT' 'PRESERVE' 'ROWS' ) opt_locality - | 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' ( ( ( ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) ( ( ',' ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) )* ) | ) ')' opt_interleave opt_partition_by_table ( opt_with_storage_parameter_list ) ( 'ON' 'COMMIT' 'PRESERVE' 'ROWS' ) opt_locality + 'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' ( ( ( ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) ( ( ',' ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) )* ) | ) ')' opt_partition_by_table ( opt_with_storage_parameter_list ) ( 'ON' 'COMMIT' 'PRESERVE' 'ROWS' ) opt_locality + | 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' ( ( ( ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) ( ( ',' ( column_def | index_def | family_def | table_constraint opt_validate_behavior | 'LIKE' table_name like_table_option_list ) ) )* ) | ) ')' opt_partition_by_table ( opt_with_storage_parameter_list ) ( 'ON' 'COMMIT' 'PRESERVE' 'ROWS' ) opt_locality diff --git a/docs/generated/sql/bnf/index_def.bnf b/docs/generated/sql/bnf/index_def.bnf index fb43452d707a..302748969a88 100644 --- a/docs/generated/sql/bnf/index_def.bnf +++ b/docs/generated/sql/bnf/index_def.bnf @@ -1,19 +1,19 @@ index_def ::= - 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'COVERING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'STORING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'COVERING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'STORING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'COVERING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'STORING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'INCLUDE' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'COVERING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'STORING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'INCLUDE' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'COVERING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'STORING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets 'INCLUDE' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'COVERING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'STORING' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' 'INCLUDE' '(' name_list ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_elem ( ( ',' index_elem ) )* ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause | 'INVERTED' 'INDEX' name '(' index_elem ( ( ',' index_elem ) )* ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause | 'INVERTED' 'INDEX' '(' index_elem ( ( ',' index_elem ) )* ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause diff --git a/docs/generated/sql/bnf/interleave.bnf b/docs/generated/sql/bnf/interleave.bnf deleted file mode 100644 index 7e71696f8c8a..000000000000 --- a/docs/generated/sql/bnf/interleave.bnf +++ /dev/null @@ -1,5 +0,0 @@ -create_table_stmt ::= - 'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' table_definition ')' 'INTERLEAVE' 'IN' 'PARENT' table_name '(' name_list ')' opt_partition_by_table 'WITH' '(' storage_parameter ( ( ',' storage_parameter ) )* ')' 'ON' 'COMMIT' 'PRESERVE' 'ROWS' opt_locality - | 'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' table_definition ')' opt_partition_by_table 'WITH' '(' storage_parameter ( ( ',' storage_parameter ) )* ')' 'ON' 'COMMIT' 'PRESERVE' 'ROWS' opt_locality - | 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' table_definition ')' 'INTERLEAVE' 'IN' 'PARENT' table_name '(' name_list ')' opt_partition_by_table 'WITH' '(' storage_parameter ( ( ',' storage_parameter ) )* ')' 'ON' 'COMMIT' 'PRESERVE' 'ROWS' opt_locality - | 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' table_definition ')' opt_partition_by_table 'WITH' '(' storage_parameter ( ( ',' storage_parameter ) )* ')' 'ON' 'COMMIT' 'PRESERVE' 'ROWS' opt_locality diff --git a/docs/generated/sql/bnf/opt_interleave.bnf b/docs/generated/sql/bnf/opt_interleave.bnf deleted file mode 100644 index fc1e45560432..000000000000 --- a/docs/generated/sql/bnf/opt_interleave.bnf +++ /dev/null @@ -1,2 +0,0 @@ -opt_interleave ::= - 'INTERLEAVE' 'IN' 'PARENT' table_name '(' name_list ')' diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index d0b7f9698b86..6c846178024b 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -959,7 +959,6 @@ unreserved_keyword ::= | 'INHERITS' | 'INJECT' | 'INSERT' - | 'INTERLEAVE' | 'INTO_DB' | 'INVERTED' | 'ISOLATION' @@ -1401,10 +1400,10 @@ create_database_stmt ::= | 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause create_index_stmt ::= - 'CREATE' opt_unique 'INDEX' opt_concurrently opt_index_name 'ON' table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' opt_unique 'INDEX' opt_concurrently 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' opt_unique 'INVERTED' 'INDEX' opt_concurrently opt_index_name 'ON' table_name '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'CREATE' opt_unique 'INVERTED' 'INDEX' opt_concurrently 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + 'CREATE' opt_unique 'INDEX' opt_concurrently opt_index_name 'ON' table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'CREATE' opt_unique 'INDEX' opt_concurrently 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'CREATE' opt_unique 'INVERTED' 'INDEX' opt_concurrently opt_index_name 'ON' table_name '(' index_params ')' opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'CREATE' opt_unique 'INVERTED' 'INDEX' opt_concurrently 'IF' 'NOT' 'EXISTS' index_name 'ON' table_name '(' index_params ')' opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause create_schema_stmt ::= 'CREATE' 'SCHEMA' qualifiable_schema_name @@ -1413,8 +1412,8 @@ create_schema_stmt ::= | 'CREATE' 'SCHEMA' 'IF' 'NOT' 'EXISTS' opt_schema_name 'AUTHORIZATION' role_spec create_table_stmt ::= - 'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' opt_table_elem_list ')' opt_interleave opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality - | 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' opt_table_elem_list ')' opt_interleave opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality + 'CREATE' opt_persistence_temp_table 'TABLE' table_name '(' opt_table_elem_list ')' opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality + | 'CREATE' opt_persistence_temp_table 'TABLE' 'IF' 'NOT' 'EXISTS' table_name '(' opt_table_elem_list ')' opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality create_table_as_stmt ::= 'CREATE' opt_persistence_temp_table 'TABLE' table_name create_as_opt_col_list opt_table_with 'AS' select_stmt opt_create_table_on_commit @@ -1956,10 +1955,6 @@ opt_storing ::= storing '(' name_list ')' | -opt_interleave ::= - 'INTERLEAVE' 'IN' 'PARENT' table_name '(' name_list ')' - | - opt_partition_by_index ::= partition_by | @@ -2640,7 +2635,7 @@ alter_table_cmd ::= | 'DROP' opt_column column_name opt_drop_behavior | 'ALTER' opt_column column_name opt_set_data 'TYPE' typename opt_collate opt_alter_column_using | 'ADD' table_constraint opt_validate_behavior - | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded opt_interleave + | 'ALTER' 'PRIMARY' 'KEY' 'USING' 'COLUMNS' '(' index_params ')' opt_hash_sharded | 'VALIDATE' 'CONSTRAINT' constraint_name | 'DROP' 'CONSTRAINT' 'IF' 'EXISTS' constraint_name opt_drop_behavior | 'DROP' 'CONSTRAINT' constraint_name opt_drop_behavior @@ -3021,8 +3016,8 @@ range_partitions ::= ( range_partition ) ( ( ',' range_partition ) )* index_def ::= - 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause | 'INVERTED' 'INDEX' opt_name '(' index_params ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause like_table_option_list ::= @@ -3112,8 +3107,8 @@ col_qual_list ::= constraint_elem ::= 'CHECK' '(' a_expr ')' - | 'UNIQUE' '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_where_clause - | 'PRIMARY' 'KEY' '(' index_params ')' opt_hash_sharded opt_interleave + | 'UNIQUE' '(' index_params ')' opt_storing opt_partition_by_index opt_where_clause + | 'PRIMARY' 'KEY' '(' index_params ')' opt_hash_sharded | 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions func_name ::= diff --git a/docs/generated/sql/bnf/table_constraint.bnf b/docs/generated/sql/bnf/table_constraint.bnf index 2b8830c5bc7f..af6f6268d5c5 100644 --- a/docs/generated/sql/bnf/table_constraint.bnf +++ b/docs/generated/sql/bnf/table_constraint.bnf @@ -1,17 +1,17 @@ table_constraint ::= 'CONSTRAINT' constraint_name 'CHECK' '(' a_expr ')' - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by_index opt_where_clause - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by_index opt_where_clause - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by_index opt_where_clause - | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' opt_interleave opt_partition_by_index opt_where_clause - | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave - | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' '(' index_params ')' opt_interleave + | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'COVERING' '(' name_list ')' opt_partition_by_index opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'STORING' '(' name_list ')' opt_partition_by_index opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_partition_by_index opt_where_clause + | 'CONSTRAINT' constraint_name 'UNIQUE' '(' index_params ')' opt_partition_by_index opt_where_clause + | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets + | 'CONSTRAINT' constraint_name 'PRIMARY' 'KEY' '(' index_params ')' | 'CONSTRAINT' constraint_name 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions | 'CHECK' '(' a_expr ')' - | 'UNIQUE' '(' index_params ')' 'COVERING' '(' name_list ')' opt_interleave opt_partition_by_index opt_where_clause - | 'UNIQUE' '(' index_params ')' 'STORING' '(' name_list ')' opt_interleave opt_partition_by_index opt_where_clause - | 'UNIQUE' '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_interleave opt_partition_by_index opt_where_clause - | 'UNIQUE' '(' index_params ')' opt_interleave opt_partition_by_index opt_where_clause - | 'PRIMARY' 'KEY' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets opt_interleave - | 'PRIMARY' 'KEY' '(' index_params ')' opt_interleave + | 'UNIQUE' '(' index_params ')' 'COVERING' '(' name_list ')' opt_partition_by_index opt_where_clause + | 'UNIQUE' '(' index_params ')' 'STORING' '(' name_list ')' opt_partition_by_index opt_where_clause + | 'UNIQUE' '(' index_params ')' 'INCLUDE' '(' name_list ')' opt_partition_by_index opt_where_clause + | 'UNIQUE' '(' index_params ')' opt_partition_by_index opt_where_clause + | 'PRIMARY' 'KEY' '(' index_params ')' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' n_buckets + | 'PRIMARY' 'KEY' '(' index_params ')' | 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions diff --git a/docs/tech-notes/encoding.md b/docs/tech-notes/encoding.md index e2e194bf616e..59196972d062 100644 --- a/docs/tech-notes/encoding.md +++ b/docs/tech-notes/encoding.md @@ -544,78 +544,6 @@ Index ID 2 is the secondary index `i2`. ^------------------------------------------------------------ ^-^--------- Indexed column: Collation key for 'Ted' BYTES 'Ted' -Interleaving ------------- - -By default, indexes (in CRDB terminology, so both primary and secondary) -occupy disjoint KV key spans. Users can request that an index be -interleaved with another index, which improves the efficiency of joining -them. - -One index, the parent, must have a primary key that, ignoring column -names, is a prefix (not necessarily proper) of the other index, the -child. The parent, which currently must be a primary index, has its -usual encoding. To encode a KV key in the child, encode it as if it were -in the parent but with an interleaving sentinel -(`EncodeNotNullDescending` in [pkg/util/encoding/encoding.go]) where the -column family ID would be. Append the non-interleaved child encoding but -without the parent columns. The sentinel informs the decoder that the -row does not belong to the parent table. - -Note that the parent may itself be interleaved. In general, the -interleaving relationships constitute an [arborescence]. - -Example schema and data: - - CREATE TABLE owners ( - owner_id INT PRIMARY KEY, - owner STRING - ); - - CREATE TABLE accounts ( - owner_id INT, - account_id INT, - balance DECIMAL, - PRIMARY KEY (owner_id, account_id) - ) INTERLEAVE IN PARENT owners (owner_id); - - INSERT INTO owners VALUES (19, 'Alice'); - INSERT INTO accounts VALUES (19, 83, 10000.50); - -Example dump: - - /Table/51/1/19/0/1489433137.133889094,0 : 0xDBCE04550A2605416C696365 - ^- ^ ^- ^ ^-------^-^^^----------- - | | | | | | ||| - Table ID (owners) Checksum| ||| - | | | | ||| - Index ID Value type (TUPLE) - | | ||| - Primary key (owner_id = 19) Column ID difference - | || - Column family ID Datum encoding type (Bytes) - | - Datum encoding ('Alice') - - /Table/51/1/19/#/52/1/83/0/1489433137.137447008,0 : 0x691956790A3505348D0F4272 - ^- ^ ^- ^ ^- ^ ^- ^ ^-------^-^^^----------- - | | | | | | | | | | ||| - Table ID (owners) | Checksum| ||| - | | | | | | | | ||| - Index ID | | | Value type (TUPLE) - | | | | | | ||| - Primary key (owner_id = 19) Column ID difference - | | | | | || - Interleaving sentinel Datum encoding type (Decimal) - | | | | | - Table ID (accounts) Datum encoding (10000.50) - | | | - Index ID - | | - Primary key (account_id = 83) - | - Column family ID - [pkg/util/encoding/encoding.go]: https://github.com/cockroachdb/cockroach/blob/master/pkg/util/encoding/encoding.go [SQL in CockroachDB: Mapping Table Data to Key-Value Storage]: https://www.cockroachlabs.com/blog/sql-in-cockroachdb-mapping-table-data-to-key-value-storage/ [Implementing Column Families in CockroachDB]: https://www.cockroachlabs.com/blog/sql-cockroachdb-column-families/ @@ -631,4 +559,3 @@ Example dump: [pkg/roachpb/data.proto]: https://github.com/cockroachdb/cockroach/blob/master/pkg/roachpb/data.proto [Unicode Collation Algorithm]: http://unicode.org/reports/tr10/ [an efficient partial inverse]: http://stackoverflow.com/q/23609457/2144669 - [arborescence]: https://en.wikipedia.org/wiki/Arborescence_(graph_theory) diff --git a/pkg/ccl/importccl/import_table_creation.go b/pkg/ccl/importccl/import_table_creation.go index 395723202d0f..f5e661b5f523 100644 --- a/pkg/ccl/importccl/import_table_creation.go +++ b/pkg/ccl/importccl/import_table_creation.go @@ -115,9 +115,6 @@ func MakeSimpleTableDescriptor( if create.IfNotExists { return nil, unimplemented.NewWithIssueDetailf(42846, "import.if-no-exists", "unsupported IF NOT EXISTS") } - if create.Interleave != nil { - return nil, unimplemented.NewWithIssueDetailf(42846, "import.interleave", "interleaved not supported") - } if create.AsSource != nil { return nil, unimplemented.NewWithIssueDetailf(42846, "import.create-as", "CREATE AS not supported") } diff --git a/pkg/ccl/importccl/read_import_pgdump.go b/pkg/ccl/importccl/read_import_pgdump.go index e8be4686185b..a48acc903d12 100644 --- a/pkg/ccl/importccl/read_import_pgdump.go +++ b/pkg/ccl/importccl/read_import_pgdump.go @@ -613,7 +613,6 @@ func readPostgresStmt( Columns: stmt.Columns, Storing: stmt.Storing, Inverted: stmt.Inverted, - Interleave: stmt.Interleave, PartitionByIndex: stmt.PartitionByIndex, StorageParams: stmt.StorageParams, } diff --git a/pkg/ccl/logictestccl/testdata/logic_test/partitioning b/pkg/ccl/logictestccl/testdata/logic_test/partitioning index 712833d75923..5a2eddd1015e 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/partitioning +++ b/pkg/ccl/logictestccl/testdata/logic_test/partitioning @@ -92,25 +92,6 @@ CREATE TABLE t (a INT, b INT, c INT, PRIMARY KEY (a, b)) PARTITION BY RANGE (a) ) ) -statement ok -CREATE TABLE interleave_root (a INT PRIMARY KEY) PARTITION BY LIST (a) ( - PARTITION p0 VALUES IN (0) -) - -statement notice NOTICE: creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored. For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations -CREATE TABLE interleave_child (a INT PRIMARY KEY) INTERLEAVE IN PARENT interleave_root (a) - -statement notice NOTICE: creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored. For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations -CREATE TABLE t (a INT PRIMARY KEY) INTERLEAVE IN PARENT interleave_root (a) PARTITION BY LIST (a) ( - PARTITION p0 VALUES IN (0) -) - -statement ok -DROP TABLE interleave_child - -statement ok -DROP TABLE t; - statement error PARTITION p1: partition has 1 columns but 2 values were supplied CREATE TABLE t (a INT, b INT, c INT, PRIMARY KEY (a, b)) PARTITION BY LIST (a) ( PARTITION p1 VALUES IN ((0, 1)) diff --git a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row index 10e2d34ef9bb..66b800342d18 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row +++ b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row @@ -117,16 +117,6 @@ LOCALITY REGIONAL BY ROW statement ok CREATE TABLE parent_table (pk INT PRIMARY KEY) -statement notice NOTICE: creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored. For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations -CREATE TABLE regional_by_row_table_int ( - pk INT NOT NULL PRIMARY KEY -) -INTERLEAVE IN PARENT parent_table(pk) -LOCALITY REGIONAL BY ROW - -statement ok -DROP TABLE regional_by_row_table_int - statement ok CREATE TABLE regional_by_row_table_explicit_crdb_region_column ( pk int PRIMARY KEY, @@ -348,12 +338,6 @@ CREATE INDEX bad_idx ON regional_by_row_table(a) USING HASH WITH BUCKET_COUNT = statement error hash sharded indexes are not compatible with REGIONAL BY ROW tables ALTER TABLE regional_by_row_table ALTER PRIMARY KEY USING COLUMNS(pk2) USING HASH WITH BUCKET_COUNT = 8 -statement notice NOTICE: creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored. For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations -CREATE INDEX bad_idx_int ON regional_by_row_table(pk) INTERLEAVE IN PARENT parent_table(pk) - -statement ok -DROP INDEX bad_idx_int - # Try add a new unique column. statement ok ALTER TABLE regional_by_row_table ADD COLUMN unique_col INT8 NOT NULL UNIQUE diff --git a/pkg/cli/testdata/dump/interleave_index b/pkg/cli/testdata/dump/interleave_index deleted file mode 100644 index 6e0a19f39e3b..000000000000 --- a/pkg/cli/testdata/dump/interleave_index +++ /dev/null @@ -1,121 +0,0 @@ -# Test the interleaving of indexes within tables - -sql -CREATE DATABASE d; -CREATE TABLE d.t1 (a INT, b INT, PRIMARY KEY (a)); -SET client_min_messages = 'warning'; -CREATE INDEX b_idx ON d.t1(a, b) INTERLEAVE IN PARENT d.t1 (a); -SET client_min_messages = 'notice'; ----- -SET - -dump d t1 ----- ----- -CREATE TABLE public.t1 ( - a INT8 NOT NULL, - b INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (a ASC), - FAMILY "primary" (a, b) -); - -CREATE INDEX b_idx ON public.t1 (a ASC, b ASC) INTERLEAVE IN PARENT public.t1 (a); - --- Validate foreign key constraints. These can fail if there was unvalidated data during the dump. ----- ----- - -sql -CREATE DATABASE e; -CREATE TABLE e.t2 (a INT, b INT, PRIMARY KEY (a)); -CREATE TABLE e.t3 (a INT, b INT, PRIMARY KEY (a)); -SET client_min_messages = 'warning'; -CREATE INDEX b_idx ON e.t2(a, b) INTERLEAVE IN PARENT e.t3 (a); -SET client_min_messages = 'notice'; -INSERT INTO e.t2 VALUES (1, 2); -INSERT INTO e.t3 VALUES (3, 4); ----- -INSERT 1 - -dump e ----- ----- -CREATE TABLE public.t3 ( - a INT8 NOT NULL, - b INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (a ASC), - FAMILY "primary" (a, b) -); - -CREATE TABLE public.t2 ( - a INT8 NOT NULL, - b INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (a ASC), - FAMILY "primary" (a, b) -); - -INSERT INTO public.t3 (a, b) VALUES - (3, 4); - -INSERT INTO public.t2 (a, b) VALUES - (1, 2); - -CREATE INDEX b_idx ON public.t2 (a ASC, b ASC) INTERLEAVE IN PARENT public.t3 (a); - --- Validate foreign key constraints. These can fail if there was unvalidated data during the dump. ----- ----- - -sql -CREATE DATABASE dd; -CREATE TABLE dd.unique (a INT, b INT, PRIMARY KEY (a)); -SET client_min_messages = 'warning'; -CREATE INDEX "b_idx" ON dd.unique(a, b) INTERLEAVE IN PARENT dd.unique (a); -SET client_min_messages = 'notice'; ----- -SET - -dump dd unique ----- ----- -CREATE TABLE public."unique" ( - a INT8 NOT NULL, - b INT8 NULL, - CONSTRAINT "primary" PRIMARY KEY (a ASC), - FAMILY "primary" (a, b) -); - -CREATE INDEX b_idx ON public."unique" (a ASC, b ASC) INTERLEAVE IN PARENT public."unique" (a); - --- Validate foreign key constraints. These can fail if there was unvalidated data during the dump. ----- ----- - -sql -CREATE DATABASE ee; -CREATE TABLE ee.a (i INT, j INT, PRIMARY KEY (i, j DESC)); -SET client_min_messages = 'warning'; -CREATE TABLE ee.d (x INT, y INT, z INT, PRIMARY KEY (x, y DESC, z DESC)) INTERLEAVE IN PARENT ee.a (x, y); -SET client_min_messages = 'notice'; ----- -SET - -dump ee a d ----- ----- -CREATE TABLE public.a ( - i INT8 NOT NULL, - j INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (i ASC, j DESC), - FAMILY "primary" (i, j) -); - -CREATE TABLE public.d ( - x INT8 NOT NULL, - y INT8 NOT NULL, - z INT8 NOT NULL, - CONSTRAINT "primary" PRIMARY KEY (x ASC, y DESC, z DESC), - FAMILY "primary" (x, y, z) -) INTERLEAVE IN PARENT public.a (x, y); ----- ----- diff --git a/pkg/cmd/cr2pg/main.go b/pkg/cmd/cr2pg/main.go index cdf40d60c6de..70482324689b 100644 --- a/pkg/cmd/cr2pg/main.go +++ b/pkg/cmd/cr2pg/main.go @@ -60,7 +60,6 @@ func main() { newstmts[0] = stmt switch stmt := stmt.(type) { case *tree.CreateTable: - stmt.Interleave = nil stmt.PartitionByTable = nil var newdefs tree.TableDefs for _, def := range stmt.Defs { diff --git a/pkg/cmd/docgen/diagrams.go b/pkg/cmd/docgen/diagrams.go index 4dbd3ce3cc60..d22c20a6c1cd 100644 --- a/pkg/cmd/docgen/diagrams.go +++ b/pkg/cmd/docgen/diagrams.go @@ -625,27 +625,6 @@ var specs = []stmtSpec{ }, nosplit: true, }, - { - name: "create_index_interleaved_stmt", - stmt: "create_index_stmt", - match: []*regexp.Regexp{regexp.MustCompile("'INTERLEAVE'")}, - inline: []string{"opt_unique", "opt_storing", "opt_interleave", "opt_concurrently"}, - replace: map[string]string{ - "'ON' a_expr": "'ON' column_name", - "'=' a_expr": "'=' n_buckets", - " opt_index_name": "", - " opt_partition_by": "", - " opt_index_access_method": "", - "'ON' table_name '(' index_params ')'": "'...'", - "storing '(' name_list ')'": "'STORING' '(' stored_columns ')'", - "table_name '(' name_list": "parent_table '(' interleave_prefix", - }, - exclude: []*regexp.Regexp{ - regexp.MustCompile("'CREATE' 'INVERTED'"), - regexp.MustCompile("'EXISTS'"), - }, - unlink: []string{"stored_columns", "parent_table", "interleave_prefix", "n_buckets"}, - }, { name: "create_inverted_index_stmt", stmt: "create_index_stmt", @@ -976,22 +955,12 @@ var specs = []stmtSpec{ nosplit: true, }, {name: "iso_level"}, - { - name: "interleave", - stmt: "create_table_stmt", - inline: []string{"opt_interleave", "opt_table_with", "opt_with_storage_parameter_list", "storage_parameter_list", "opt_create_table_on_commit"}, - replace: map[string]string{"opt_table_elem_list": "table_definition"}, - unlink: []string{"table_definition"}, - }, { name: "not_null_column_level", stmt: "stmt_block", replace: map[string]string{" stmt": " 'CREATE' 'TABLE' table_name '(' column_name column_type 'NOT NULL' ( column_constraints | ) ( ',' ( column_def ( ',' column_def )* ) | ) ( table_constraints | ) ')' ')'"}, unlink: []string{"table_name", "column_name", "column_type", "table_constraints"}, }, - { - name: "opt_interleave", - }, { name: "opt_with_storage_parameter_list", inline: []string{"storage_parameter_list"}, diff --git a/pkg/sql/alter_primary_key.go b/pkg/sql/alter_primary_key.go index 08128dd507db..adb71776aa70 100644 --- a/pkg/sql/alter_primary_key.go +++ b/pkg/sql/alter_primary_key.go @@ -67,18 +67,10 @@ func (p *planner) AlterPrimaryKey( } } - if alterPKNode.Interleave != nil { - p.BufferClientNotice(ctx, interleavedTableDisabledMigrationError) - alterPKNode.Interleave = nil - } - if alterPKNode.Sharded != nil { if !p.EvalContext().SessionData().HashShardedIndexesEnabled { return hashShardedIndexesDisabledError } - if alterPKNode.Interleave != nil { - return pgerror.Newf(pgcode.FeatureNotSupported, "interleaved indexes cannot also be hash sharded") - } if tableDesc.IsLocalityRegionalByRow() { return pgerror.New(pgcode.FeatureNotSupported, "hash sharded indexes are not compatible with REGIONAL BY ROW tables") } diff --git a/pkg/sql/alter_table.go b/pkg/sql/alter_table.go index f991b114de79..77adea737d0c 100644 --- a/pkg/sql/alter_table.go +++ b/pkg/sql/alter_table.go @@ -225,10 +225,9 @@ func (n *alterTableNode) startExec(params runParams) error { // Translate this operation into an ALTER PRIMARY KEY command. alterPK := &tree.AlterTableAlterPrimaryKey{ - Columns: d.Columns, - Sharded: d.Sharded, - Interleave: d.Interleave, - Name: d.Name, + Columns: d.Columns, + Sharded: d.Sharded, + Name: d.Name, } if err := params.p.AlterPrimaryKey( params.ctx, diff --git a/pkg/sql/create_index.go b/pkg/sql/create_index.go index 4902d2fb40e4..11240f1922e8 100644 --- a/pkg/sql/create_index.go +++ b/pkg/sql/create_index.go @@ -65,10 +65,6 @@ func (p *planner) CreateIndex(ctx context.Context, n *tree.CreateIndex) (planNod } if tableDesc.MaterializedView() { - if n.Interleave != nil { - return nil, pgerror.New(pgcode.InvalidObjectDefinition, - "cannot create interleaved index on materialized view") - } if n.Sharded != nil { return nil, pgerror.New(pgcode.InvalidObjectDefinition, "cannot create hash sharded index on materialized view") @@ -204,10 +200,6 @@ func MakeIndexDescriptor( } if n.Inverted { - if n.Interleave != nil { - return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes don't support interleaved tables") - } - if n.Sharded != nil { return nil, pgerror.New(pgcode.InvalidSQLStatementName, "inverted indexes don't support hash sharding") } @@ -244,9 +236,6 @@ func MakeIndexDescriptor( if tableDesc.IsLocalityRegionalByRow() { return nil, hashShardedIndexesOnRegionalByRowError() } - if n.Interleave != nil { - return nil, pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot also be hash sharded") - } shardCol, newColumns, newColumn, err := setupShardedIndex( params.ctx, params.EvalContext(), @@ -600,10 +589,6 @@ func maybeCreateAndAddShardCol( return shardCol, created, err } -var interleavedTableDisabledMigrationError = pgnotice.Newf( - "creation of new interleaved tables or interleaved indexes is no longer supported and will be ignored." + - " For details, see https://www.cockroachlabs.com/docs/releases/v20.2.0#deprecations") - func (n *createIndexNode) startExec(params runParams) error { telemetry.Inc(sqltelemetry.SchemaChangeCreateCounter("index")) foundIndex, err := n.tableDesc.FindIndexWithName(string(n.n.Name)) @@ -640,14 +625,6 @@ func (n *createIndexNode) startExec(params runParams) error { ) } - if n.n.Interleave != nil { - if n.n.PartitionByIndex != nil { - return pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot be partitioned") - } - params.p.BufferClientNotice(params.ctx, interleavedTableDisabledMigrationError) - n.n.Interleave = nil - } - indexDesc, err := MakeIndexDescriptor(params, *n.n, n.tableDesc) if err != nil { return err diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index b2c7bde382c5..f566b2a90957 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -290,11 +290,6 @@ func (n *createTableNode) startExec(params runParams) error { } return err } - if n.n.Interleave != nil { - telemetry.Inc(sqltelemetry.CreateInterleavedTableCounter) - params.p.BufferClientNotice(params.ctx, interleavedTableDisabledMigrationError) - n.n.Interleave = nil - } if n.n.Persistence.IsTemporary() { telemetry.Inc(sqltelemetry.CreateTempTableCounter) @@ -670,11 +665,6 @@ func addUniqueWithoutIndexTableDef( "unique constraints without an index cannot store columns", ) } - if d.Interleave != nil { - return pgerror.New(pgcode.FeatureNotSupported, - "interleaved unique constraints without an index are not supported", - ) - } if d.PartitionByIndex.ContainsPartitions() { return pgerror.New(pgcode.FeatureNotSupported, "partitioned unique constraints without an index are not supported", @@ -1486,9 +1476,6 @@ func NewTableDesc( if n.PartitionByTable.ContainsPartitions() { return nil, pgerror.New(pgcode.FeatureNotSupported, "sharded indexes don't support partitioning") } - if n.Interleave != nil { - return nil, pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot also be hash sharded") - } buckets, err := tabledesc.EvalShardBucketCount(ctx, semaCtx, evalCtx, d.PrimaryKey.ShardBuckets) if err != nil { return nil, err @@ -1693,9 +1680,6 @@ func NewTableDesc( } columns := d.Columns if d.Sharded != nil { - if d.Interleave != nil { - return nil, pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot also be hash sharded") - } if isRegionalByRow { return nil, hashShardedIndexesOnRegionalByRowError() } @@ -1777,9 +1761,6 @@ func NewTableDesc( if err := desc.AddSecondaryIndex(idx); err != nil { return nil, err } - if d.Interleave != nil { - return nil, unimplemented.NewWithIssue(9148, "use CREATE INDEX to make interleaved indexes") - } case *tree.UniqueConstraintTableDef: if d.WithoutIndex { // We will add the unique constraint below. @@ -1815,9 +1796,6 @@ func NewTableDesc( } columns := d.Columns if d.Sharded != nil { - if n.Interleave != nil && d.PrimaryKey { - return nil, pgerror.New(pgcode.FeatureNotSupported, "interleaved indexes cannot also be hash sharded") - } if isRegionalByRow { return nil, hashShardedIndexesOnRegionalByRowError() } @@ -1884,12 +1862,6 @@ func NewTableDesc( if err := desc.AddPrimaryIndex(idx); err != nil { return nil, err } - if d.Interleave != nil { - return nil, unimplemented.NewWithIssue( - 45710, - "interleave not supported in primary key constraint definition", - ) - } for _, c := range columns { primaryIndexColumnSet[string(c.Column)] = struct{}{} } @@ -1898,9 +1870,6 @@ func NewTableDesc( return nil, err } } - if d.Interleave != nil { - return nil, unimplemented.NewWithIssue(9148, "use CREATE INDEX to make interleaved indexes") - } case *tree.CheckConstraintTableDef, *tree.ForeignKeyConstraintTableDef, *tree.FamilyTableDef: // pass, handled below. diff --git a/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema b/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema index 573cf92f68ea..9e931e76ab86 100644 --- a/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema +++ b/pkg/sql/opt/testutils/opttester/testfixtures/tpcc_schema @@ -59,7 +59,7 @@ CREATE TABLE customer primary key (c_w_id, c_d_id, c_id), index customer_idx (c_w_id, c_d_id, c_last, c_first), foreign key (c_w_id, c_d_id) references district (d_w_id, d_id) -) interleave in parent district (c_w_id, c_d_id) +) ---- exec-ddl @@ -143,7 +143,7 @@ CREATE TABLE stock primary key (s_w_id, s_i_id), foreign key (s_w_id) references warehouse (w_id), foreign key (s_i_id) references item (i_id) -) interleave in parent warehouse (s_w_id) +) ---- exec-ddl diff --git a/pkg/sql/opt/testutils/testcat/create_index.go b/pkg/sql/opt/testutils/testcat/create_index.go index 8b5bb5006b63..25c98279b844 100644 --- a/pkg/sql/opt/testutils/testcat/create_index.go +++ b/pkg/sql/opt/testutils/testcat/create_index.go @@ -37,7 +37,6 @@ func (tc *Catalog) CreateIndex(stmt *tree.CreateIndex, version descpb.IndexDescr Columns: stmt.Columns, Sharded: stmt.Sharded, Storing: stmt.Storing, - Interleave: stmt.Interleave, Inverted: stmt.Inverted, PartitionByIndex: stmt.PartitionByIndex, Predicate: stmt.Predicate, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 4aaa25fe062e..48a88bce41e0 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -573,9 +573,6 @@ func (u *sqlSymUnion) dropBehavior() tree.DropBehavior { func (u *sqlSymUnion) validationBehavior() tree.ValidationBehavior { return u.val.(tree.ValidationBehavior) } -func (u *sqlSymUnion) interleave() *tree.InterleaveDef { - return u.val.(*tree.InterleaveDef) -} func (u *sqlSymUnion) partitionBy() *tree.PartitionBy { return u.val.(*tree.PartitionBy) } @@ -797,7 +794,7 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { %token IDENTITY %token IF IFERROR IFNULL IGNORE_FOREIGN_KEYS ILIKE IMMEDIATE IMPORT IN INCLUDE INCLUDING INCREMENT INCREMENTAL %token INET INET_CONTAINED_BY_OR_EQUALS -%token INET_CONTAINS_OR_EQUALS INDEX INDEXES INHERITS INJECT INTERLEAVE INITIALLY +%token INET_CONTAINS_OR_EQUALS INDEX INDEXES INHERITS INJECT INITIALLY %token INNER INSERT INT INTEGER %token INTERSECT INTERVAL INTO INTO_DB INVERTED IS ISERROR ISNULL ISOLATION @@ -1140,7 +1137,6 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { %type alter_index_cmds %type opt_drop_behavior -%type opt_interleave_drop_behavior %type opt_validate_behavior @@ -1189,7 +1185,6 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { %type <[]tree.LikeTableOption> like_table_option_list %type like_table_option %type opt_create_table_on_commit -%type <*tree.InterleaveDef> opt_interleave %type <*tree.PartitionBy> opt_partition_by partition_by partition_by_inner %type <*tree.PartitionByTable> opt_partition_by_table partition_by_table %type <*tree.PartitionByIndex> opt_partition_by_index partition_by_index @@ -2239,12 +2234,11 @@ alter_table_cmd: } // ALTER TABLE VALIDATE CONSTRAINT ... // ALTER TABLE ALTER PRIMARY KEY USING INDEX -| ALTER PRIMARY KEY USING COLUMNS '(' index_params ')' opt_hash_sharded opt_interleave +| ALTER PRIMARY KEY USING COLUMNS '(' index_params ')' opt_hash_sharded { $$.val = &tree.AlterTableAlterPrimaryKey{ Columns: $7.idxElems(), Sharded: $9.shardedIndexDef(), - Interleave: $10.interleave(), } } | VALIDATE CONSTRAINT constraint_name @@ -6321,20 +6315,20 @@ alter_schema_stmt: // %Help: CREATE TABLE - create a new table // %Category: DDL // %Text: -// CREATE [[GLOBAL | LOCAL] {TEMPORARY | TEMP}] TABLE [IF NOT EXISTS] ( ) [] [] -// CREATE [[GLOBAL | LOCAL] {TEMPORARY | TEMP}] TABLE [IF NOT EXISTS] [( )] AS [] [] +// CREATE [[GLOBAL | LOCAL] {TEMPORARY | TEMP}] TABLE [IF NOT EXISTS] ( ) [] +// CREATE [[GLOBAL | LOCAL] {TEMPORARY | TEMP}] TABLE [IF NOT EXISTS] [( )] AS [] // // Table elements: // [] // [UNIQUE | INVERTED] INDEX [] ( [ASC | DESC] [, ...] ) -// [USING HASH WITH BUCKET_COUNT = ] [{STORING | INCLUDE | COVERING} ( )] [] +// [USING HASH WITH BUCKET_COUNT = ] [{STORING | INCLUDE | COVERING} ( )] // FAMILY [] ( ) // [CONSTRAINT ] // // Table constraints: // PRIMARY KEY ( ) [USING HASH WITH BUCKET_COUNT = ] // FOREIGN KEY ( ) REFERENCES [( )] [ON DELETE {NO ACTION | RESTRICT}] [ON UPDATE {NO ACTION | RESTRICT}] -// UNIQUE ( ) [{STORING | INCLUDE | COVERING} ( )] [] +// UNIQUE ( ) [{STORING | INCLUDE | COVERING} ( )] // CHECK ( ) // // Column qualifiers: @@ -6344,9 +6338,6 @@ alter_schema_stmt: // COLLATE // AS ( ) { STORED | VIRTUAL } // -// Interleave clause: -// INTERLEAVE IN PARENT ( ) [CASCADE | RESTRICT] -// // On commit clause: // ON COMMIT {PRESERVE ROWS | DROP | DELETE ROWS} // @@ -6354,36 +6345,34 @@ alter_schema_stmt: // WEBDOCS/create-table.html // WEBDOCS/create-table-as.html create_table_stmt: - CREATE opt_persistence_temp_table TABLE table_name '(' opt_table_elem_list ')' opt_create_table_inherits opt_interleave opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality + CREATE opt_persistence_temp_table TABLE table_name '(' opt_table_elem_list ')' opt_create_table_inherits opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality { name := $4.unresolvedObjectName().ToTableName() $$.val = &tree.CreateTable{ Table: name, IfNotExists: false, - Interleave: $9.interleave(), Defs: $6.tblDefs(), AsSource: nil, - PartitionByTable: $10.partitionByTable(), + PartitionByTable: $9.partitionByTable(), Persistence: $2.persistence(), - StorageParams: $11.storageParams(), - OnCommit: $12.createTableOnCommitSetting(), - Locality: $13.locality(), + StorageParams: $10.storageParams(), + OnCommit: $11.createTableOnCommitSetting(), + Locality: $12.locality(), } } -| CREATE opt_persistence_temp_table TABLE IF NOT EXISTS table_name '(' opt_table_elem_list ')' opt_create_table_inherits opt_interleave opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality +| CREATE opt_persistence_temp_table TABLE IF NOT EXISTS table_name '(' opt_table_elem_list ')' opt_create_table_inherits opt_partition_by_table opt_table_with opt_create_table_on_commit opt_locality { name := $7.unresolvedObjectName().ToTableName() $$.val = &tree.CreateTable{ Table: name, IfNotExists: true, - Interleave: $12.interleave(), Defs: $9.tblDefs(), AsSource: nil, - PartitionByTable: $13.partitionByTable(), + PartitionByTable: $12.partitionByTable(), Persistence: $2.persistence(), - StorageParams: $14.storageParams(), - OnCommit: $15.createTableOnCommitSetting(), - Locality: $16.locality(), + StorageParams: $13.storageParams(), + OnCommit: $14.createTableOnCommitSetting(), + Locality: $15.locality(), } } @@ -6474,7 +6463,6 @@ create_table_as_stmt: $$.val = &tree.CreateTable{ Table: name, IfNotExists: false, - Interleave: nil, Defs: $5.tblDefs(), AsSource: $8.slct(), StorageParams: $6.storageParams(), @@ -6488,7 +6476,6 @@ create_table_as_stmt: $$.val = &tree.CreateTable{ Table: name, IfNotExists: true, - Interleave: nil, Defs: $8.tblDefs(), AsSource: $11.slct(), StorageParams: $9.storageParams(), @@ -6603,38 +6590,6 @@ like_table_option: | ALL { $$.val = tree.LikeTableOption{Opt: tree.LikeTableOptAll} } -opt_interleave: - INTERLEAVE IN PARENT table_name '(' name_list ')' opt_interleave_drop_behavior - { - name := $4.unresolvedObjectName().ToTableName() - $$.val = &tree.InterleaveDef{ - Parent: name, - Fields: $6.nameList(), - DropBehavior: $8.dropBehavior(), - } - } -| /* EMPTY */ - { - $$.val = (*tree.InterleaveDef)(nil) - } - -// TODO(dan): This can be removed in favor of opt_drop_behavior when #7854 is fixed. -opt_interleave_drop_behavior: - CASCADE - { - /* SKIP DOC */ - $$.val = tree.DropCascade - } -| RESTRICT - { - /* SKIP DOC */ - $$.val = tree.DropRestrict - } -| /* EMPTY */ - { - $$.val = tree.DropDefault - } - partition: PARTITION partition_name { @@ -6938,20 +6893,19 @@ generated_by_default_as: GENERATED_BY_DEFAULT BY DEFAULT AS {} index_def: - INDEX opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + INDEX opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause { $$.val = &tree.IndexTableDef{ Name: tree.Name($2), Columns: $4.idxElems(), Sharded: $6.shardedIndexDef(), Storing: $7.nameList(), - Interleave: $8.interleave(), - PartitionByIndex: $9.partitionByIndex(), - StorageParams: $10.storageParams(), - Predicate: $11.expr(), + PartitionByIndex: $8.partitionByIndex(), + StorageParams: $9.storageParams(), + Predicate: $10.expr(), } } -| UNIQUE INDEX opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause +| UNIQUE INDEX opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause { $$.val = &tree.UniqueConstraintTableDef{ IndexTableDef: tree.IndexTableDef { @@ -6959,10 +6913,9 @@ index_def: Columns: $5.idxElems(), Sharded: $7.shardedIndexDef(), Storing: $8.nameList(), - Interleave: $9.interleave(), - PartitionByIndex: $10.partitionByIndex(), - StorageParams: $11.storageParams(), - Predicate: $12.expr(), + PartitionByIndex: $9.partitionByIndex(), + StorageParams: $10.storageParams(), + Predicate: $11.expr(), }, } } @@ -7009,26 +6962,24 @@ constraint_elem: } } | UNIQUE opt_without_index '(' index_params ')' - opt_storing opt_interleave opt_partition_by_index opt_deferrable opt_where_clause + opt_storing opt_partition_by_index opt_deferrable opt_where_clause { $$.val = &tree.UniqueConstraintTableDef{ WithoutIndex: $2.bool(), IndexTableDef: tree.IndexTableDef{ Columns: $4.idxElems(), Storing: $6.nameList(), - Interleave: $7.interleave(), - PartitionByIndex: $8.partitionByIndex(), - Predicate: $10.expr(), + PartitionByIndex: $7.partitionByIndex(), + Predicate: $9.expr(), }, } } -| PRIMARY KEY '(' index_params ')' opt_hash_sharded opt_interleave +| PRIMARY KEY '(' index_params ')' opt_hash_sharded { $$.val = &tree.UniqueConstraintTableDef{ IndexTableDef: tree.IndexTableDef{ Columns: $4.idxElems(), Sharded: $6.shardedIndexDef(), - Interleave: $7.interleave(), }, PrimaryKey: true, } @@ -7723,17 +7674,14 @@ enum_val_list: // %Text: // CREATE [UNIQUE | INVERTED] INDEX [CONCURRENTLY] [IF NOT EXISTS] [] // ON ( [ASC | DESC] [, ...] ) -// [USING HASH WITH BUCKET_COUNT = ] [STORING ( )] [] +// [USING HASH WITH BUCKET_COUNT = ] [STORING ( )] // [PARTITION BY ] // [WITH ] // -// Interleave clause: -// INTERLEAVE IN PARENT ( ) [CASCADE | RESTRICT] -// // %SeeAlso: CREATE TABLE, SHOW INDEXES, SHOW CREATE, // WEBDOCS/create-index.html create_index_stmt: - CREATE opt_unique INDEX opt_concurrently opt_index_name ON table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + CREATE opt_unique INDEX opt_concurrently opt_index_name ON table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause { table := $7.unresolvedObjectName().ToTableName() $$.val = &tree.CreateIndex{ @@ -7743,15 +7691,14 @@ create_index_stmt: Columns: $10.idxElems(), Sharded: $12.shardedIndexDef(), Storing: $13.nameList(), - Interleave: $14.interleave(), - PartitionByIndex: $15.partitionByIndex(), - StorageParams: $16.storageParams(), - Predicate: $17.expr(), + PartitionByIndex: $14.partitionByIndex(), + StorageParams: $15.storageParams(), + Predicate: $16.expr(), Inverted: $8.bool(), Concurrently: $4.bool(), } } -| CREATE opt_unique INDEX opt_concurrently IF NOT EXISTS index_name ON table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause +| CREATE opt_unique INDEX opt_concurrently IF NOT EXISTS index_name ON table_name opt_index_access_method '(' index_params ')' opt_hash_sharded opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause { table := $10.unresolvedObjectName().ToTableName() $$.val = &tree.CreateIndex{ @@ -7762,15 +7709,14 @@ create_index_stmt: Columns: $13.idxElems(), Sharded: $15.shardedIndexDef(), Storing: $16.nameList(), - Interleave: $17.interleave(), - PartitionByIndex: $18.partitionByIndex(), + PartitionByIndex: $17.partitionByIndex(), Inverted: $11.bool(), - StorageParams: $19.storageParams(), - Predicate: $20.expr(), + StorageParams: $18.storageParams(), + Predicate: $19.expr(), Concurrently: $4.bool(), } } -| CREATE opt_unique INVERTED INDEX opt_concurrently opt_index_name ON table_name '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause +| CREATE opt_unique INVERTED INDEX opt_concurrently opt_index_name ON table_name '(' index_params ')' opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause { table := $8.unresolvedObjectName().ToTableName() $$.val = &tree.CreateIndex{ @@ -7780,14 +7726,13 @@ create_index_stmt: Inverted: true, Columns: $10.idxElems(), Storing: $12.nameList(), - Interleave: $13.interleave(), - PartitionByIndex: $14.partitionByIndex(), - StorageParams: $15.storageParams(), - Predicate: $16.expr(), + PartitionByIndex: $13.partitionByIndex(), + StorageParams: $14.storageParams(), + Predicate: $15.expr(), Concurrently: $5.bool(), } } -| CREATE opt_unique INVERTED INDEX opt_concurrently IF NOT EXISTS index_name ON table_name '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause +| CREATE opt_unique INVERTED INDEX opt_concurrently IF NOT EXISTS index_name ON table_name '(' index_params ')' opt_storing opt_partition_by_index opt_with_storage_parameter_list opt_where_clause { table := $11.unresolvedObjectName().ToTableName() $$.val = &tree.CreateIndex{ @@ -7798,10 +7743,9 @@ create_index_stmt: IfNotExists: true, Columns: $13.idxElems(), Storing: $15.nameList(), - Interleave: $16.interleave(), - PartitionByIndex: $17.partitionByIndex(), - StorageParams: $18.storageParams(), - Predicate: $19.expr(), + PartitionByIndex: $16.partitionByIndex(), + StorageParams: $17.storageParams(), + Predicate: $18.expr(), Concurrently: $5.bool(), } } @@ -13256,7 +13200,6 @@ unreserved_keyword: | INHERITS | INJECT | INSERT -| INTERLEAVE | INTO_DB | INVERTED | ISOLATION diff --git a/pkg/sql/parser/testdata/alter_table b/pkg/sql/parser/testdata/alter_table index 1a8fc2e60437..d99b30854284 100644 --- a/pkg/sql/parser/testdata/alter_table +++ b/pkg/sql/parser/testdata/alter_table @@ -449,14 +449,6 @@ ALTER TABLE a ADD PRIMARY KEY (x, y, z) -- fully parenthesized ALTER TABLE a ADD PRIMARY KEY (x, y, z) -- literals removed ALTER TABLE _ ADD PRIMARY KEY (_, _, _) -- identifiers removed -parse -ALTER TABLE a ADD PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = 10 INTERLEAVE IN PARENT b (x, y) ----- -ALTER TABLE a ADD PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = 10 INTERLEAVE IN PARENT b (x, y) -ALTER TABLE a ADD PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = (10) INTERLEAVE IN PARENT b (x, y) -- fully parenthesized -ALTER TABLE a ADD PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = _ INTERLEAVE IN PARENT b (x, y) -- literals removed -ALTER TABLE _ ADD PRIMARY KEY (_, _, _) USING HASH WITH BUCKET_COUNT = 10 INTERLEAVE IN PARENT _ (_, _) -- identifiers removed - parse ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) ---- @@ -465,14 +457,6 @@ ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) -- fully parenthesi ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) -- literals removed ALTER TABLE _ ADD CONSTRAINT _ PRIMARY KEY (_, _, _) -- identifiers removed -parse -ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = 10 INTERLEAVE IN PARENT b (x, y) ----- -ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = 10 INTERLEAVE IN PARENT b (x, y) -ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = (10) INTERLEAVE IN PARENT b (x, y) -- fully parenthesized -ALTER TABLE a ADD CONSTRAINT "primary" PRIMARY KEY (x, y, z) USING HASH WITH BUCKET_COUNT = _ INTERLEAVE IN PARENT b (x, y) -- literals removed -ALTER TABLE _ ADD CONSTRAINT _ PRIMARY KEY (_, _, _) USING HASH WITH BUCKET_COUNT = 10 INTERLEAVE IN PARENT _ (_, _) -- identifiers removed - parse ALTER TABLE a ALTER COLUMN b SET DEFAULT 42 ---- diff --git a/pkg/sql/parser/testdata/create_index b/pkg/sql/parser/testdata/create_index index 23cacbaf0e81..9d66f02fa74f 100644 --- a/pkg/sql/parser/testdata/create_index +++ b/pkg/sql/parser/testdata/create_index @@ -80,22 +80,6 @@ CREATE INDEX ON a (b) WHERE ((c) > (3)) -- fully parenthesized CREATE INDEX ON a (b) WHERE c > _ -- literals removed CREATE INDEX ON _ (_) WHERE _ > 3 -- identifiers removed -parse -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c (d) ----- -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c (d) -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c (d) -- fully parenthesized -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c (d) -- literals removed -CREATE INDEX ON _ (_) INTERLEAVE IN PARENT _ (_) -- identifiers removed - -parse -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c.d (e) ----- -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c.d (e) -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c.d (e) -- fully parenthesized -CREATE INDEX ON a (b) INTERLEAVE IN PARENT c.d (e) -- literals removed -CREATE INDEX ON _ (_) INTERLEAVE IN PARENT _._ (_) -- identifiers removed - parse CREATE INDEX ON a (b ASC, c DESC) ---- @@ -144,22 +128,6 @@ CREATE UNIQUE INDEX a ON b (c) WHERE ((d) > (3)) -- fully parenthesized CREATE UNIQUE INDEX a ON b (c) WHERE d > _ -- literals removed CREATE UNIQUE INDEX _ ON _ (_) WHERE _ > 3 -- identifiers removed -parse -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d (e, f) ----- -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d (e, f) -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d (e, f) -- fully parenthesized -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d (e, f) -- literals removed -CREATE UNIQUE INDEX _ ON _ (_) INTERLEAVE IN PARENT _ (_, _) -- identifiers removed - -parse -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d.e (f, g) ----- -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d.e (f, g) -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d.e (f, g) -- fully parenthesized -CREATE UNIQUE INDEX a ON b (c) INTERLEAVE IN PARENT d.e (f, g) -- literals removed -CREATE UNIQUE INDEX _ ON _ (_) INTERLEAVE IN PARENT _._ (_, _) -- identifiers removed - parse CREATE UNIQUE INDEX a ON b.c (d) ---- @@ -201,14 +169,6 @@ CREATE INVERTED INDEX a ON b (c) WHERE ((d) > (3)) -- fully parenthesized CREATE INVERTED INDEX a ON b (c) WHERE d > _ -- literals removed CREATE INVERTED INDEX _ ON _ (_) WHERE _ > 3 -- identifiers removed -parse -CREATE INVERTED INDEX a ON b (c) INTERLEAVE IN PARENT d (e) ----- -CREATE INVERTED INDEX a ON b (c) INTERLEAVE IN PARENT d (e) -CREATE INVERTED INDEX a ON b (c) INTERLEAVE IN PARENT d (e) -- fully parenthesized -CREATE INVERTED INDEX a ON b (c) INTERLEAVE IN PARENT d (e) -- literals removed -CREATE INVERTED INDEX _ ON _ (_) INTERLEAVE IN PARENT _ (_) -- identifiers removed - parse CREATE INVERTED INDEX IF NOT EXISTS a ON b (c) WHERE d > 3 ---- diff --git a/pkg/sql/parser/testdata/create_table b/pkg/sql/parser/testdata/create_table index 57ffac6f8f70..83e9d4daa3ea 100644 --- a/pkg/sql/parser/testdata/create_table +++ b/pkg/sql/parser/testdata/create_table @@ -558,14 +558,6 @@ CREATE TABLE a (b INT8, CONSTRAINT foo UNIQUE (b) WHERE ((c) > (3))) -- fully pa CREATE TABLE a (b INT8, CONSTRAINT foo UNIQUE (b) WHERE c > _) -- literals removed CREATE TABLE _ (_ INT8, CONSTRAINT _ UNIQUE (_) WHERE _ > 3) -- identifiers removed -parse -CREATE TABLE a (b INT, UNIQUE INDEX foo (b) INTERLEAVE IN PARENT c (d)) ----- -CREATE TABLE a (b INT8, CONSTRAINT foo UNIQUE (b) INTERLEAVE IN PARENT c (d)) -- normalized! -CREATE TABLE a (b INT8, CONSTRAINT foo UNIQUE (b) INTERLEAVE IN PARENT c (d)) -- fully parenthesized -CREATE TABLE a (b INT8, CONSTRAINT foo UNIQUE (b) INTERLEAVE IN PARENT c (d)) -- literals removed -CREATE TABLE _ (_ INT8, CONSTRAINT _ UNIQUE (_) INTERLEAVE IN PARENT _ (_)) -- identifiers removed - parse CREATE TABLE a (UNIQUE INDEX (b) PARTITION BY LIST (c) (PARTITION d VALUES IN (1))) ---- @@ -1321,14 +1313,6 @@ CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE WITHOUT INDEX (b, c)) -- f CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE WITHOUT INDEX (b, c)) -- literals removed CREATE TABLE _ (_ INT8, _ STRING, CONSTRAINT _ UNIQUE WITHOUT INDEX (_, _)) -- identifiers removed -parse -CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE (b, c) INTERLEAVE IN PARENT d (e, f)) ----- -CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE (b, c) INTERLEAVE IN PARENT d (e, f)) -CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE (b, c) INTERLEAVE IN PARENT d (e, f)) -- fully parenthesized -CREATE TABLE a (b INT8, c STRING, CONSTRAINT d UNIQUE (b, c) INTERLEAVE IN PARENT d (e, f)) -- literals removed -CREATE TABLE _ (_ INT8, _ STRING, CONSTRAINT _ UNIQUE (_, _) INTERLEAVE IN PARENT _ (_, _)) -- identifiers removed - error CREATE TABLE test ( CONSTRAINT foo INDEX (bar) @@ -1613,14 +1597,6 @@ CREATE TABLE a (b INT8, c STRING, INDEX (b ASC, c DESC) STORING (c)) -- fully pa CREATE TABLE a (b INT8, c STRING, INDEX (b ASC, c DESC) STORING (c)) -- literals removed CREATE TABLE _ (_ INT8, _ STRING, INDEX (_ ASC, _ DESC) STORING (_)) -- identifiers removed -parse -CREATE TABLE a (b INT8, INDEX (b) INTERLEAVE IN PARENT c (d, e)) ----- -CREATE TABLE a (b INT8, INDEX (b) INTERLEAVE IN PARENT c (d, e)) -CREATE TABLE a (b INT8, INDEX (b) INTERLEAVE IN PARENT c (d, e)) -- fully parenthesized -CREATE TABLE a (b INT8, INDEX (b) INTERLEAVE IN PARENT c (d, e)) -- literals removed -CREATE TABLE _ (_ INT8, INDEX (_) INTERLEAVE IN PARENT _ (_, _)) -- identifiers removed - parse CREATE TABLE a (b INT8, FAMILY (b)) ---- @@ -1637,22 +1613,6 @@ CREATE TABLE a (b INT8, c STRING, FAMILY foo (b), FAMILY (c)) -- fully parenthes CREATE TABLE a (b INT8, c STRING, FAMILY foo (b), FAMILY (c)) -- literals removed CREATE TABLE _ (_ INT8, _ STRING, FAMILY _ (_), FAMILY (_)) -- identifiers removed -parse -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c, d) ----- -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c, d) -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c, d) -- fully parenthesized -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c, d) -- literals removed -CREATE TABLE _ (_ INT8) INTERLEAVE IN PARENT _ (_, _) -- identifiers removed - -parse -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c) CASCADE ----- -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c) CASCADE -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c) CASCADE -- fully parenthesized -CREATE TABLE a (b INT8) INTERLEAVE IN PARENT foo (c) CASCADE -- literals removed -CREATE TABLE _ (_ INT8) INTERLEAVE IN PARENT _ (_) CASCADE -- identifiers removed - parse CREATE TABLE a.b (b INT8) ---- @@ -1835,15 +1795,6 @@ CREATE TABLE a (b INT8) PARTITION ALL BY RANGE (b) (PARTITION p1 VALUES FROM ((m CREATE TABLE a (b INT8) PARTITION ALL BY RANGE (b) (PARTITION p1 VALUES FROM (minvalue) TO (_), PARTITION p2 VALUES FROM (_, maxvalue) TO (_, _), PARTITION p3 VALUES FROM (_, _) TO (maxvalue)) -- literals removed CREATE TABLE _ (_ INT8) PARTITION ALL BY RANGE (_) (PARTITION _ VALUES FROM (_) TO (1), PARTITION _ VALUES FROM (2, _) TO (4, 4), PARTITION _ VALUES FROM (4, 4) TO (_)) -- identifiers removed - -parse -CREATE TABLE a () INTERLEAVE IN PARENT b (c) PARTITION BY LIST (d) (PARTITION e VALUES IN (1)) ----- -CREATE TABLE a () INTERLEAVE IN PARENT b (c) PARTITION BY LIST (d) (PARTITION e VALUES IN (1)) -CREATE TABLE a () INTERLEAVE IN PARENT b (c) PARTITION BY LIST (d) (PARTITION e VALUES IN ((1))) -- fully parenthesized -CREATE TABLE a () INTERLEAVE IN PARENT b (c) PARTITION BY LIST (d) (PARTITION e VALUES IN (_)) -- literals removed -CREATE TABLE _ () INTERLEAVE IN PARENT _ (_) PARTITION BY LIST (_) (PARTITION _ VALUES IN (1)) -- identifiers removed - parse CREATE TABLE IF NOT EXISTS a () PARTITION BY LIST (b) (PARTITION c VALUES IN (1)) ---- diff --git a/pkg/sql/randgen/mutator.go b/pkg/sql/randgen/mutator.go index 0ff53b1a3f43..467d04192d7a 100644 --- a/pkg/sql/randgen/mutator.go +++ b/pkg/sql/randgen/mutator.go @@ -643,10 +643,6 @@ var postgresStatementMutator MultiStatementMutation = func(rng *rand.Rand, stmts case *tree.SetClusterSetting, *tree.SetVar: continue case *tree.CreateTable: - if stmt.Interleave != nil { - stmt.Interleave = nil - changed = true - } if stmt.PartitionByTable != nil { stmt.PartitionByTable = nil changed = true @@ -674,10 +670,6 @@ var postgresStatementMutator MultiStatementMutation = func(rng *rand.Rand, stmts changed = true } case *tree.UniqueConstraintTableDef: - if def.Interleave != nil { - def.Interleave = nil - changed = true - } if def.PartitionByIndex != nil { def.PartitionByIndex = nil changed = true @@ -714,10 +706,6 @@ func postgresCreateTableMutator( mutated = append(mutated, stmt) switch stmt := stmt.(type) { case *tree.CreateTable: - if stmt.Interleave != nil { - stmt.Interleave = nil - changed = true - } // Get all the column types first. colTypes := make(map[string]*types.T) for _, def := range stmt.Defs { diff --git a/pkg/sql/sem/tree/alter_table.go b/pkg/sql/sem/tree/alter_table.go index 34458cbc87a1..f08b9ad6ac41 100644 --- a/pkg/sql/sem/tree/alter_table.go +++ b/pkg/sql/sem/tree/alter_table.go @@ -255,10 +255,9 @@ func (node *AlterTableAlterColumnType) GetColumn() Name { // AlterTableAlterPrimaryKey represents an ALTER TABLE ALTER PRIMARY KEY command. type AlterTableAlterPrimaryKey struct { - Columns IndexElemList - Interleave *InterleaveDef - Sharded *ShardedIndexDef - Name Name + Columns IndexElemList + Sharded *ShardedIndexDef + Name Name } // TelemetryCounter implements the AlterTableCmd interface. @@ -274,9 +273,6 @@ func (node *AlterTableAlterPrimaryKey) Format(ctx *FmtCtx) { if node.Sharded != nil { ctx.FormatNode(node.Sharded) } - if node.Interleave != nil { - ctx.FormatNode(node.Interleave) - } } // AlterTableDropColumn represents a DROP COLUMN command. diff --git a/pkg/sql/sem/tree/create.go b/pkg/sql/sem/tree/create.go index a15721a8a6d9..8082b99273bf 100644 --- a/pkg/sql/sem/tree/create.go +++ b/pkg/sql/sem/tree/create.go @@ -213,7 +213,6 @@ type CreateIndex struct { // Extra columns to be stored together with the indexed ones as an optimization // for improved reading performance. Storing NameList - Interleave *InterleaveDef PartitionByIndex *PartitionByIndex StorageParams StorageParams Predicate Expr @@ -261,9 +260,6 @@ func (node *CreateIndex) Format(ctx *FmtCtx) { ctx.FormatNode(&node.Storing) ctx.WriteByte(')') } - if node.Interleave != nil { - ctx.FormatNode(node.Interleave) - } if node.PartitionByIndex != nil { ctx.FormatNode(node.PartitionByIndex) } @@ -937,7 +933,6 @@ type IndexTableDef struct { Columns IndexElemList Sharded *ShardedIndexDef Storing NameList - Interleave *InterleaveDef Inverted bool PartitionByIndex *PartitionByIndex StorageParams StorageParams @@ -965,9 +960,6 @@ func (node *IndexTableDef) Format(ctx *FmtCtx) { ctx.FormatNode(&node.Storing) ctx.WriteByte(')') } - if node.Interleave != nil { - ctx.FormatNode(node.Interleave) - } if node.PartitionByIndex != nil { ctx.FormatNode(node.PartitionByIndex) } @@ -1037,9 +1029,6 @@ func (node *UniqueConstraintTableDef) Format(ctx *FmtCtx) { ctx.FormatNode(&node.Storing) ctx.WriteByte(')') } - if node.Interleave != nil { - ctx.FormatNode(node.Interleave) - } if node.PartitionByIndex != nil { ctx.FormatNode(node.PartitionByIndex) } @@ -1214,32 +1203,6 @@ func (node *ShardedIndexDef) Format(ctx *FmtCtx) { ctx.FormatNode(node.ShardBuckets) } -// InterleaveDef represents an interleave definition within a CREATE TABLE -// or CREATE INDEX statement. -type InterleaveDef struct { - Parent TableName - Fields NameList - DropBehavior DropBehavior -} - -// Format implements the NodeFormatter interface. -func (node *InterleaveDef) Format(ctx *FmtCtx) { - ctx.WriteString(" INTERLEAVE IN PARENT ") - ctx.FormatNode(&node.Parent) - ctx.WriteString(" (") - for i := range node.Fields { - if i > 0 { - ctx.WriteString(", ") - } - ctx.FormatNode(&node.Fields[i]) - } - ctx.WriteString(")") - if node.DropBehavior != DropDefault { - ctx.WriteString(" ") - ctx.WriteString(node.DropBehavior.String()) - } -} - // PartitionByType is an enum of each type of partitioning (LIST/RANGE). type PartitionByType string @@ -1429,7 +1392,6 @@ const ( type CreateTable struct { IfNotExists bool Table TableName - Interleave *InterleaveDef PartitionByTable *PartitionByTable Persistence Persistence StorageParams StorageParams @@ -1495,9 +1457,6 @@ func (node *CreateTable) FormatBody(ctx *FmtCtx) { ctx.WriteString(" (") ctx.FormatNode(&node.Defs) ctx.WriteByte(')') - if node.Interleave != nil { - ctx.FormatNode(node.Interleave) - } if node.PartitionByTable != nil { ctx.FormatNode(node.PartitionByTable) } diff --git a/pkg/sql/sem/tree/pretty.go b/pkg/sql/sem/tree/pretty.go index 0b95a6fd27b8..75bf11a7a900 100644 --- a/pkg/sql/sem/tree/pretty.go +++ b/pkg/sql/sem/tree/pretty.go @@ -1239,9 +1239,6 @@ func (node *CreateTable) doc(p *PrettyCfg) pretty.Doc { if node.As() { clauses = append(clauses, p.Doc(node.AsSource)) } - if node.Interleave != nil { - clauses = append(clauses, p.Doc(node.Interleave)) - } if node.PartitionByTable != nil { clauses = append(clauses, p.Doc(node.PartitionByTable)) } @@ -1559,22 +1556,6 @@ func (node *ShardedIndexDef) doc(p *PrettyCfg) pretty.Doc { return pretty.Fold(pretty.ConcatSpace, parts...) } -func (node *InterleaveDef) doc(p *PrettyCfg) pretty.Doc { - // Final layout: - // - // INTERLEAVE IN PARENT tbl (...) [RESTRICT|CASCADE] - // - parts := []pretty.Doc{ - pretty.Keyword("INTERLEAVE IN PARENT"), - p.Doc(&node.Parent), - p.bracket("(", p.Doc(&node.Fields), ")"), - } - if node.DropBehavior != DropDefault { - parts = append(parts, pretty.Keyword(node.DropBehavior.String())) - } - return pretty.Fold(pretty.ConcatSpace, parts...) -} - func (node *CreateIndex) doc(p *PrettyCfg) pretty.Doc { // Final layout: // CREATE [UNIQUE] [INVERTED] INDEX [name] @@ -1620,9 +1601,6 @@ func (node *CreateIndex) doc(p *PrettyCfg) pretty.Doc { ")", "", )) } - if node.Interleave != nil { - clauses = append(clauses, p.Doc(node.Interleave)) - } if node.PartitionByIndex != nil { clauses = append(clauses, p.Doc(node.PartitionByIndex)) } @@ -1693,9 +1671,6 @@ func (node *IndexTableDef) doc(p *PrettyCfg) pretty.Doc { p.Doc(&node.Storing), ")", "")) } - if node.Interleave != nil { - clauses = append(clauses, p.Doc(node.Interleave)) - } if node.PartitionByIndex != nil { clauses = append(clauses, p.Doc(node.PartitionByIndex)) } @@ -1756,9 +1731,7 @@ func (node *UniqueConstraintTableDef) doc(p *PrettyCfg) pretty.Doc { p.Doc(&node.Storing), ")", "")) } - if node.Interleave != nil { - clauses = append(clauses, p.Doc(node.Interleave)) - } + if node.PartitionByIndex != nil { clauses = append(clauses, p.Doc(node.PartitionByIndex)) } diff --git a/pkg/sql/sem/tree/testdata/pretty/20.align-deindent.golden.short b/pkg/sql/sem/tree/testdata/pretty/20.align-deindent.golden.short index c8c04fab861b..ca469c5031b9 100644 --- a/pkg/sql/sem/tree/testdata/pretty/20.align-deindent.golden.short +++ b/pkg/sql/sem/tree/testdata/pretty/20.align-deindent.golden.short @@ -5,8 +5,7 @@ CREATE TABLE t ( a INT8, b INT8, c INT8, PRIMARY KEY (a, b) -) INTERLEAVE IN PARENT p2 (i) - PARTITION BY LIST (a) ( +) PARTITION BY LIST (a) ( PARTITION p1 VALUES IN ( 1 diff --git a/pkg/sql/sem/tree/testdata/pretty/20.align-only.golden.short b/pkg/sql/sem/tree/testdata/pretty/20.align-only.golden.short index c8c04fab861b..ca469c5031b9 100644 --- a/pkg/sql/sem/tree/testdata/pretty/20.align-only.golden.short +++ b/pkg/sql/sem/tree/testdata/pretty/20.align-only.golden.short @@ -5,8 +5,7 @@ CREATE TABLE t ( a INT8, b INT8, c INT8, PRIMARY KEY (a, b) -) INTERLEAVE IN PARENT p2 (i) - PARTITION BY LIST (a) ( +) PARTITION BY LIST (a) ( PARTITION p1 VALUES IN ( 1 diff --git a/pkg/sql/sem/tree/testdata/pretty/20.ref.golden.short b/pkg/sql/sem/tree/testdata/pretty/20.ref.golden.short index feb7008e0a42..15a169ca3cd3 100644 --- a/pkg/sql/sem/tree/testdata/pretty/20.ref.golden.short +++ b/pkg/sql/sem/tree/testdata/pretty/20.ref.golden.short @@ -6,7 +6,6 @@ CREATE TABLE t ( a INT8, b INT8, c INT8, PRIMARY KEY (a, b) ) - INTERLEAVE IN PARENT p2 (i) PARTITION BY LIST (a) ( PARTITION p1 VALUES IN (1), diff --git a/pkg/sql/sem/tree/testdata/pretty/20.sql b/pkg/sql/sem/tree/testdata/pretty/20.sql index 581e4f51ce31..dc6740fd3eb3 100644 --- a/pkg/sql/sem/tree/testdata/pretty/20.sql +++ b/pkg/sql/sem/tree/testdata/pretty/20.sql @@ -1,4 +1,4 @@ -CREATE TABLE t (a INT, b INT, c INT, PRIMARY KEY (a, b)) INTERLEAVE IN PARENT p2 (i) PARTITION BY LIST (a) ( +CREATE TABLE t (a INT, b INT, c INT, PRIMARY KEY (a, b)) PARTITION BY LIST (a) ( PARTITION p1 VALUES IN (1), PARTITION p2 VALUES IN (2) ) diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden deleted file mode 100644 index b690994cdea9..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden +++ /dev/null @@ -1,50 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -CREATE TABLE t ( - a - INT8 -) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -10: ----------- -CREATE TABLE t ( - a INT8 -) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -16: ----------------- -CREATE TABLE t ( - a INT8 -) INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -44: --------------------------------------------- -CREATE TABLE t ( - a INT8 -) INTERLEAVE IN PARENT p2 (i, test) RESTRICT - -49: -------------------------------------------------- -CREATE TABLE t (a INT8) INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -66: ------------------------------------------------------------------- -CREATE TABLE t (a INT8) INTERLEAVE IN PARENT p2 (i, test) RESTRICT - - diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden.short b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden.short deleted file mode 100644 index dd789f37978e..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-deindent.golden.short +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -CREATE TABLE t ( - a INT8 -) INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - - diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden deleted file mode 100644 index b690994cdea9..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden +++ /dev/null @@ -1,50 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -CREATE TABLE t ( - a - INT8 -) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -10: ----------- -CREATE TABLE t ( - a INT8 -) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -16: ----------------- -CREATE TABLE t ( - a INT8 -) INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -44: --------------------------------------------- -CREATE TABLE t ( - a INT8 -) INTERLEAVE IN PARENT p2 (i, test) RESTRICT - -49: -------------------------------------------------- -CREATE TABLE t (a INT8) INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -66: ------------------------------------------------------------------- -CREATE TABLE t (a INT8) INTERLEAVE IN PARENT p2 (i, test) RESTRICT - - diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden.short b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden.short deleted file mode 100644 index dd789f37978e..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.align-only.golden.short +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -CREATE TABLE t ( - a INT8 -) INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - - diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden deleted file mode 100644 index 599ae2cfa805..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden +++ /dev/null @@ -1,41 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -CREATE TABLE t ( - a - INT8 -) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -10: ----------- -CREATE TABLE t ( - a INT8 -) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -23: ------------------------ -CREATE TABLE t (a INT8) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - -46: ----------------------------------------------- -CREATE TABLE t (a INT8) - INTERLEAVE IN PARENT p2 (i, test) RESTRICT - -66: ------------------------------------------------------------------- -CREATE TABLE t (a INT8) INTERLEAVE IN PARENT p2 (i, test) RESTRICT - - diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden.short b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden.short deleted file mode 100644 index c59ef4ae2328..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.ref.golden.short +++ /dev/null @@ -1,11 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -CREATE TABLE t (a INT8) - INTERLEAVE IN PARENT p2 ( - i, - test - ) RESTRICT - - diff --git a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.sql b/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.sql deleted file mode 100644 index 25cd20bf1d4d..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/create_interleaved_drop.sql +++ /dev/null @@ -1 +0,0 @@ -create table t (a int) interleave in parent p2 (i, "test") restrict diff --git a/pkg/sql/sqltelemetry/schema.go b/pkg/sql/sqltelemetry/schema.go index 308262775111..d0624cd08998 100644 --- a/pkg/sql/sqltelemetry/schema.go +++ b/pkg/sql/sqltelemetry/schema.go @@ -31,10 +31,6 @@ func SchemaNewTypeCounter(t string) telemetry.Counter { } var ( - // CreateInterleavedTableCounter is to be incremented every time an - // interleaved table is being created. - CreateInterleavedTableCounter = telemetry.GetCounterOnce("sql.schema.create_interleaved_table") - // CreateTempTableCounter is to be incremented every time a TEMP TABLE // has been created. CreateTempTableCounter = telemetry.GetCounterOnce("sql.schema.create_temp_table") diff --git a/pkg/sql/testdata/telemetry/error b/pkg/sql/testdata/telemetry/error index 1113d6cbf5f1..1c6687ed15bb 100644 --- a/pkg/sql/testdata/telemetry/error +++ b/pkg/sql/testdata/telemetry/error @@ -89,14 +89,6 @@ errorcodes.XXUUU othererror.XXUUU othererror.XXUUU.crdb_internal.set_vmodule() -# 0A000 is pgcode.FeatureNotSupported. -feature-usage -CREATE TABLE foo (a INT8 PRIMARY KEY, b INT8, INDEX (b) INTERLEAVE IN PARENT foo (b)) ----- -error: pq: unimplemented: use CREATE INDEX to make interleaved indexes -errorcodes.0A000 -unimplemented.#9148 - # 22012 is pgcode.DivisionByZero. feature-usage SELECT 2/0 From 3916aa674df72e4be8a233da273c0fdacb41ecdb Mon Sep 17 00:00:00 2001 From: rharding6373 Date: Wed, 6 Oct 2021 16:15:50 -0700 Subject: [PATCH 197/205] opt: take advantage of partial ordering in the hash aggregator Before this change, the optimizer cost model for group by included overhead for processing all input rows for all non-streaming aggregation. Recent changes in the vectorized execution engine added optimizations for aggregation with partially ordered grouping columns that does not require all input rows to be processed if there is a limit, but still requires a hash table, unlike streaming aggregation. This change adds checks for whether an aggregation has a subset of grouping columns that are ordered, and costs it similarly to streaming aggregation by reflecting the limit hint on the number of both input rows and output rows. We also pass the limit hint property to child nodes of the aggregation if the grouping columns are partially ordered. We also add a new exploration rule, `GenerateLimitedGroupByScans`, to enable the optimizer to explore scans on secondary indexes that lead to partially ordered grouping columns. Previously, fully ordered grouping columns could be found by exploring secondary indexes with full cover over the columns via `GenerateIndexScans`. When introducing partially ordered grouping columns, however, not all grouping columns may be part of an index, so we may need to construct an IndexJoin to add the missing columns. The new exploration rule is triggered when there is a limit expression with a positive constant limit, a canonical group by, and a canonical scan. This change also modifies the criteria for streaming group by to include group by with no grouping columns. This change also adds the group by mode (streaming, hybrid, or none) to the EXPLAIN(OPT) output for easier debugging. Fixes: #63049 Fixes: #71768 Release note (performance improvement): Improves performance of some GROUP BY queries with a LIMIT if there is an index ordering that matches a subset of the grouping columns. In this case the total number of aggregations needed to satisfy the LIMIT can be emited without scanning the entire input, enabling the execution to be more effective. --- .../logic_test/regional_by_row_query_behavior | 6 +- pkg/sql/distsql_spec_exec_factory.go | 1 + pkg/sql/opt/exec/execbuilder/relational.go | 3 +- .../opt/exec/execbuilder/testdata/aggregate | 44 +-- .../opt/exec/execbuilder/testdata/distinct_on | 4 +- .../opt/exec/execbuilder/testdata/distsql_agg | 42 +-- pkg/sql/opt/exec/execbuilder/testdata/explain | 6 +- .../testdata/explain_analyze_plans | 2 +- .../testdata/inverted_join_geospatial | 2 +- .../testdata/inverted_join_geospatial_dist | 2 +- pkg/sql/opt/exec/execbuilder/testdata/limit | 2 +- .../opt/exec/execbuilder/testdata/lookup_join | 2 +- .../exec/execbuilder/testdata/select_index | 2 +- pkg/sql/opt/exec/execbuilder/testdata/srfs | 2 +- .../execbuilder/testdata/subquery_correlated | 2 +- pkg/sql/opt/exec/execbuilder/testdata/window | 4 +- pkg/sql/opt/exec/explain/emit.go | 15 +- pkg/sql/opt/exec/explain/testdata/gists | 4 +- pkg/sql/opt/exec/explain/testdata/gists_tpce | 8 +- pkg/sql/opt/exec/factory.go | 15 + pkg/sql/opt/exec/factory.opt | 4 + pkg/sql/opt/memo/expr.go | 56 +++- pkg/sql/opt/memo/expr_format.go | 11 + pkg/sql/opt/memo/testdata/format | 22 +- pkg/sql/opt/memo/testdata/logprops/groupby | 20 +- pkg/sql/opt/memo/testdata/logprops/join | 12 +- pkg/sql/opt/memo/testdata/logprops/scalar | 2 +- pkg/sql/opt/memo/testdata/stats/groupby | 30 +- pkg/sql/opt/memo/testdata/stats/index-join | 2 +- pkg/sql/opt/memo/testdata/stats/inverted-geo | 2 +- pkg/sql/opt/memo/testdata/stats/join | 14 +- pkg/sql/opt/memo/testdata/stats/project | 6 +- pkg/sql/opt/memo/testdata/stats/scan | 2 +- pkg/sql/opt/memo/testdata/stats/select | 6 +- pkg/sql/opt/memo/testdata/stats_quality/tpcc | 16 +- .../opt/memo/testdata/stats_quality/tpch/q01 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q02 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q03 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q04 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q05 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q07 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q08 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q09 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q10 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q11 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q12 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q13 | 4 +- .../opt/memo/testdata/stats_quality/tpch/q15 | 4 +- .../opt/memo/testdata/stats_quality/tpch/q16 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q17 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q18 | 4 +- .../opt/memo/testdata/stats_quality/tpch/q20 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q21 | 2 +- .../opt/memo/testdata/stats_quality/tpch/q22 | 2 +- pkg/sql/opt/memo/testdata/typing | 4 +- pkg/sql/opt/norm/testdata/rules/combo | 55 ++-- pkg/sql/opt/norm/testdata/rules/decorrelate | 96 +++--- pkg/sql/opt/norm/testdata/rules/groupby | 92 +++--- pkg/sql/opt/norm/testdata/rules/inline | 2 +- pkg/sql/opt/norm/testdata/rules/join | 6 +- pkg/sql/opt/norm/testdata/rules/ordering | 2 +- pkg/sql/opt/norm/testdata/rules/prune_cols | 14 +- pkg/sql/opt/norm/testdata/rules/reject_nulls | 18 +- pkg/sql/opt/norm/testdata/rules/scalar | 14 +- pkg/sql/opt/norm/testdata/rules/select | 6 +- pkg/sql/opt/norm/testdata/rules/side_effects | 2 +- pkg/sql/opt/optbuilder/testdata/aggregate | 174 +++++------ pkg/sql/opt/optbuilder/testdata/distinct | 4 +- pkg/sql/opt/optbuilder/testdata/distinct_on | 4 +- pkg/sql/opt/optbuilder/testdata/having | 26 +- pkg/sql/opt/optbuilder/testdata/limit | 30 +- .../opt/optbuilder/testdata/projection-reuse | 2 +- pkg/sql/opt/optbuilder/testdata/subquery | 32 +- pkg/sql/opt/optbuilder/testdata/window | 2 +- pkg/sql/opt/optbuilder/testdata/with | 2 +- pkg/sql/opt/optgen/cmd/optgen/metadata.go | 1 + pkg/sql/opt/optgen/exprgen/testdata/groupby | 4 +- pkg/sql/opt/ordering/group_by.go | 2 +- pkg/sql/opt/ordering/scan.go | 2 +- pkg/sql/opt/props/ordering_choice.go | 30 +- pkg/sql/opt/xform/coster.go | 18 +- pkg/sql/opt/xform/groupby_funcs.go | 86 +++++- pkg/sql/opt/xform/limit_funcs.go | 2 +- pkg/sql/opt/xform/physical_props.go | 3 +- pkg/sql/opt/xform/rules/groupby.opt | 69 +++++ pkg/sql/opt/xform/scan_funcs.go | 3 +- pkg/sql/opt/xform/testdata/coster/groupby | 217 ++++++++++++- pkg/sql/opt/xform/testdata/external/customer | 4 +- pkg/sql/opt/xform/testdata/external/hibernate | 32 +- pkg/sql/opt/xform/testdata/external/liquibase | 2 +- pkg/sql/opt/xform/testdata/external/navicat | 2 +- pkg/sql/opt/xform/testdata/external/nova | 40 +-- .../xform/testdata/external/postgis-tutorial | 6 +- .../testdata/external/postgis-tutorial-idx | 6 +- pkg/sql/opt/xform/testdata/external/tpcc | 16 +- .../xform/testdata/external/tpcc-later-stats | 16 +- .../opt/xform/testdata/external/tpcc-no-stats | 16 +- pkg/sql/opt/xform/testdata/external/tpce | 8 +- .../opt/xform/testdata/external/tpce-no-stats | 8 +- pkg/sql/opt/xform/testdata/external/tpch | 44 +-- .../opt/xform/testdata/external/tpch-no-stats | 44 +-- pkg/sql/opt/xform/testdata/external/trading | 8 +- .../xform/testdata/external/trading-mutation | 8 +- .../opt/xform/testdata/physprops/limit_hint | 6 +- pkg/sql/opt/xform/testdata/physprops/ordering | 24 +- .../opt/xform/testdata/physprops/presentation | 2 +- .../opt/xform/testdata/ruleprops/orderings | 6 +- pkg/sql/opt/xform/testdata/rules/groupby | 287 ++++++++++++++++-- pkg/sql/opt/xform/testdata/rules/join | 4 +- pkg/sql/opt_exec_factory.go | 3 + pkg/sql/testdata/explain_tree | 4 +- 111 files changed, 1358 insertions(+), 679 deletions(-) diff --git a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior index ba22a9541e1f..124cfa5b4927 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior +++ b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row_query_behavior @@ -1217,7 +1217,7 @@ LIMIT 1] OFFSET 2 │ estimated row count: 333,300 │ filter: count_rows > 1 │ - └── • group + └── • group (streaming) │ columns: (a, b, count_rows) │ estimated row count: 999,900 │ aggregate 0: count_rows() @@ -1299,7 +1299,7 @@ LIMIT 1] OFFSET 2 │ estimated row count: 111,111 │ filter: count_rows > 1 │ - └── • group + └── • group (streaming) │ columns: (b, count_rows) │ estimated row count: 333,333 │ aggregate 0: count_rows() @@ -1367,7 +1367,7 @@ LIMIT 1] OFFSET 2 │ estimated row count: 111,111 │ filter: count_rows > 1 │ - └── • group + └── • group (streaming) │ columns: (c, count_rows) │ estimated row count: 333,333 │ aggregate 0: count_rows() diff --git a/pkg/sql/distsql_spec_exec_factory.go b/pkg/sql/distsql_spec_exec_factory.go index ac108250d52e..583fe77317b2 100644 --- a/pkg/sql/distsql_spec_exec_factory.go +++ b/pkg/sql/distsql_spec_exec_factory.go @@ -549,6 +549,7 @@ func (e *distSQLSpecExecFactory) ConstructGroupBy( groupColOrdering colinfo.ColumnOrdering, aggregations []exec.AggInfo, reqOrdering exec.OutputOrdering, + groupingOrderType exec.GroupingOrderType, ) (exec.Node, error) { return e.constructAggregators( input, diff --git a/pkg/sql/opt/exec/execbuilder/relational.go b/pkg/sql/opt/exec/execbuilder/relational.go index 24597ddd7373..d97daec885ee 100644 --- a/pkg/sql/opt/exec/execbuilder/relational.go +++ b/pkg/sql/opt/exec/execbuilder/relational.go @@ -1355,8 +1355,9 @@ func (b *Builder) buildGroupBy(groupBy memo.RelExpr) (execPlan, error) { &groupBy.GroupingPrivate, &groupBy.RequiredPhysical().Ordering, )) reqOrdering := ep.reqOrdering(groupBy) + orderType := exec.GroupingOrderType(groupBy.GroupingOrderType(&groupBy.RequiredPhysical().Ordering)) ep.root, err = b.factory.ConstructGroupBy( - input.root, groupingColIdx, groupingColOrder, aggInfos, reqOrdering, + input.root, groupingColIdx, groupingColOrder, aggInfos, reqOrdering, orderType, ) } if err != nil { diff --git a/pkg/sql/opt/exec/execbuilder/testdata/aggregate b/pkg/sql/opt/exec/execbuilder/testdata/aggregate index ab94852f3978..9cc0fb92108e 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/aggregate +++ b/pkg/sql/opt/exec/execbuilder/testdata/aggregate @@ -194,7 +194,7 @@ EXPLAIN (TYPES) SELECT count(*), k FROM kv GROUP BY 2 distribution: local vectorized: true · -• group +• group (streaming) │ columns: (count int, k int) │ estimated row count: 1,000 (missing stats) │ aggregate 0: count_rows() @@ -215,7 +215,7 @@ EXPLAIN (TYPES) SELECT count(*), k+v AS r FROM kv GROUP BY k+v distribution: local vectorized: true · -• group +• group (hash) │ columns: (count int, r int) │ estimated row count: 1,000 (missing stats) │ aggregate 0: count_rows() @@ -245,7 +245,7 @@ vectorized: true │ render r: ((k)[int] + (any_not_null)[int])[int] │ render count_rows: (count_rows)[int] │ -└── • group +└── • group (streaming) │ columns: (k int, count_rows int, any_not_null int) │ estimated row count: 1,000 (missing stats) │ aggregate 0: count_rows() @@ -365,7 +365,7 @@ vectorized: true │ columns: (min) │ estimated row count: 1,000 (missing stats) │ -└── • group +└── • group (hash) │ columns: (k, min) │ estimated row count: 1,000 (missing stats) │ aggregate 0: min(k) @@ -397,7 +397,7 @@ vectorized: true │ columns: (min) │ estimated row count: 1,000 (missing stats) │ -└── • group +└── • group (hash) │ columns: (k, min) │ estimated row count: 1,000 (missing stats) │ aggregate 0: min(k) @@ -430,7 +430,7 @@ vectorized: true │ columns: (min) │ estimated row count: 1,000 (missing stats) │ -└── • group +└── • group (hash) │ columns: (k, min) │ estimated row count: 1,000 (missing stats) │ aggregate 0: min(k) @@ -1180,7 +1180,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +count_rows │ -└── • group +└── • group (hash) │ columns: (v int, count_rows int) │ estimated row count: 100 (missing stats) │ aggregate 0: count_rows() @@ -1204,7 +1204,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +count_rows │ -└── • group +└── • group (hash) │ columns: (v int, count_rows int) │ estimated row count: 100 (missing stats) │ aggregate 0: count_rows() @@ -1231,7 +1231,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +count_rows │ - └── • group + └── • group (hash) │ columns: (v int, count int, count_rows int) │ estimated row count: 100 (missing stats) │ aggregate 0: count(column7) @@ -1257,7 +1257,7 @@ EXPLAIN (VERBOSE) SELECT * FROM (SELECT v, count(NULL) FROM kv GROUP BY v) WHERE distribution: local vectorized: true · -• group +• group (hash) │ columns: (v, count) │ estimated row count: 33 (missing stats) │ aggregate 0: count(column7) @@ -1300,7 +1300,7 @@ vectorized: true │ columns: (count, max) │ estimated row count: 100 (missing stats) │ -└── • group +└── • group (hash) │ columns: (v, count, max) │ estimated row count: 100 (missing stats) │ aggregate 0: count(column7) FILTER (WHERE column8) @@ -1330,7 +1330,7 @@ vectorized: true │ columns: (count) │ estimated row count: 100 (missing stats) │ -└── • group +└── • group (hash) │ columns: (v, count) │ estimated row count: 100 (missing stats) │ aggregate 0: count(column7) FILTER (WHERE column8) @@ -1377,7 +1377,7 @@ vectorized: true │ columns: (sum decimal) │ estimated row count: 1,000 (missing stats) │ -└── • group +└── • group (hash) │ columns: (k int, sum decimal) │ estimated row count: 1,000 (missing stats) │ aggregate 0: sum(d) @@ -1505,7 +1505,7 @@ vectorized: true │ columns: (min int) │ estimated row count: 1,000 (missing stats) │ -└── • group +└── • group (streaming) │ columns: (k int, min int) │ estimated row count: 1,000 (missing stats) │ aggregate 0: min(v) @@ -1551,7 +1551,7 @@ vectorized: true │ render r: (((any_not_null)[int], (a)[int]))[tuple{int, int}] │ render min: (min)[string] │ -└── • group +└── • group (hash) │ columns: (a int, x string, min string, any_not_null int) │ estimated row count: 100,000 (missing stats) │ aggregate 0: min(y) @@ -1736,7 +1736,7 @@ vectorized: true │ columns: (m) │ estimated row count: 100 (missing stats) │ -└── • group +└── • group (hash) │ columns: (column7, min) │ estimated row count: 100 (missing stats) │ aggregate 0: min(a) @@ -1764,7 +1764,7 @@ vectorized: true │ columns: (m) │ estimated row count: 100 (missing stats) │ -└── • group +└── • group (hash) │ columns: (column7, min) │ estimated row count: 100 (missing stats) │ aggregate 0: min(a) @@ -1937,7 +1937,7 @@ EXPLAIN (VERBOSE) SELECT u, v, array_agg(w) AS s FROM (SELECT * FROM uvw ORDER B distribution: local vectorized: true · -• group +• group (streaming) │ columns: (u, v, s) │ estimated row count: 1,000 (missing stats) │ aggregate 0: array_agg(w) @@ -2001,7 +2001,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +company_id │ -└── • group +└── • group (hash) │ columns: (company_id, string_agg) │ estimated row count: 100 (missing stats) │ aggregate 0: string_agg(employee, column6) @@ -2040,7 +2040,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +company_id │ -└── • group +└── • group (hash) │ columns: (company_id, string_agg) │ estimated row count: 100 (missing stats) │ aggregate 0: string_agg(column6, column7) @@ -2079,7 +2079,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +company_id │ -└── • group +└── • group (hash) │ columns: (company_id, string_agg) │ estimated row count: 100 (missing stats) │ aggregate 0: string_agg(employee, column6) @@ -2118,7 +2118,7 @@ vectorized: true │ estimated row count: 100 (missing stats) │ order: +company_id │ -└── • group +└── • group (hash) │ columns: (company_id, string_agg) │ estimated row count: 100 (missing stats) │ aggregate 0: string_agg(column6, column7) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/distinct_on b/pkg/sql/opt/exec/execbuilder/testdata/distinct_on index 72026f14630a..9417894d362b 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/distinct_on +++ b/pkg/sql/opt/exec/execbuilder/testdata/distinct_on @@ -404,7 +404,7 @@ vectorized: true • project │ columns: (min) │ -└── • group +└── • group (hash) │ columns: (y, min) │ estimated row count: 100 (missing stats) │ aggregate 0: min(x) @@ -436,7 +436,7 @@ vectorized: true │ estimated row count: 1 (missing stats) │ filter: min = 1 │ - └── • group + └── • group (hash) │ columns: (y, min) │ estimated row count: 100 (missing stats) │ aggregate 0: min(x) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/distsql_agg b/pkg/sql/opt/exec/execbuilder/testdata/distsql_agg index 022402bb39fc..7a64544b7c77 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/distsql_agg +++ b/pkg/sql/opt/exec/execbuilder/testdata/distsql_agg @@ -52,7 +52,7 @@ FROM data GROUP BY b distribution: full vectorized: true · -• group +• group (hash) │ group by: b │ └── • scan @@ -513,7 +513,7 @@ vectorized: true · • render │ -└── • group +└── • group (hash) │ group by: c, d │ └── • render @@ -533,7 +533,7 @@ vectorized: true · • render │ -└── • group +└── • group (streaming) │ group by: c, d │ ordered: +c,+d │ @@ -558,7 +558,7 @@ vectorized: true · • render │ -└── • group +└── • group (hash) │ group by: c, d │ └── • render @@ -579,7 +579,7 @@ vectorized: true • filter │ filter: sum > 10 │ -└── • group +└── • group (hash) │ group by: d │ └── • render @@ -597,7 +597,7 @@ EXPLAIN (DISTSQL) SELECT avg(a+b), c FROM data GROUP BY c, d HAVING c = d distribution: full vectorized: true · -• group +• group (hash) │ group by: d │ └── • render @@ -618,7 +618,7 @@ EXPLAIN (DISTSQL) SELECT sum(a+b), sum(a+b) FILTER (WHERE a < d), sum(a+b) FILTE distribution: full vectorized: true · -• group +• group (hash) │ group by: d │ └── • render @@ -637,7 +637,7 @@ EXPLAIN (DISTSQL) SELECT sum(a+b), sum(a+b) FILTER (WHERE a < d), sum(a+b) FILTE distribution: full vectorized: true · -• group +• group (hash) │ group by: d │ └── • render @@ -793,7 +793,7 @@ EXPLAIN (DISTSQL) SELECT a, max(b) FROM sorted_data GROUP BY a distribution: full vectorized: true · -• group +• group (hash) │ group by: a │ └── • scan @@ -809,7 +809,7 @@ EXPLAIN (DISTSQL) SELECT a, max(b) FROM sorted_data GROUP BY a ORDER BY a distribution: full vectorized: true · -• group +• group (streaming) │ group by: a │ ordered: +a │ @@ -826,7 +826,7 @@ EXPLAIN (DISTSQL) SELECT c, min(b), a FROM sorted_data GROUP BY a, c distribution: full vectorized: true · -• group +• group (streaming) │ group by: a │ ordered: +a │ @@ -843,7 +843,7 @@ EXPLAIN (DISTSQL) SELECT c, min(b), a FROM sorted_data GROUP BY a, c ORDER BY a distribution: full vectorized: true · -• group +• group (streaming) │ group by: a │ ordered: +a │ @@ -860,7 +860,7 @@ EXPLAIN (DISTSQL) SELECT b, max(c) FROM sorted_data@foo GROUP BY b distribution: full vectorized: true · -• group +• group (streaming) │ group by: b │ ordered: +b │ @@ -885,7 +885,7 @@ vectorized: true │ left cols are key │ right cols are key │ -├── • group +├── • group (streaming) │ │ group by: a │ │ ordered: +a │ │ @@ -894,7 +894,7 @@ vectorized: true │ table: sorted_data@sorted_data_pkey │ spans: FULL SCAN │ -└── • group +└── • group (streaming) │ group by: b │ ordered: +b │ @@ -915,7 +915,7 @@ EXPLAIN (DISTSQL) SELECT sum(x) FROM (SELECT a, b::float + c AS x FROM data) GRO distribution: full vectorized: true · -• group +• group (streaming) │ group by: a │ ordered: +a │ @@ -934,7 +934,7 @@ EXPLAIN (DISTSQL) SELECT sum(x) FROM (SELECT a, b::float + c AS x FROM data) WHE distribution: full vectorized: true · -• group +• group (streaming) │ group by: a │ ordered: +a │ @@ -981,7 +981,7 @@ NULL /1/1 {2} 2 query T EXPLAIN (opt,verbose) SELECT b, count(*) FROM data2 WHERE a=1 GROUP BY b ---- -group-by +group-by (streaming) ├── columns: b:2 count:5 ├── grouping columns: b:2 ├── internal-ordering: +2 opt(1) @@ -1009,7 +1009,7 @@ EXPLAIN (verbose) SELECT b, count(*), corr(a, b) FROM data2 WHERE a=1 GROUP BY b distribution: full vectorized: true · -• group +• group (streaming) │ columns: (b, count, corr) │ estimated row count: 10 (missing stats) │ aggregate 0: count_rows() @@ -1030,7 +1030,7 @@ EXPLAIN (DISTSQL) SELECT b, count(*) FROM data2 WHERE a=1 GROUP BY b; distribution: full vectorized: true · -• group +• group (streaming) │ group by: b │ ordered: +b │ @@ -1052,7 +1052,7 @@ EXPLAIN (DISTSQL) SELECT sum(v) FROM data INNER LOOKUP JOIN uv ON (a=u) GROUP BY distribution: full vectorized: true · -• group +• group (streaming) │ group by: u │ ordered: +u │ diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain b/pkg/sql/opt/exec/execbuilder/testdata/explain index 9284c64fd92a..36dc20079260 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain @@ -424,7 +424,7 @@ vectorized: true │ └── • render │ - └── • group + └── • group (hash) │ group by: column_name, ordinal_position, column_default, is_nullable, generation_expression, is_hidden, crdb_sql_type │ └── • window @@ -517,7 +517,7 @@ vectorized: true │ └── • render │ - └── • group + └── • group (hash) │ group by: username │ └── • sort @@ -527,7 +527,7 @@ vectorized: true │ equality: (username) = (member) │ left cols are key │ - ├── • group + ├── • group (hash) │ │ group by: username │ │ │ └── • window diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans b/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans index 1359b93065da..4d2409aff67a 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans @@ -69,7 +69,7 @@ maximum memory usage: network usage: regions: · -• group +• group (streaming) │ nodes: │ regions: │ actual row count: 5 diff --git a/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial b/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial index 6fb6c126fba2..3749f680c517 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial +++ b/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial @@ -324,7 +324,7 @@ vectorized: true │ │ render count: @S2 │ │ render count_rows: count_rows │ │ -│ └── • group +│ └── • group (hash) │ │ columns: (lk, count_rows) │ │ estimated row count: 333 (missing stats) │ │ aggregate 0: count_rows() diff --git a/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial_dist b/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial_dist index 6523d8006eb4..50ec816f58b2 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial_dist +++ b/pkg/sql/opt/exec/execbuilder/testdata/inverted_join_geospatial_dist @@ -272,7 +272,7 @@ vectorized: true │ ├── • render │ │ -│ └── • group +│ └── • group (hash) │ │ group by: lk │ │ │ └── • lookup join (left outer) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/limit b/pkg/sql/opt/exec/execbuilder/testdata/limit index 0900da1f4d51..37e0f1541769 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/limit +++ b/pkg/sql/opt/exec/execbuilder/testdata/limit @@ -160,7 +160,7 @@ vectorized: true │ order: -any_not_null │ k: 10 │ - └── • group + └── • group (streaming) │ columns: (k, sum, any_not_null) │ estimated row count: 1,000 (missing stats) │ aggregate 0: sum(w) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join index 1a8829ca22e5..45057ee9ea6c 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/lookup_join +++ b/pkg/sql/opt/exec/execbuilder/testdata/lookup_join @@ -1713,7 +1713,7 @@ vectorized: true │ order: -count_rows,+s_name │ k: 100 │ -└── • group +└── • group (hash) │ estimated row count: 0 │ group by: s_name │ diff --git a/pkg/sql/opt/exec/execbuilder/testdata/select_index b/pkg/sql/opt/exec/execbuilder/testdata/select_index index 0ccecfc344d0..ceb17a73fc1f 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/select_index +++ b/pkg/sql/opt/exec/execbuilder/testdata/select_index @@ -921,7 +921,7 @@ vectorized: true • sort │ order: -count_rows │ -└── • group +└── • group (streaming) │ group by: resource_key │ ordered: +resource_key │ diff --git a/pkg/sql/opt/exec/execbuilder/testdata/srfs b/pkg/sql/opt/exec/execbuilder/testdata/srfs index bad23fb5365d..755834e3c7ab 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/srfs +++ b/pkg/sql/opt/exec/execbuilder/testdata/srfs @@ -431,7 +431,7 @@ vectorized: true │ order: +any_not_null │ k: 10 │ -└── • group +└── • group (hash) │ columns: (id, any_not_null, any_not_null, any_not_null, any_not_null, any_not_null, any_not_null, any_not_null, any_not_null) │ estimated row count: 14 (missing stats) │ aggregate 0: any_not_null(body) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/subquery_correlated b/pkg/sql/opt/exec/execbuilder/testdata/subquery_correlated index da028d7b7cb8..f08b31baf4a9 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/subquery_correlated +++ b/pkg/sql/opt/exec/execbuilder/testdata/subquery_correlated @@ -27,7 +27,7 @@ vectorized: true │ └── • render │ - └── • group + └── • group (hash) │ group by: c_id │ └── • sort diff --git a/pkg/sql/opt/exec/execbuilder/testdata/window b/pkg/sql/opt/exec/execbuilder/testdata/window index 396862171de0..e9450dff6b85 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/window +++ b/pkg/sql/opt/exec/execbuilder/testdata/window @@ -290,7 +290,7 @@ vectorized: true │ estimated row count: 1,000 (missing stats) │ window 0: (stddev((d)[decimal]) OVER (PARTITION BY (v)[int] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW))[decimal] │ - └── • group + └── • group (hash) │ columns: (v int, d decimal, max int) │ estimated row count: 1,000 (missing stats) │ aggregate 0: max(k) @@ -323,7 +323,7 @@ vectorized: true │ estimated row count: 1,000 (missing stats) │ window 0: (stddev((d)[decimal]) OVER (PARTITION BY (v)[int] RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW))[decimal] │ - └── • group + └── • group (hash) │ columns: (v int, d decimal, max int) │ estimated row count: 1,000 (missing stats) │ aggregate 0: max(k) diff --git a/pkg/sql/opt/exec/explain/emit.go b/pkg/sql/opt/exec/explain/emit.go index 5abd834dab32..e54daa4c9220 100644 --- a/pkg/sql/opt/exec/explain/emit.go +++ b/pkg/sql/opt/exec/explain/emit.go @@ -201,6 +201,19 @@ func (e *emitter) nodeName(n *Node) (string, error) { return "values", nil } + case groupByOp: + a := n.args.(*groupByArgs) + switch a.groupingOrderType { + case exec.Streaming: + return "group (streaming)", nil + case exec.PartialStreaming: + return "group (partial streaming)", nil + case exec.NoStreaming: + return "group (hash)", nil + default: + return "", errors.AssertionFailedf("unhandled group by order type %d", a.groupingOrderType) + } + case hashJoinOp: a := n.args.(*hashJoinArgs) if len(n.args.(*hashJoinArgs).LeftEqCols) == 0 { @@ -280,7 +293,7 @@ var nodeNames = [...]string{ explainOptOp: "explain", exportOp: "export", filterOp: "filter", - groupByOp: "group", + groupByOp: "", // This node does not have a fixed name. hashJoinOp: "", // This node does not have a fixed name. indexJoinOp: "index join", insertFastPathOp: "insert fast path", diff --git a/pkg/sql/opt/exec/explain/testdata/gists b/pkg/sql/opt/exec/explain/testdata/gists index b2503fc51cd2..85f7091d8151 100644 --- a/pkg/sql/opt/exec/explain/testdata/gists +++ b/pkg/sql/opt/exec/explain/testdata/gists @@ -208,7 +208,7 @@ SELECT c, count(*) FROM foo, bar WHERE a = 1 GROUP BY c hash: 6889256940365688364 plan-gist: AgFsAgAAAAAAAWoCAAUCAAAJAAAAAAEFAgsABgQ= explain(shape): -• group +• group (streaming) │ └── • cross join │ @@ -222,7 +222,7 @@ explain(shape): table: foo@foo_pkey spans: 1 span explain(gist): -• group +• group (hash) │ └── • cross join │ diff --git a/pkg/sql/opt/exec/explain/testdata/gists_tpce b/pkg/sql/opt/exec/explain/testdata/gists_tpce index 4ba1a3576ea0..ffcc47004de7 100644 --- a/pkg/sql/opt/exec/explain/testdata/gists_tpce +++ b/pkg/sql/opt/exec/explain/testdata/gists_tpce @@ -29,7 +29,7 @@ explain(shape): │ └── • render │ - └── • group + └── • group (hash) │ group by: b_name │ └── • render @@ -71,7 +71,7 @@ explain(gist): │ └── • render │ - └── • group + └── • group (hash) │ group by: _ │ └── • render @@ -126,7 +126,7 @@ explain(shape): │ └── • render │ - └── • group + └── • group (streaming) │ group by: ca_id │ ordered: +ca_id │ @@ -154,7 +154,7 @@ explain(gist): │ └── • render │ - └── • group + └── • group (hash) │ group by: _ │ └── • render diff --git a/pkg/sql/opt/exec/factory.go b/pkg/sql/opt/exec/factory.go index 83f3ae70b947..557befb29c03 100644 --- a/pkg/sql/opt/exec/factory.go +++ b/pkg/sql/opt/exec/factory.go @@ -342,3 +342,18 @@ type ExecutionStats struct { // BuildPlanForExplainFn builds an execution plan against the given // base factory. type BuildPlanForExplainFn func(f Factory) (Plan, error) + +// GroupingOrderType is the grouping column order type for group by and distinct +// operations. +type GroupingOrderType int + +const ( + // NoStreaming means that the grouping columns have no useful order, so a + // hash aggregator should be used. + NoStreaming GroupingOrderType = iota + // PartialStreaming means that the grouping columns are partially ordered, so + // some optimizations can be done during aggregation. + PartialStreaming + // Streaming means that the grouping columns are fully ordered. + Streaming +) diff --git a/pkg/sql/opt/exec/factory.opt b/pkg/sql/opt/exec/factory.opt index 1e433b30d3cb..9bc1f03f5d3e 100644 --- a/pkg/sql/opt/exec/factory.opt +++ b/pkg/sql/opt/exec/factory.opt @@ -138,6 +138,10 @@ define GroupBy { # If set, the output must have this ordering, but it is guaranteed that # ReqOrdering is a prefix of GroupColOrdering. ReqOrdering exec.OutputOrdering + + # The grouping column order type (Streaming, PartialStreaming, or + # NoStreaming). + groupingOrderType exec.GroupingOrderType } # ScalarGroupBy runs a scalar aggregation, i.e. one which performs a set of diff --git a/pkg/sql/opt/memo/expr.go b/pkg/sql/opt/memo/expr.go index d2a32ea3abac..d52a526e0744 100644 --- a/pkg/sql/opt/memo/expr.go +++ b/pkg/sql/opt/memo/expr.go @@ -387,7 +387,7 @@ const ( // DisallowHashJoinStoreLeft corresponds to a hash join where the left side is // stored into the hashtable. Note that execution can override the stored side // if it finds that the other side is smaller (up to a certain size). - DisallowHashJoinStoreLeft JoinFlags = (1 << iota) + DisallowHashJoinStoreLeft JoinFlags = 1 << iota // DisallowHashJoinStoreRight corresponds to a hash join where the right side // is stored into the hashtable. Note that execution can override the stored @@ -423,28 +423,28 @@ const ( ) const ( - disallowAll JoinFlags = (DisallowHashJoinStoreLeft | + disallowAll = DisallowHashJoinStoreLeft | DisallowHashJoinStoreRight | DisallowMergeJoin | DisallowLookupJoinIntoLeft | DisallowLookupJoinIntoRight | DisallowInvertedJoinIntoLeft | - DisallowInvertedJoinIntoRight) + DisallowInvertedJoinIntoRight // AllowOnlyHashJoinStoreRight has all "disallow" flags set except // DisallowHashJoinStoreRight. - AllowOnlyHashJoinStoreRight JoinFlags = disallowAll ^ DisallowHashJoinStoreRight + AllowOnlyHashJoinStoreRight = disallowAll ^ DisallowHashJoinStoreRight // AllowOnlyLookupJoinIntoRight has all "disallow" flags set except // DisallowLookupJoinIntoRight. - AllowOnlyLookupJoinIntoRight JoinFlags = disallowAll ^ DisallowLookupJoinIntoRight + AllowOnlyLookupJoinIntoRight = disallowAll ^ DisallowLookupJoinIntoRight // AllowOnlyInvertedJoinIntoRight has all "disallow" flags set except // DisallowInvertedJoinIntoRight. - AllowOnlyInvertedJoinIntoRight JoinFlags = disallowAll ^ DisallowInvertedJoinIntoRight + AllowOnlyInvertedJoinIntoRight = disallowAll ^ DisallowInvertedJoinIntoRight // AllowOnlyMergeJoin has all "disallow" flags set except DisallowMergeJoin. - AllowOnlyMergeJoin JoinFlags = disallowAll ^ DisallowMergeJoin + AllowOnlyMergeJoin = disallowAll ^ DisallowMergeJoin ) var joinFlagStr = map[JoinFlags]string{ @@ -1032,3 +1032,45 @@ type CascadeBuilder interface { oldValues, newValues opt.ColList, ) (RelExpr, error) } + +// GroupingOrderType is the grouping column order type for group by and distinct +// operations in the memo. +type GroupingOrderType int + +const ( + // NoStreaming means that the grouping columns have no useful order, so a + // hash aggregator should be used. + NoStreaming GroupingOrderType = iota + // PartialStreaming means that the grouping columns are partially ordered, so + // some optimizations can be done during aggregation. + PartialStreaming + // Streaming means that the grouping columns are fully ordered. + Streaming +) + +// GroupingOrderType calculates how many ordered columns that the grouping +// and input columns have in common and returns NoStreaming if there are none, Streaming if +// all columns match, and PartialStreaming if only some match. It is similar to +// StreamingGroupingColOrdering, but does not build an ordering. +func (g *GroupingPrivate) GroupingOrderType(required *props.OrderingChoice) GroupingOrderType { + inputOrdering := required.Intersection(&g.Ordering) + count := 0 + for i := range inputOrdering.Columns { + // Get any grouping column from the set. Normally there would be at most one + // because we have rules that remove redundant grouping columns. + cols := inputOrdering.Group(i).Intersection(g.GroupingCols) + _, ok := cols.Next(0) + if !ok { + // This group refers to a column that is not a grouping column. + // The rest of the ordering is not useful. + break + } + count++ + } + if count == g.GroupingCols.Len() || g.GroupingCols.Len() == 0 { + return Streaming + } else if count == 0 { + return NoStreaming + } + return PartialStreaming +} diff --git a/pkg/sql/opt/memo/expr_format.go b/pkg/sql/opt/memo/expr_format.go index 2eeb034061cb..b3a5b9b5cd0e 100644 --- a/pkg/sql/opt/memo/expr_format.go +++ b/pkg/sql/opt/memo/expr_format.go @@ -216,6 +216,17 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { fmt.Fprintf(f.Buffer, "%v", e.Op()) FormatPrivate(f, e.Private(), required) + case *GroupByExpr: + fmt.Fprintf(f.Buffer, "%v ", e.Op()) + groupingColOrderType := e.Private().(*GroupingPrivate).GroupingOrderType(&required.Ordering) + if groupingColOrderType == Streaming { + fmt.Fprintf(f.Buffer, "(streaming)") + } else if groupingColOrderType == PartialStreaming { + fmt.Fprintf(f.Buffer, "(partial streaming)") + } else { + fmt.Fprintf(f.Buffer, "(hash)") + } + case *SortExpr: if t.InputOrdering.Any() { fmt.Fprintf(f.Buffer, "%v", e.Op()) diff --git a/pkg/sql/opt/memo/testdata/format b/pkg/sql/opt/memo/testdata/format index 4202c4c93f02..dc0a8ef826e6 100644 --- a/pkg/sql/opt/memo/testdata/format +++ b/pkg/sql/opt/memo/testdata/format @@ -23,7 +23,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: t.public.t.a:1(int) min:6(int!null) │ ├── grouping columns: t.public.t.a:1(int) │ ├── immutable @@ -75,7 +75,7 @@ project │ ├── stats: [rows=98.1771622, distinct(1)=98.1771622, null(1)=1] │ ├── cost: 1122.50329 │ ├── ordering: +1 - │ └── group-by + │ └── group-by (hash) │ ├── columns: t.public.t.a:1(int) min:6(int!null) │ ├── grouping columns: t.public.t.a:1(int) │ ├── stats: [rows=98.1771622, distinct(1)=98.1771622, null(1)=1] @@ -119,7 +119,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: a:1(int) min:6(int!null) │ ├── grouping columns: a:1(int) │ ├── immutable @@ -163,7 +163,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: a:1 min:6!null │ ├── grouping columns: a:1 │ ├── immutable @@ -207,7 +207,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: a:1(int) min:6(int) │ ├── grouping columns: a:1(int) │ ├── immutable @@ -251,7 +251,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: a:1 min:6 │ ├── grouping columns: a:1 │ ├── immutable @@ -293,7 +293,7 @@ project │ ├── key: (1) │ ├── fd: (1)-->(6) │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── stats: [rows=98.1771622, distinct(1)=98.1771622, null(1)=1] │ ├── cost: 1105.5623 │ ├── key: (1) @@ -331,7 +331,7 @@ SELECT a + 1, min(b) FROM t WHERE k + a > b GROUP BY a ORDER BY a ---- project ├── sort - │ └── group-by + │ └── group-by (hash) │ ├── select │ │ ├── scan t │ │ └── filters @@ -411,7 +411,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: t.public.t.a:1(int) min:6(int!null) │ ├── grouping columns: t.public.t.a:1(int) │ ├── immutable @@ -476,7 +476,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: t.public.t.a:1(int) min:6(int!null) │ ├── grouping columns: t.public.t.a:1(int) │ ├── immutable @@ -535,7 +535,7 @@ project │ ├── fd: (1)-->(6) │ ├── ordering: +1 │ ├── prune: (6) - │ └── group-by + │ └── group-by (hash) │ ├── columns: t.public.t.a:1(int) min:6(int!null) │ ├── grouping columns: t.public.t.a:1(int) │ ├── immutable diff --git a/pkg/sql/opt/memo/testdata/logprops/groupby b/pkg/sql/opt/memo/testdata/logprops/groupby index f42611fe790c..e2017b4decab 100644 --- a/pkg/sql/opt/memo/testdata/logprops/groupby +++ b/pkg/sql/opt/memo/testdata/logprops/groupby @@ -19,7 +19,7 @@ project ├── fd: ()-->(12), (1)-->(2,7,9,11) ├── prune: (1,2,7,9,11,12) ├── interesting orderings: (+1 opt(12)) - ├── group-by + ├── group-by (hash) │ ├── columns: x:1(int!null) y:2(int) sum:7(float!null) avg:9(float) string_agg:11(string!null) │ ├── grouping columns: x:1(int!null) y:2(int) │ ├── key: (1) @@ -105,7 +105,7 @@ project ├── columns: s:4(string) ├── prune: (4) ├── interesting orderings: (-4) - └── group-by + └── group-by (hash) ├── columns: z:3(float!null) s:4(string) ├── grouping columns: z:3(float!null) s:4(string) ├── key: (3,4) @@ -129,7 +129,7 @@ SELECT y, sum(z) FROM xyzs GROUP BY z, y project ├── columns: y:2(int) sum:7(float!null) ├── prune: (2,7) - └── group-by + └── group-by (hash) ├── columns: y:2(int) z:3(float!null) sum:7(float!null) ├── grouping columns: y:2(int) z:3(float!null) ├── key: (2,3) @@ -152,7 +152,7 @@ project build SELECT z, max(s) FROM xyzs GROUP BY z ---- -group-by +group-by (hash) ├── columns: z:3(float!null) max:7(string) ├── grouping columns: z:3(float!null) ├── key: (3) @@ -181,7 +181,7 @@ project ├── columns: s:4(string) ├── prune: (4) ├── interesting orderings: (-4) - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) ├── grouping columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) ├── key: (1) @@ -207,7 +207,7 @@ SELECT (SELECT sum(x) FROM (SELECT y, u FROM kuv) GROUP BY u) FROM xyzs GROUP BY project ├── columns: sum:16(decimal) ├── prune: (16) - ├── group-by + ├── group-by (hash) │ ├── columns: xyzs.y:2(int) sum:14(decimal!null) │ ├── grouping columns: xyzs.y:2(int) │ ├── key: (2) @@ -244,7 +244,7 @@ project ├── outer: (2,14) ├── fd: ()-->(15) ├── prune: (15) - ├── group-by + ├── group-by (hash) │ ├── columns: u:8(float) │ ├── grouping columns: u:8(float) │ ├── outer: (2) @@ -273,7 +273,7 @@ project build SELECT * FROM (VALUES (1), (2), (1), (NULL)) GROUP BY column1 ---- -group-by +group-by (hash) ├── columns: column1:1(int) ├── grouping columns: column1:1(int) ├── cardinality: [1 - 4] @@ -298,7 +298,7 @@ group-by opt SELECT x, count(y) FROM xyzs GROUP BY x HAVING x=1 ---- -group-by +group-by (streaming) ├── columns: x:1(int!null) count:7(int!null) ├── cardinality: [0 - 1] ├── key: () @@ -327,7 +327,7 @@ GROUP BY x, y project ├── columns: variance:7(decimal) stddev:8(decimal) corr:9(float) ├── prune: (7-9) - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) y:2(int) variance:7(decimal) stddev:8(decimal) corr:9(float) ├── grouping columns: x:1(int!null) y:2(int) ├── key: (1) diff --git a/pkg/sql/opt/memo/testdata/logprops/join b/pkg/sql/opt/memo/testdata/logprops/join index 801948b3ad2b..1ed4d0a0f1f4 100644 --- a/pkg/sql/opt/memo/testdata/logprops/join +++ b/pkg/sql/opt/memo/testdata/logprops/join @@ -1294,7 +1294,7 @@ project │ ├── prune: (1-6) │ ├── interesting orderings: (+1) (-3,+4,+1) │ └── unfiltered-cols: (1-6) - ├── group-by + ├── group-by (hash) │ ├── columns: u:7(int) sum:12(decimal!null) │ ├── grouping columns: u:7(int) │ ├── key: (7) @@ -1342,7 +1342,7 @@ project │ ├── prune: (1-6) │ ├── interesting orderings: (+1) (-3,+4,+1) │ └── unfiltered-cols: (1-6) - ├── group-by + ├── group-by (hash) │ ├── columns: u:7(int!null) sum:12(decimal!null) │ ├── grouping columns: u:7(int!null) │ ├── key: (7) @@ -1391,7 +1391,7 @@ project ├── prune: (6-12) ├── reject-nulls: (1,6) ├── interesting orderings: (+7) (-9,+10,+7) - ├── group-by + ├── group-by (hash) │ ├── columns: u:1(int) sum:6(decimal!null) │ ├── grouping columns: u:1(int) │ ├── key: (1) @@ -1438,7 +1438,7 @@ project ├── prune: (6-12) ├── reject-nulls: (1,6) ├── interesting orderings: (+7) (-9,+10,+7) - ├── group-by + ├── group-by (hash) │ ├── columns: u:1(int!null) sum:6(decimal!null) │ ├── grouping columns: u:1(int!null) │ ├── key: (1) @@ -1500,7 +1500,7 @@ project │ ├── prune: (1-6) │ ├── interesting orderings: (+1) (-3,+4,+1) │ └── unfiltered-cols: (1-6) - ├── group-by + ├── group-by (hash) │ ├── columns: u:7(int) sum:12(decimal!null) │ ├── grouping columns: u:7(int) │ ├── key: (7) @@ -1541,7 +1541,7 @@ project ├── prune: (6-12) ├── reject-nulls: (1,6-12) ├── interesting orderings: (+7) (-9,+10,+7) - ├── group-by + ├── group-by (hash) │ ├── columns: u:1(int) sum:6(decimal!null) │ ├── grouping columns: u:1(int) │ ├── key: (1) diff --git a/pkg/sql/opt/memo/testdata/logprops/scalar b/pkg/sql/opt/memo/testdata/logprops/scalar index 9ca550679ea0..b771df04b821 100644 --- a/pkg/sql/opt/memo/testdata/logprops/scalar +++ b/pkg/sql/opt/memo/testdata/logprops/scalar @@ -216,7 +216,7 @@ INNER JOIN (SELECT * FROM uv WHERE now() > '2018-01-01') ON x=u GROUP BY div ---- -group-by +group-by (hash) ├── columns: sum:11(decimal!null) div:5(decimal) ├── grouping columns: div:5(decimal) ├── stable diff --git a/pkg/sql/opt/memo/testdata/stats/groupby b/pkg/sql/opt/memo/testdata/stats/groupby index 64a7fc9c459a..a58f6171c78d 100644 --- a/pkg/sql/opt/memo/testdata/stats/groupby +++ b/pkg/sql/opt/memo/testdata/stats/groupby @@ -39,7 +39,7 @@ project ├── columns: x:1(int!null) ├── stats: [rows=2000] ├── key: (1) - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) y:2(int) ├── grouping columns: x:1(int!null) y:2(int) ├── stats: [rows=2000, distinct(1,2)=2000, null(1,2)=0] @@ -63,7 +63,7 @@ SELECT max(y) FROM a GROUP BY x project ├── columns: max:7(int) ├── stats: [rows=2000] - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) max:7(int) ├── grouping columns: x:1(int!null) ├── stats: [rows=2000, distinct(1)=2000, null(1)=0] @@ -87,7 +87,7 @@ project build SELECT y, sum(z) FROM a GROUP BY y ---- -group-by +group-by (hash) ├── columns: y:2(int) sum:7(float!null) ├── grouping columns: y:2(int) ├── stats: [rows=400, distinct(2)=400, null(2)=0] @@ -111,7 +111,7 @@ SELECT max(x) FROM a GROUP BY y, z, s project ├── columns: max:7(int!null) ├── stats: [rows=600] - └── group-by + └── group-by (hash) ├── columns: y:2(int) z:3(float!null) s:4(string) max:7(int!null) ├── grouping columns: y:2(int) z:3(float!null) s:4(string) ├── stats: [rows=600, distinct(2-4)=600, null(2-4)=0] @@ -137,7 +137,7 @@ SELECT min(x) FROM a GROUP BY y, z project ├── columns: min:7(int!null) ├── stats: [rows=2000] - └── group-by + └── group-by (hash) ├── columns: y:2(int) z:3(float!null) min:7(int!null) ├── grouping columns: y:2(int) z:3(float!null) ├── stats: [rows=2000, distinct(2,3)=2000, null(2,3)=0] @@ -168,7 +168,7 @@ project ├── stats: [rows=120, distinct(4)=2, null(4)=0] ├── key: (3,4) ├── fd: (3,4)-->(2), (2-4)-->(7) - ├── group-by + ├── group-by (hash) │ ├── columns: y:2(int) z:3(float!null) s:4(string) max:7(int!null) │ ├── grouping columns: y:2(int) z:3(float!null) s:4(string) │ ├── stats: [rows=600, distinct(3)=200, null(3)=0, distinct(4)=10, null(4)=0, distinct(7)=600, null(7)=0, distinct(2-4)=600, null(2-4)=0] @@ -200,7 +200,7 @@ select ├── stats: [rows=1, distinct(7)=1, null(7)=0] ├── key: (4) ├── fd: ()-->(7) - ├── group-by + ├── group-by (hash) │ ├── columns: s:4(string) sum:7(decimal!null) │ ├── grouping columns: s:4(string) │ ├── stats: [rows=10, distinct(4)=10, null(4)=0, distinct(7)=10, null(7)=0] @@ -295,7 +295,7 @@ project ├── columns: x:1(int!null) ├── stats: [rows=2000] ├── key: (1) - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) y:2(int) ├── grouping columns: x:1(int!null) y:2(int) ├── stats: [rows=2000, distinct(1,2)=2000, null(1,2)=0] @@ -319,7 +319,7 @@ SELECT max(y) FROM a GROUP BY x project ├── columns: max:7(int) ├── stats: [rows=2000] - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) max:7(int) ├── grouping columns: x:1(int!null) ├── stats: [rows=2000, distinct(1)=2000, null(1)=0] @@ -343,7 +343,7 @@ project build SELECT y, sum(z) FROM a GROUP BY y ---- -group-by +group-by (hash) ├── columns: y:2(int) sum:7(float!null) ├── grouping columns: y:2(int) ├── stats: [rows=400, distinct(2)=400, null(2)=1] @@ -367,7 +367,7 @@ SELECT max(x) FROM a GROUP BY y, z, s project ├── columns: max:7(int!null) ├── stats: [rows=600] - └── group-by + └── group-by (hash) ├── columns: y:2(int) z:3(float!null) s:4(string) max:7(int!null) ├── grouping columns: y:2(int) z:3(float!null) s:4(string) ├── stats: [rows=600, distinct(2-4)=600, null(2-4)=0] @@ -393,7 +393,7 @@ SELECT min(x) FROM a GROUP BY y, z project ├── columns: min:7(int!null) ├── stats: [rows=2000] - └── group-by + └── group-by (hash) ├── columns: y:2(int) z:3(float!null) min:7(int!null) ├── grouping columns: y:2(int) z:3(float!null) ├── stats: [rows=2000, distinct(2,3)=2000, null(2,3)=0] @@ -424,7 +424,7 @@ project ├── stats: [rows=133.111111, distinct(4)=2, null(4)=0] ├── key: (3,4) ├── fd: (3,4)-->(2), (2-4)-->(7) - ├── group-by + ├── group-by (hash) │ ├── columns: y:2(int) z:3(float!null) s:4(string) max:7(int!null) │ ├── grouping columns: y:2(int) z:3(float!null) s:4(string) │ ├── stats: [rows=600, distinct(3)=200, null(3)=0, distinct(4)=10, null(4)=1, distinct(7)=600, null(7)=0, distinct(2-4)=600, null(2-4)=0] @@ -456,7 +456,7 @@ select ├── stats: [rows=1, distinct(7)=1, null(7)=0] ├── key: (4) ├── fd: ()-->(7) - ├── group-by + ├── group-by (hash) │ ├── columns: s:4(string) sum:7(decimal!null) │ ├── grouping columns: s:4(string) │ ├── stats: [rows=10, distinct(4)=10, null(4)=1, distinct(7)=10, null(7)=0] @@ -498,7 +498,7 @@ project │ ├── stats: [rows=1, distinct(5)=1, null(5)=0] │ ├── key: (4) │ ├── fd: ()-->(5) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: b:4(int) bool_or:5(bool!null) │ │ ├── grouping columns: b:4(int) │ │ ├── cardinality: [0 - 3] diff --git a/pkg/sql/opt/memo/testdata/stats/index-join b/pkg/sql/opt/memo/testdata/stats/index-join index ffe6c0f95d16..6b63aae5ea3e 100644 --- a/pkg/sql/opt/memo/testdata/stats/index-join +++ b/pkg/sql/opt/memo/testdata/stats/index-join @@ -41,7 +41,7 @@ project ├── columns: count:7(int!null) ├── immutable ├── stats: [rows=28.5478625] - └── group-by + └── group-by (hash) ├── columns: y:2(int) count_rows:7(int!null) ├── grouping columns: y:2(int) ├── immutable diff --git a/pkg/sql/opt/memo/testdata/stats/inverted-geo b/pkg/sql/opt/memo/testdata/stats/inverted-geo index 57a68651257a..a7aeeb0a3142 100644 --- a/pkg/sql/opt/memo/testdata/stats/inverted-geo +++ b/pkg/sql/opt/memo/testdata/stats/inverted-geo @@ -756,7 +756,7 @@ project ├── columns: count:21(int!null) ├── immutable ├── stats: [rows=0.333333333] - └── group-by + └── group-by (hash) ├── columns: b:2(geography!null) count_rows:21(int!null) ├── grouping columns: b:2(geography!null) ├── immutable diff --git a/pkg/sql/opt/memo/testdata/stats/join b/pkg/sql/opt/memo/testdata/stats/join index 008d380c3673..bc66c135e9cf 100644 --- a/pkg/sql/opt/memo/testdata/stats/join +++ b/pkg/sql/opt/memo/testdata/stats/join @@ -282,7 +282,7 @@ project build SELECT sum(v), x, v FROM xysd, uv GROUP BY x, v ---- -group-by +group-by (hash) ├── columns: sum:12(decimal!null) x:1(int!null) v:8(int!null) ├── grouping columns: x:1(int!null) v:8(int!null) ├── stats: [rows=500000, distinct(1,8)=500000, null(1,8)=0] @@ -315,7 +315,7 @@ group-by norm SELECT sum(v), x, v FROM xysd, uv WHERE x=u GROUP BY x, v ---- -group-by +group-by (hash) ├── columns: sum:12(decimal!null) x:1(int!null) v:8(int!null) ├── grouping columns: x:1(int!null) v:8(int!null) ├── stats: [rows=10000, distinct(1,8)=10000, null(1,8)=0] @@ -439,7 +439,7 @@ project ├── columns: count:12(int!null) ├── immutable ├── stats: [rows=138.170075] - └── group-by + └── group-by (hash) ├── columns: y:2(int) count_rows:12(int!null) ├── grouping columns: y:2(int) ├── immutable @@ -476,7 +476,7 @@ project ├── columns: count:12(int!null) ├── immutable ├── stats: [rows=400] - └── group-by + └── group-by (hash) ├── columns: y:2(int) count_rows:12(int!null) ├── grouping columns: y:2(int) ├── immutable @@ -513,7 +513,7 @@ project ├── columns: count:12(int!null) ├── immutable ├── stats: [rows=400] - └── group-by + └── group-by (hash) ├── columns: y:2(int) count_rows:12(int!null) ├── grouping columns: y:2(int) ├── immutable @@ -550,7 +550,7 @@ project ├── columns: count:12(int!null) ├── immutable ├── stats: [rows=399.903879] - └── group-by + └── group-by (hash) ├── columns: y:2(int) count_rows:12(int!null) ├── grouping columns: y:2(int) ├── immutable @@ -587,7 +587,7 @@ project ├── columns: count:12(int!null) ├── immutable ├── stats: [rows=400] - └── group-by + └── group-by (hash) ├── columns: y:2(int) count_rows:12(int!null) ├── grouping columns: y:2(int) ├── immutable diff --git a/pkg/sql/opt/memo/testdata/stats/project b/pkg/sql/opt/memo/testdata/stats/project index dbe21e679ad5..0ca6b6093502 100644 --- a/pkg/sql/opt/memo/testdata/stats/project +++ b/pkg/sql/opt/memo/testdata/stats/project @@ -62,7 +62,7 @@ SELECT count(*) FROM (SELECT x, y FROM a) GROUP BY x, y project ├── columns: count:7(int!null) ├── stats: [rows=2000] - └── group-by + └── group-by (hash) ├── columns: x:1(int!null) y:2(int) count_rows:7(int!null) ├── grouping columns: x:1(int!null) y:2(int) ├── stats: [rows=2000, distinct(1,2)=2000, null(1,2)=0] @@ -108,7 +108,7 @@ select build SELECT * FROM (SELECT concat(s, y::string), x FROM a) AS q(v, x) GROUP BY v, x ---- -group-by +group-by (hash) ├── columns: v:7(string) x:1(int!null) ├── grouping columns: x:1(int!null) concat:7(string) ├── immutable @@ -193,7 +193,7 @@ project │ └── fd: (1)-->(2-6), (3,4)~~>(1,2,5,6) └── filters └── exists [type=bool, outer=(3), correlated-subquery] - └── group-by + └── group-by (hash) ├── columns: column12:12(bool) ├── grouping columns: column12:12(bool) ├── outer: (3) diff --git a/pkg/sql/opt/memo/testdata/stats/scan b/pkg/sql/opt/memo/testdata/stats/scan index 72f79f234e68..808141632d77 100644 --- a/pkg/sql/opt/memo/testdata/stats/scan +++ b/pkg/sql/opt/memo/testdata/stats/scan @@ -132,7 +132,7 @@ scan a opt SELECT count(*), y, x FROM a WHERE x > 0 AND x <= 100 GROUP BY x, y ---- -group-by +group-by (streaming) ├── columns: count:8(int!null) y:2(int) x:1(int!null) ├── grouping columns: x:1(int!null) ├── internal-ordering: +1 diff --git a/pkg/sql/opt/memo/testdata/stats/select b/pkg/sql/opt/memo/testdata/stats/select index 61ac9b344def..270f226ae382 100644 --- a/pkg/sql/opt/memo/testdata/stats/select +++ b/pkg/sql/opt/memo/testdata/stats/select @@ -150,7 +150,7 @@ SELECT sum(x) FROM b WHERE x > 1000 AND x <= 2000 GROUP BY z project ├── columns: sum:6(decimal!null) ├── stats: [rows=100] - └── group-by + └── group-by (hash) ├── columns: z:2(int!null) sum:6(decimal!null) ├── grouping columns: z:2(int!null) ├── stats: [rows=100, distinct(2)=100, null(2)=0] @@ -236,7 +236,7 @@ SELECT count(*) FROM (SELECT * FROM tab0 WHERE col3 = 10) GROUP BY col0 project ├── columns: count:10(int!null) ├── stats: [rows=0.999973439] - └── group-by + └── group-by (hash) ├── columns: col0:2(int) count_rows:10(int!null) ├── grouping columns: col0:2(int) ├── stats: [rows=0.999973439, distinct(2)=0.999973439, null(2)=0.999973439] @@ -1364,7 +1364,7 @@ project │ ├── stats: [rows=1, distinct(4)=1, null(4)=0] │ ├── key: (2,3) │ ├── fd: ()-->(4) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: column2:2(string) column3:3(varbit) min:4(bool!null) │ │ ├── grouping columns: column2:2(string) column3:3(varbit) │ │ ├── cardinality: [1 - 2] diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpcc b/pkg/sql/opt/memo/testdata/stats_quality/tpcc index ef2c80e50978..fca73af65b92 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpcc +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpcc @@ -738,7 +738,7 @@ scalar-group-by │ │ ├── key: (1) │ │ ├── fd: (1)-->(9) │ │ └── ordering: +1 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── save-table-name: consistency_01_group_by_4 │ │ ├── columns: d_w_id:13(int!null) sum:25(decimal) │ │ ├── grouping columns: d_w_id:13(int!null) @@ -852,7 +852,7 @@ GROUP BY no_d_id, no_w_id ORDER BY no_w_id, no_d_id ---- ---- -group-by +group-by (streaming) ├── save-table-name: consistency_03_group_by_1 ├── columns: max:6(int!null) [hidden: no_d_id:2(int!null) no_w_id:3(int!null)] ├── grouping columns: no_d_id:2(int!null) no_w_id:3(int!null) @@ -907,7 +907,7 @@ GROUP BY o_d_id, o_w_id ORDER BY o_w_id, o_d_id ---- ---- -group-by +group-by (streaming) ├── save-table-name: consistency_04_group_by_1 ├── columns: max:11(int!null) [hidden: o_d_id:2(int!null) o_w_id:3(int!null)] ├── grouping columns: o_d_id:2(int!null) o_w_id:3(int!null) @@ -981,7 +981,7 @@ scalar-group-by │ ├── stats: [rows=33.3333333, distinct(2)=9.8265847, null(2)=0, distinct(3)=9.8265847, null(3)=0, distinct(6)=33.3333333, null(6)=0, distinct(7)=33.3333333, null(7)=0, distinct(8)=33.3333333, null(8)=0] │ ├── key: (2,3) │ ├── fd: (2,3)-->(6-8) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── save-table-name: consistency_05_group_by_3 │ │ ├── columns: no_d_id:2(int!null) no_w_id:3(int!null) max:6(int!null) min:7(int!null) count_rows:8(int!null) │ │ ├── grouping columns: no_d_id:2(int!null) no_w_id:3(int!null) @@ -1069,7 +1069,7 @@ GROUP BY o_w_id, o_d_id ORDER BY o_w_id, o_d_id ---- ---- -group-by +group-by (streaming) ├── save-table-name: consistency_06_group_by_1 ├── columns: sum:11(decimal) [hidden: o_d_id:2(int!null) o_w_id:3(int!null)] ├── grouping columns: o_d_id:2(int!null) o_w_id:3(int!null) @@ -1123,7 +1123,7 @@ GROUP BY ol_w_id, ol_d_id ORDER BY ol_w_id, ol_d_id ---- ---- -group-by +group-by (streaming) ├── save-table-name: consistency_07_group_by_1 ├── columns: count:13(int!null) [hidden: ol_d_id:2(int!null) ol_w_id:3(int!null)] ├── grouping columns: ol_d_id:2(int!null) ol_w_id:3(int!null) @@ -1434,7 +1434,7 @@ except-all │ ├── key: (1-3) │ ├── fd: (1-3)-->(7) │ └── ordering: +3,+2,-1 - └── group-by + └── group-by (streaming) ├── save-table-name: consistency_10_group_by_3 ├── columns: ol_o_id:11(int!null) ol_d_id:12(int!null) ol_w_id:13(int!null) count_rows:23(int!null) ├── grouping columns: ol_o_id:11(int!null) ol_d_id:12(int!null) ol_w_id:13(int!null) @@ -1532,7 +1532,7 @@ except-all ├── stats: [rows=295745, distinct(1)=2999, null(1)=0, distinct(2)=10, null(2)=0, distinct(3)=10, null(3)=0, distinct(13)=295745, null(13)=0] ├── key: (1-3) ├── fd: (1-3)-->(13) - ├── group-by + ├── group-by (streaming) │ ├── save-table-name: consistency_11_group_by_2 │ ├── columns: ol_o_id:1(int!null) ol_d_id:2(int!null) ol_w_id:3(int!null) count_rows:13(int!null) │ ├── grouping columns: ol_o_id:1(int!null) ol_d_id:2(int!null) ol_w_id:3(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q01 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q01 index afacc641706d..0c7a99e98e79 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q01 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q01 @@ -49,7 +49,7 @@ sort ├── key: (9,10) ├── fd: (9,10)-->(19,20,22,24-28) ├── ordering: +9,+10 - └── group-by + └── group-by (hash) ├── save-table-name: q1_group_by_2 ├── columns: l_returnflag:9(char!null) l_linestatus:10(char!null) sum:19(float!null) sum:20(float!null) sum:22(float!null) sum:24(float!null) avg:25(float!null) avg:26(float!null) avg:27(float!null) count_rows:28(int!null) ├── grouping columns: l_returnflag:9(char!null) l_linestatus:10(char!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 index 1ba04fecf928..07bde8c2f47b 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q02 @@ -92,7 +92,7 @@ project ├── stats: [rows=1, distinct(1)=1, null(1)=0, distinct(3)=1, null(3)=0, distinct(13)=1, null(13)=0, distinct(14)=1, null(14)=0, distinct(16)=1, null(16)=0, distinct(17)=1, null(17)=0, distinct(18)=1, null(18)=0, distinct(21)=0.999912293, null(21)=0, distinct(22)=0.999982117, null(22)=0, distinct(24)=1, null(24)=0, distinct(29)=1, null(29)=0, distinct(66)=1, null(66)=0] ├── key: (21,22) ├── fd: (1)-->(3), (21,22)-->(1,3,13,14,16-18,24,29,66), (1)==(21), (21)==(1), (22)-->(13,14,16-18,29), (24)==(66), (66)==(24) - ├── group-by + ├── group-by (hash) │ ├── save-table-name: q2_group_by_4 │ ├── columns: p_partkey:1(int!null) p_mfgr:3(char!null) s_name:13(char!null) s_address:14(varchar!null) s_phone:16(char!null) s_acctbal:17(float!null) s_comment:18(varchar!null) ps_partkey:21(int!null) ps_suppkey:22(int!null) ps_supplycost:24(float!null) n_name:29(char!null) min:66(float!null) │ ├── grouping columns: ps_partkey:21(int!null) ps_suppkey:22(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 index 8be9610227f1..4b7d97e425ce 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q03 @@ -52,7 +52,7 @@ top-k ├── key: (22) ├── fd: (22)-->(15,18,41) ├── ordering: -41,+15 - └── group-by + └── group-by (hash) ├── save-table-name: q3_group_by_2 ├── columns: o_orderdate:15(date!null) o_shippriority:18(int!null) l_orderkey:22(int!null) sum:41(float!null) ├── grouping columns: l_orderkey:22(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 index 7d2e81b56750..8d347fc0a299 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q04 @@ -46,7 +46,7 @@ sort ├── key: (6) ├── fd: (6)-->(30) ├── ordering: +6 - └── group-by + └── group-by (hash) ├── save-table-name: q4_group_by_2 ├── columns: o_orderpriority:6(char!null) count_rows:30(int!null) ├── grouping columns: o_orderpriority:6(char!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 index ffd277db1cf4..8d1dd05b2165 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q05 @@ -53,7 +53,7 @@ sort ├── key: (50) ├── fd: (50)-->(61) ├── ordering: -61 - └── group-by + └── group-by (hash) ├── save-table-name: q5_group_by_2 ├── columns: n_name:50(char!null) sum:61(float!null) ├── grouping columns: n_name:50(char!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q07 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q07 index 92b053ff16a0..6e6d26458c4c 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q07 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q07 @@ -65,7 +65,7 @@ sort ├── key: (50,56,61) ├── fd: (50,56,61)-->(63) ├── ordering: +50,+56,+61 - └── group-by + └── group-by (hash) ├── save-table-name: q7_group_by_2 ├── columns: n1.n_name:50(char!null) n2.n_name:56(char!null) l_year:61(float) sum:63(float!null) ├── grouping columns: n1.n_name:50(char!null) n2.n_name:56(char!null) l_year:61(float) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 index 412193d6d4b8..4b79efb219f7 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q08 @@ -71,7 +71,7 @@ sort ├── stats: [rows=728.862489, distinct(77)=728.862489, null(77)=0, distinct(82)=728.862489, null(82)=0] ├── key: (77) ├── fd: (77)-->(82) - ├── group-by + ├── group-by (hash) │ ├── save-table-name: q8_group_by_3 │ ├── columns: o_year:77(float) sum:80(float!null) sum:81(float!null) │ ├── grouping columns: o_year:77(float) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 index 6e43c9d33da8..cad4f39cd008 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q09 @@ -62,7 +62,7 @@ sort ├── key: (58,63) ├── fd: (58,63)-->(65) ├── ordering: +58,-63 - └── group-by + └── group-by (hash) ├── save-table-name: q9_group_by_2 ├── columns: n_name:58(char!null) o_year:63(float) sum:65(float!null) ├── grouping columns: n_name:58(char!null) o_year:63(float) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 index 7cc27e07b047..7e81eb6f8213 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q10 @@ -64,7 +64,7 @@ top-k ├── key: (1) ├── fd: (1)-->(2,3,5,6,8,41,47) ├── ordering: -47 - └── group-by + └── group-by (hash) ├── save-table-name: q10_group_by_2 ├── columns: c_custkey:1(int!null) c_name:2(varchar!null) c_address:3(varchar!null) c_phone:5(char!null) c_acctbal:6(float!null) c_comment:8(varchar!null) n_name:41(char!null) sum:47(float!null) ├── grouping columns: c_custkey:1(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 index afa5e0a03234..046a2dd94144 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q11 @@ -59,7 +59,7 @@ sort ├── stats: [rows=9927.82897, distinct(1)=9927.82897, null(1)=0, distinct(24)=9927.82897, null(24)=0] ├── key: (1) ├── fd: (1)-->(24) - ├── group-by + ├── group-by (hash) │ ├── save-table-name: q11_group_by_3 │ ├── columns: ps_partkey:1(int!null) sum:24(float!null) │ ├── grouping columns: ps_partkey:1(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q12 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q12 index f889f5759f42..2241a566f269 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q12 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q12 @@ -56,7 +56,7 @@ sort ├── key: (26) ├── fd: (26)-->(31,33) ├── ordering: +26 - └── group-by + └── group-by (hash) ├── save-table-name: q12_group_by_2 ├── columns: l_shipmode:26(char!null) sum:31(decimal!null) sum:33(decimal!null) ├── grouping columns: l_shipmode:26(char!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 index 84cfba4814e2..e2d9f6fef5b8 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q13 @@ -43,14 +43,14 @@ sort ├── key: (22) ├── fd: (22)-->(23) ├── ordering: -23,-22 - └── group-by + └── group-by (hash) ├── save-table-name: q13_group_by_2 ├── columns: count:22(int!null) count_rows:23(int!null) ├── grouping columns: count:22(int!null) ├── stats: [rows=148813, distinct(22)=148813, null(22)=0, distinct(23)=148813, null(23)=0] ├── key: (22) ├── fd: (22)-->(23) - ├── group-by + ├── group-by (hash) │ ├── save-table-name: q13_group_by_3 │ ├── columns: c_custkey:1(int!null) count:22(int!null) │ ├── grouping columns: c_custkey:1(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 index 601a3d1ccd45..d608ffd31de9 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q15 @@ -99,7 +99,7 @@ project │ ├── stats: [rows=3306.66667, distinct(12)=3306.66667, null(12)=0, distinct(29)=3306.66667, null(29)=0] │ ├── key: (12) │ ├── fd: (12)-->(29) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── save-table-name: q15_group_by_6 │ │ ├── columns: l_suppkey:12(int!null) sum:29(float!null) │ │ ├── grouping columns: l_suppkey:12(int!null) @@ -148,7 +148,7 @@ project │ ├── stats: [rows=1, distinct(50)=1, null(50)=0] │ ├── key: () │ ├── fd: ()-->(50) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── save-table-name: q15_group_by_11 │ │ ├── columns: l_suppkey:32(int!null) sum:49(float!null) │ │ ├── grouping columns: l_suppkey:32(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q16 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q16 index 478ce940e99f..ea7ecc93658f 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q16 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q16 @@ -58,7 +58,7 @@ sort ├── key: (11-13) ├── fd: (11-13)-->(28) ├── ordering: -28,+11,+12,+13 - └── group-by + └── group-by (hash) ├── save-table-name: q16_group_by_2 ├── columns: p_brand:11(char!null) p_type:12(varchar!null) p_size:13(int!null) count:28(int!null) ├── grouping columns: p_brand:11(char!null) p_type:12(varchar!null) p_size:13(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 index 0345f978d981..4cbf0702245f 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q17 @@ -79,7 +79,7 @@ project │ │ │ │ ├── stats: [rows=284.037462, distinct(19)=284.037462, null(19)=0, distinct(49)=284.037462, null(49)=0] │ │ │ │ ├── key: (19) │ │ │ │ ├── fd: (19)-->(49) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── save-table-name: q17_group_by_6 │ │ │ │ │ ├── columns: p_partkey:19(int!null) avg:48(float!null) │ │ │ │ │ ├── grouping columns: p_partkey:19(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 index 964876f77e57..c691469ab6c9 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q18 @@ -61,7 +61,7 @@ top-k ├── key: (11) ├── fd: (1)-->(2), (11)-->(1,2,14,15,59) ├── ordering: -14,+15 - └── group-by + └── group-by (hash) ├── save-table-name: q18_group_by_2 ├── columns: c_custkey:1(int!null) c_name:2(varchar!null) o_orderkey:11(int!null) o_totalprice:14(float!null) o_orderdate:15(date!null) sum:59(float!null) ├── grouping columns: o_orderkey:11(int!null) @@ -121,7 +121,7 @@ top-k │ │ │ │ ├── key: (40) │ │ │ │ ├── fd: (40)-->(58) │ │ │ │ ├── ordering: +40 - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── save-table-name: q18_group_by_9 │ │ │ │ │ ├── columns: l_orderkey:40(int!null) sum:58(float!null) │ │ │ │ │ ├── grouping columns: l_orderkey:40(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 index 242766cdfbf9..461b6399e359 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q20 @@ -117,7 +117,7 @@ sort │ │ │ ├── stats: [rows=127.585211, distinct(16)=127.585211, null(16)=0, distinct(17)=127.585211, null(17)=0, distinct(18)=127.585211, null(18)=0, distinct(52)=127.585211, null(52)=0] │ │ │ ├── key: (16,17) │ │ │ ├── fd: (16,17)-->(18,52) - │ │ │ ├── group-by + │ │ │ ├── group-by (hash) │ │ │ │ ├── save-table-name: q20_group_by_9 │ │ │ │ ├── columns: ps_partkey:16(int!null) ps_suppkey:17(int!null) ps_availqty:18(int!null) sum:52(float!null) │ │ │ │ ├── grouping columns: ps_partkey:16(int!null) ps_suppkey:17(int!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q21 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q21 index ffce10dec49b..9f47fa712b0f 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q21 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q21 @@ -67,7 +67,7 @@ top-k ├── key: (2) ├── fd: (2)-->(81) ├── ordering: -81,+2 - └── group-by + └── group-by (hash) ├── save-table-name: q21_group_by_2 ├── columns: s_name:2(char!null) count_rows:81(int!null) ├── grouping columns: s_name:2(char!null) diff --git a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 index 67686ec81215..29c8142b15ab 100644 --- a/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 +++ b/pkg/sql/opt/memo/testdata/stats_quality/tpch/q22 @@ -62,7 +62,7 @@ sort ├── key: (33) ├── fd: (33)-->(34,35) ├── ordering: +33 - └── group-by + └── group-by (hash) ├── save-table-name: q22_group_by_2 ├── columns: cntrycode:33(string) count_rows:34(int!null) sum:35(float!null) ├── grouping columns: cntrycode:33(string) diff --git a/pkg/sql/opt/memo/testdata/typing b/pkg/sql/opt/memo/testdata/typing index eaf0062d7783..4bf01bb71912 100644 --- a/pkg/sql/opt/memo/testdata/typing +++ b/pkg/sql/opt/memo/testdata/typing @@ -385,7 +385,7 @@ project ├── columns: x:1(int!null) x:5(string!null) y:2(int!null) ├── select │ ├── columns: a.x:1(int!null) y:2(int!null) max:10(string!null) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: a.x:1(int!null) y:2(int!null) max:10(string!null) │ │ ├── grouping columns: a.x:1(int!null) │ │ ├── inner-join (hash) @@ -416,7 +416,7 @@ SELECT EXISTS(SELECT * FROM a WHERE expr<0) FROM (SELECT x+1 AS expr FROM a) ---- project ├── columns: exists:10(bool!null) - ├── group-by + ├── group-by (hash) │ ├── columns: true_agg:12(bool) rownum:14(int!null) │ ├── grouping columns: rownum:14(int!null) │ ├── left-join (cross) diff --git a/pkg/sql/opt/norm/testdata/rules/combo b/pkg/sql/opt/norm/testdata/rules/combo index a21ed51e426c..8d6938a34c55 100644 --- a/pkg/sql/opt/norm/testdata/rules/combo +++ b/pkg/sql/opt/norm/testdata/rules/combo @@ -1907,7 +1907,7 @@ TryDecorrelateScalarGroupBy + │ │ │ │ ├── columns: x:1!null bool_or:14 + │ │ │ │ ├── key: (1) + │ │ │ │ ├── fd: (1)-->(14) - + │ │ │ │ └── group-by + + │ │ │ │ └── group-by (hash) + │ │ │ │ ├── columns: x:1!null bool_or:14 + │ │ │ │ ├── grouping columns: x:1!null + │ │ │ │ ├── key: (1) @@ -1972,7 +1972,7 @@ TryDecorrelateProjectSelect │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2058,7 +2058,7 @@ DecorrelateJoin │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2121,7 +2121,7 @@ PushFilterIntoJoinRight │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2196,7 +2196,7 @@ PushSelectIntoProject │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2271,7 +2271,7 @@ EliminateSelect │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2353,7 +2353,7 @@ PruneJoinRightCols │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2423,7 +2423,7 @@ EliminateGroupByProject │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - │ │ │ │ └── group-by + │ │ │ │ └── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) @@ -2511,12 +2511,12 @@ EliminateProject │ │ │ ├── key: (1) │ │ │ ├── fd: (1)-->(14) - │ │ │ ├── project - + │ │ │ ├── group-by + + │ │ │ ├── group-by (hash) │ │ │ │ ├── columns: x:1!null bool_or:14 + │ │ │ │ ├── grouping columns: x:1!null │ │ │ │ ├── key: (1) │ │ │ │ ├── fd: (1)-->(14) - - │ │ │ │ └── group-by + - │ │ │ │ └── group-by (hash) - │ │ │ │ ├── columns: x:1!null bool_or:14 - │ │ │ │ ├── grouping columns: x:1!null - │ │ │ │ ├── key: (1) @@ -2600,12 +2600,12 @@ EliminateSelect │ │ ├── key: (1) │ │ ├── fd: (1)-->(15) - │ │ ├── select - + │ │ ├── group-by + + │ │ ├── group-by (hash) │ │ │ ├── columns: x:1!null bool_or:14 + │ │ │ ├── grouping columns: x:1!null │ │ │ ├── key: (1) │ │ │ ├── fd: (1)-->(14) - - │ │ │ ├── group-by + - │ │ │ ├── group-by (hash) - │ │ │ │ ├── columns: x:1!null bool_or:14 - │ │ │ │ ├── grouping columns: x:1!null + │ │ │ ├── left-join (hash) @@ -2684,12 +2684,12 @@ EliminateSelect │ ├── fd: (1)-->(15) - │ ├── project - │ │ ├── columns: case:15 x:1!null - + │ ├── group-by + + │ ├── group-by (hash) + │ │ ├── columns: x:1!null bool_or:14 + │ │ ├── grouping columns: x:1!null │ │ ├── key: (1) - │ │ ├── fd: (1)-->(15) - - │ │ ├── group-by + - │ │ ├── group-by (hash) - │ │ │ ├── columns: x:1!null bool_or:14 - │ │ │ ├── grouping columns: x:1!null + │ │ ├── fd: (1)-->(14) @@ -2767,7 +2767,7 @@ PruneProjectCols - │ ├── key: (1) - │ ├── fd: (1)-->(15) + │ ├── columns: case:15 - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: x:1!null bool_or:14 │ │ ├── grouping columns: x:1!null │ │ ├── key: (1) @@ -2813,10 +2813,10 @@ InlineProjectInProject ├── columns: r:12 - ├── project - │ ├── columns: case:15 - - │ ├── group-by + - │ ├── group-by (hash) - │ │ ├── columns: x:1!null bool_or:14 - │ │ ├── grouping columns: x:1!null - + ├── group-by + + ├── group-by (hash) + │ ├── columns: x:1!null bool_or:14 + │ ├── grouping columns: x:1!null + │ ├── key: (1) @@ -2905,7 +2905,7 @@ CommuteLeftJoin (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -2951,7 +2951,7 @@ GenerateMergeJoins ================================================================================ project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3002,7 +3002,7 @@ HoistProjectFromLeftJoin (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3064,7 +3064,7 @@ CommuteLeftJoin (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3109,7 +3109,7 @@ GenerateMergeJoins (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3160,7 +3160,7 @@ GenerateLookupJoinsWithFilter (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3210,7 +3210,7 @@ GenerateMergeJoins (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3260,7 +3260,7 @@ GenerateMergeJoins (higher cost) -------------------------------------------------------------------------------- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── key: (1) @@ -3321,7 +3321,8 @@ GenerateStreamingGroupBy ================================================================================ project ├── columns: r:12 - ├── group-by + - ├── group-by (hash) + + ├── group-by (streaming) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null + │ ├── internal-ordering: +1 @@ -3369,7 +3370,7 @@ Final best expression ================================================================================ project ├── columns: r:12 - ├── group-by + ├── group-by (streaming) │ ├── columns: x:1!null bool_or:14 │ ├── grouping columns: x:1!null │ ├── internal-ordering: +1 diff --git a/pkg/sql/opt/norm/testdata/rules/decorrelate b/pkg/sql/opt/norm/testdata/rules/decorrelate index 1ca6a8247d23..233c58d505d0 100644 --- a/pkg/sql/opt/norm/testdata/rules/decorrelate +++ b/pkg/sql/opt/norm/testdata/rules/decorrelate @@ -896,7 +896,7 @@ SELECT 5=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a ---- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null bool_or:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -979,7 +979,7 @@ SELECT 5=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a ---- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null bool_or:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -1022,7 +1022,7 @@ SELECT i=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a ---- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 bool_or:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -1064,7 +1064,7 @@ SELECT i*i/5=ANY(SELECT y FROM xy WHERE x=k) AS r FROM a project ├── columns: r:12 ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null scalar:13 bool_or:15 │ ├── grouping columns: k:1!null │ ├── immutable @@ -1308,7 +1308,7 @@ SELECT (SELECT sum(y + v) FROM xy, uv WHERE x=u AND x=k) FROM a project ├── columns: sum:18 ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null sum:17 │ ├── grouping columns: k:1!null │ ├── immutable @@ -1434,7 +1434,7 @@ project ├── columns: k:1!null count_rows:16!null ├── key: (1) ├── fd: (1)-->(16) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null count_rows:16!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -1633,7 +1633,7 @@ WHERE EXISTS SELECT * FROM xy INNER JOIN (SELECT count(*) AS cnt, sum(v) FROM uv WHERE i=5 GROUP BY v) ON x=cnt ) ---- -group-by +group-by (hash) ├── columns: k:1!null i:2!null f:3 s:4 j:5 ├── grouping columns: k:1!null ├── key: (1) @@ -1642,7 +1642,7 @@ group-by │ ├── columns: k:1!null i:2!null f:3 s:4 j:5 x:8!null v:13 count_rows:16!null │ ├── key: (1,8,13) │ ├── fd: ()-->(2), (1)-->(3-5), (1,8,13)-->(3-5,16), (8)==(16), (16)==(8) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null i:2!null f:3 s:4 j:5 x:8!null v:13 count_rows:16!null │ │ ├── grouping columns: k:1!null x:8!null v:13 │ │ ├── key: (1,8,13) @@ -1699,7 +1699,7 @@ WHERE EXISTS SELECT * FROM xy INNER JOIN (SELECT count(DISTINCT uv.v) AS cnt, sum(v) FROM uv WHERE i=5 GROUP BY v) ON x=cnt ) ---- -group-by +group-by (hash) ├── columns: k:1!null i:2!null f:3 s:4 j:5 ├── grouping columns: k:1!null ├── key: (1) @@ -1708,7 +1708,7 @@ group-by │ ├── columns: k:1!null i:2!null f:3 s:4 j:5 x:8!null v:13 count:16!null │ ├── key: (1,8,13) │ ├── fd: ()-->(2), (1)-->(3-5), (1,8,13)-->(3-5,16), (8)==(16), (16)==(8) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null i:2!null f:3 s:4 j:5 x:8!null v:13 count:16!null │ │ ├── grouping columns: k:1!null x:8!null v:13 │ │ ├── key: (1,8,13) @@ -1773,7 +1773,7 @@ project ├── columns: x:1!null y:2 u:5!null v:6!null max:16!null ├── key: (5) ├── fd: (1)-->(2), (5)-->(1,2,6), (1)==(6), (6)==(1), (5)==(16), (16)==(5) - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null y:2 u:5!null v:6!null max:16!null │ ├── grouping columns: u:5!null │ ├── key: (5) @@ -1837,7 +1837,7 @@ project ├── columns: x:1!null y:2 u:5!null v:6!null max:16 ├── key: (5) ├── fd: (1)-->(2), (5)-->(1,2,6,16), (1)==(6), (6)==(1) - ├── group-by + ├── group-by (hash) │ ├── columns: x:1!null y:2 u:5!null v:6!null max:16 │ ├── grouping columns: u:5!null │ ├── key: (5) @@ -1896,7 +1896,7 @@ project │ ├── columns: x:1!null y:2!null max:13!null │ ├── key: (1) │ ├── fd: ()-->(2,13) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: x:1!null y:2!null max:13 │ │ ├── grouping columns: x:1!null │ │ ├── key: (1) @@ -1944,7 +1944,7 @@ WHERE EXISTS ) ON x=u ) ---- -group-by +group-by (hash) ├── columns: k:1!null i:2!null f:3 s:4 j:5 ├── grouping columns: k:1!null ├── key: (1) @@ -2081,7 +2081,7 @@ with &1 (w0) │ │ │ ├── columns: tab_orig.rowid:16!null t1._decimal:23 t1.rowid:25!null t2._bool:31 t2.rowid:34!null t3._int2:37!null t3._timestamptz:39!null t3.rowid:43!null t4._int8:54!null t4._timestamptz:55!null │ │ │ ├── outer: (6) │ │ │ ├── fd: (25)-->(23), (34)-->(31), (43)-->(37,39), (16,25,34,43)-->(23,31,37,39), (39)==(55), (55)==(39), (37)==(54), (54)==(37) - │ │ │ ├── group-by + │ │ │ ├── group-by (hash) │ │ │ │ ├── columns: tab_orig.rowid:16!null t1._decimal:23 t1.rowid:25!null t2._bool:31 t2.rowid:34!null t3._int2:37 t3._timestamptz:39 t3.rowid:43!null │ │ │ │ ├── grouping columns: tab_orig.rowid:16!null t1.rowid:25!null t2.rowid:34!null t3.rowid:43!null │ │ │ │ ├── outer: (6) @@ -2153,7 +2153,7 @@ WHERE EXISTS SELECT * FROM xy INNER JOIN (SELECT sum(v), count(*) AS cnt FROM uv WHERE i=5) ON x=cnt ) ---- -group-by +group-by (hash) ├── columns: k:1!null i:2 f:3 s:4 j:5 ├── grouping columns: k:1!null ├── key: (1) @@ -2162,7 +2162,7 @@ group-by │ ├── columns: k:1!null i:2 f:3 s:4 j:5 x:8!null count_rows:17!null │ ├── key: (1,8) │ ├── fd: (1)-->(2-5), (1,8)-->(2-5,17), (8)==(17), (17)==(8) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 x:8!null count_rows:17!null │ │ ├── grouping columns: k:1!null x:8!null │ │ ├── key: (1,8) @@ -2224,7 +2224,7 @@ project │ ├── columns: k:1!null i:2!null max:13!null │ ├── key: (1) │ ├── fd: ()-->(13), (1)-->(2) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null i:2!null max:13!null │ │ ├── grouping columns: k:1!null │ │ ├── key: (1) @@ -2269,7 +2269,7 @@ project ├── columns: k:1!null array_agg:13 ├── key: (1) ├── fd: (1)-->(13) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null y:9 array_agg:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -2300,7 +2300,7 @@ project ├── columns: k:1!null "?column?":15 ├── key: (1) ├── fd: (1)-->(15) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null y:9 max:13 array_agg:16 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -2334,7 +2334,7 @@ project ├── columns: k:1!null array:13 ├── key: (1) ├── fd: (1)-->(13) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null canary:14 array_agg:15 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -2369,7 +2369,7 @@ SELECT i, ARRAY(SELECT y FROM xy WHERE xy.y = a.k OR xy.y IS NULL ORDER BY y) FR ---- project ├── columns: i:2 array:13 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 canary:14 array_agg:15 │ ├── grouping columns: k:1!null │ ├── internal-ordering: +9 @@ -2428,7 +2428,7 @@ project ├── columns: k:1!null i:2 f:3 s:4 j:5 max:17 ├── key: (1) ├── fd: ()-->(17), (1)-->(2-5) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 f:3 s:4 j:5 max:17 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -2441,7 +2441,7 @@ project │ │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 │ │ │ ├── key: (1) │ │ │ └── fd: (1)-->(2-5) - │ │ ├── group-by + │ │ ├── group-by (hash) │ │ │ ├── columns: x:8!null y:9 max:16 │ │ │ ├── grouping columns: x:8!null │ │ │ ├── outer: (1) @@ -2506,7 +2506,7 @@ project │ ├── columns: array_agg:9 c:1!null d:2!null │ ├── key: (1) │ ├── fd: (1)-->(2,9) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: c:1!null d:2!null x:5 array_agg:10 │ │ ├── grouping columns: c:1!null │ │ ├── key: (1) @@ -2556,7 +2556,7 @@ project │ ├── immutable │ ├── key: (1) │ ├── fd: (1)-->(2-5,13) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 canary:14 concat_agg:15 │ │ ├── grouping columns: k:1!null │ │ ├── immutable @@ -2612,7 +2612,7 @@ project ├── columns: k:1!null string_agg:17 ├── key: (1) ├── fd: (1)-->(17) - ├── group-by + ├── group-by (hash) │ ├── columns: a2.k:1!null string_agg:16 │ ├── grouping columns: a2.k:1!null │ ├── key: (1) @@ -2658,7 +2658,7 @@ WHERE EXISTS SELECT * FROM a WHERE i=(SELECT max(i) FROM a WHERE f=y::float) ) ---- -group-by +group-by (hash) ├── columns: x:1!null y:2 ├── grouping columns: x:1!null ├── immutable @@ -2669,7 +2669,7 @@ group-by │ ├── immutable │ ├── key: (1,5) │ ├── fd: (1)-->(2), (5)-->(6), (1,5)-->(2,6,19), (6)==(19), (19)==(6) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: x:1!null y:2 k:5!null i:6 max:19!null │ │ ├── grouping columns: x:1!null k:5!null │ │ ├── immutable @@ -2729,7 +2729,7 @@ WHERE EXISTS SELECT * FROM (SELECT DISTINCT ON (f) i FROM a WHERE y > f) WHERE x=i ) ---- -group-by +group-by (hash) ├── columns: x:1!null y:2!null ├── grouping columns: x:1!null ├── key: (1) @@ -2815,7 +2815,7 @@ project norm expect=TryDecorrelateSemiJoin SELECT * FROM xy WHERE EXISTS(SELECT generate_series(x, 10), generate_series(y, 10)) ---- -group-by +group-by (hash) ├── columns: x:1!null y:2 ├── grouping columns: x:1!null ├── immutable @@ -3217,7 +3217,7 @@ WHERE EXISTS ON x=u ) ---- -group-by +group-by (hash) ├── columns: k:1!null i:2!null f:3 s:4 j:5 ├── grouping columns: k:1!null ├── key: (1) @@ -4156,7 +4156,7 @@ project │ │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 count_rows:12!null │ │ │ ├── key: (1) │ │ │ ├── fd: (1)-->(2-5,12) - │ │ │ ├── group-by + │ │ │ ├── group-by (hash) │ │ │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 count_rows:12!null │ │ │ │ ├── grouping columns: k:1!null │ │ │ │ ├── key: (1) @@ -4219,7 +4219,7 @@ project ├── immutable ├── key: (1) ├── fd: (1)-->(2-5,12) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 f:3 s:4 j:5 count_rows:12!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4264,7 +4264,7 @@ project ├── columns: k:1!null i:2 f:3 s:4 j:5 true_agg:13 ├── key: (1) ├── fd: (1)-->(2-5,13) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 f:3 s:4 j:5 true_agg:13 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4315,7 +4315,7 @@ project │ ├── columns: case:14 k:1!null i:2 f:3 s:4 j:5 │ ├── key: (1) │ ├── fd: (1)-->(2-5,14) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null i:2 f:3 s:4 j:5 bool_or:13 │ │ ├── grouping columns: k:1!null │ │ ├── key: (1) @@ -4385,7 +4385,7 @@ SELECT i*i/100 < ALL(SELECT y FROM xy WHERE x=k) AS r, s FROM a project ├── columns: r:12 s:4 ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null s:4 scalar:13 bool_or:15 │ ├── grouping columns: k:1!null │ ├── immutable @@ -4444,7 +4444,7 @@ project ├── columns: k:1!null i:2 f:3 s:4 j:5 true_agg:17 ├── key: (1) ├── fd: (1)-->(2-5,17) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 f:3 s:4 j:5 true_agg:17 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4534,7 +4534,7 @@ FROM a project ├── columns: a:29!null x:30 y:31 b:32 exists:33 count:34!null ├── fd: ()-->(29) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null xy.x:8 count_rows:28!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4639,7 +4639,7 @@ SELECT EXISTS(SELECT * FROM xy WHERE y=i) FROM a ---- project ├── columns: exists:12!null - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null true_agg:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4672,7 +4672,7 @@ SELECT 5 < ANY(SELECT y FROM xy WHERE y=i) AS r FROM a ---- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null bool_or:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4808,7 +4808,7 @@ project │ ├── columns: exists:18!null x:8!null │ ├── key: (8) │ ├── fd: (8)-->(18) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: x:8!null true_agg:17 │ │ ├── grouping columns: x:8!null │ │ ├── key: (8) @@ -4853,7 +4853,7 @@ project ├── project │ ├── columns: case:18 j:5 x:8!null y:9 │ ├── fd: (8)-->(9) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null j:5 x:8!null y:9 bool_or:17 │ │ ├── grouping columns: k:1!null x:8!null │ │ ├── key: (1,8) @@ -4962,7 +4962,7 @@ SELECT (VALUES (EXISTS(SELECT * FROM xy WHERE x=k))) FROM a ---- project ├── columns: column1:16!null - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null true_agg:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -4998,7 +4998,7 @@ SELECT (VALUES (5 IN (SELECT y FROM xy WHERE x=k))) FROM a ---- project ├── columns: column1:16 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null bool_or:14 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -5070,7 +5070,7 @@ project norm expect=(HoistProjectSetSubquery,TryDecorrelateSemiJoin,TryDecorrelateProjectSet) SELECT * FROM xy WHERE EXISTS(SELECT * FROM generate_series(1, (SELECT v FROM uv WHERE u=x))) ---- -group-by +group-by (hash) ├── columns: x:1!null y:2 ├── grouping columns: x:1!null ├── immutable @@ -5269,7 +5269,7 @@ limit │ ├── fd: (1)-->(2-9) │ ├── ordering: +8 │ ├── limit hint: 10.00 - │ └── group-by + │ └── group-by (hash) │ ├── columns: id:1!null body:2 description:3 title:4 slug:5 tag_list:6 user_id:7 created_at:8 updated_at:9 │ ├── grouping columns: id:1!null │ ├── immutable @@ -5326,7 +5326,7 @@ project ├── immutable ├── key: (1,12) ├── fd: (1)-->(2-9), (1,12)-->(2-9,13,21) - ├── group-by + ├── group-by (hash) │ ├── columns: id:1!null body:2 description:3 title:4 slug:5 tag_list:6 user_id:7 created_at:8 updated_at:9 x:12!null y:13 true_agg:21 │ ├── grouping columns: id:1!null x:12!null │ ├── immutable diff --git a/pkg/sql/opt/norm/testdata/rules/groupby b/pkg/sql/opt/norm/testdata/rules/groupby index 378af9f6cd22..6bfc9084ca77 100644 --- a/pkg/sql/opt/norm/testdata/rules/groupby +++ b/pkg/sql/opt/norm/testdata/rules/groupby @@ -98,7 +98,7 @@ distinct-on norm expect-not=ConvertGroupByToDistinct SELECT s, f, sum(f) FROM a GROUP BY s, f ---- -group-by +group-by (hash) ├── columns: s:4!null f:3 sum:8 ├── grouping columns: f:3 s:4!null ├── key: (3,4) @@ -138,7 +138,7 @@ scan xy norm expect=EliminateJoinUnderGroupByLeft SELECT k, max(r1) FROM fks INNER JOIN (SELECT * FROM (VALUES (1), (2)) f(t)) ON True GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null max:8!null ├── grouping columns: k:1!null ├── key: (1) @@ -174,7 +174,7 @@ scalar-group-by norm expect=EliminateJoinUnderGroupByLeft SELECT x, max(y) FROM xy LEFT JOIN fks ON True GROUP BY x ---- -group-by +group-by (hash) ├── columns: x:1!null max:11 ├── grouping columns: x:1!null ├── key: (1) @@ -191,7 +191,7 @@ group-by norm expect=EliminateJoinUnderGroupByLeft disable=EliminateJoinUnderProjectLeft SELECT k, sum(r1) FROM fks LEFT JOIN xy ON x=r1 GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null sum:11!null ├── grouping columns: k:1!null ├── key: (1) @@ -210,7 +210,7 @@ group-by norm expect=EliminateJoinUnderGroupByLeft disable=EliminateJoinUnderProjectLeft SELECT x, sum(y) FROM xy LEFT JOIN fks ON x=k GROUP BY x ---- -group-by +group-by (hash) ├── columns: x:1!null sum:11 ├── grouping columns: x:1!null ├── key: (1) @@ -230,7 +230,7 @@ group-by norm expect=EliminateJoinUnderGroupByLeft disable=EliminateJoinUnderProjectLeft SELECT k, sum(r1) FROM fks LEFT JOIN xy ON x=r2 GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null sum:11!null ├── grouping columns: k:1!null ├── key: (1) @@ -248,7 +248,7 @@ group-by norm expect=EliminateJoinUnderGroupByLeft disable=EliminateJoinUnderProjectLeft SELECT k, sum(r1) FROM fks INNER JOIN xy ON x=r1 GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null sum:11!null ├── grouping columns: k:1!null ├── key: (1) @@ -265,7 +265,7 @@ group-by norm expect=EliminateJoinUnderGroupByLeft SELECT max(y) FROM xy LEFT JOIN fks ON True GROUP BY x ORDER BY x ---- -group-by +group-by (streaming) ├── columns: max:11 [hidden: x:1!null] ├── grouping columns: x:1!null ├── key: (1) @@ -345,7 +345,7 @@ distinct-on norm expect-not=EliminateJoinUnderGroupByLeft SELECT k, sum(r1) FROM fks INNER JOIN xy ON True GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null sum:11!null ├── grouping columns: k:1!null ├── key: (1) @@ -369,7 +369,7 @@ group-by norm expect-not=EliminateJoinUnderGroupByLeft SELECT k, sum(r1) FROM fks INNER JOIN xy ON x=r2 GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null sum:11!null ├── grouping columns: k:1!null ├── key: (1) @@ -396,7 +396,7 @@ group-by norm expect-not=EliminateJoinUnderGroupByLeft SELECT x, max(y) FROM xy LEFT JOIN fks ON True GROUP BY x, k ORDER BY x, k ---- -group-by +group-by (streaming) ├── columns: x:1!null max:11 [hidden: k:5] ├── grouping columns: x:1!null k:5 ├── key: (1,5) @@ -432,7 +432,7 @@ group-by norm expect=EliminateJoinUnderGroupByRight disable=EliminateJoinUnderProjectRight SELECT k, sum(r1) FROM xy INNER JOIN fks ON x = r1 GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:5!null sum:11!null ├── grouping columns: k:5!null ├── key: (5) @@ -604,7 +604,7 @@ SELECT min(s) FROM (SELECT i, s FROM (SELECT * FROM a UNION SELECT * FROM a)) GR ---- project ├── columns: min:20!null - └── group-by + └── group-by (hash) ├── columns: i:16!null min:20!null ├── grouping columns: i:16!null ├── key: (16) @@ -790,7 +790,7 @@ SELECT min(s) FROM (SELECT i+1 AS i2, s FROM a) GROUP BY i2 project ├── columns: min:9!null ├── immutable - └── group-by + └── group-by (hash) ├── columns: i2:8!null min:9!null ├── grouping columns: i2:8!null ├── immutable @@ -814,7 +814,7 @@ project norm expect=ReduceGroupingCols SELECT k, min(i), f, s FROM a GROUP BY s, f, k ---- -group-by +group-by (hash) ├── columns: k:1!null min:8!null f:3 s:4!null ├── grouping columns: k:1!null ├── key: (1) @@ -834,7 +834,7 @@ group-by norm expect=ReduceGroupingCols SELECT k, sum(DISTINCT i), f, s FROM a, xy GROUP BY s, f, k ---- -group-by +group-by (hash) ├── columns: k:1!null sum:12!null f:3 s:4!null ├── grouping columns: k:1!null ├── key: (1) @@ -863,7 +863,7 @@ SELECT min(f) FROM a GROUP BY i, s, k ---- project ├── columns: min:8 - └── group-by + └── group-by (hash) ├── columns: i:2!null s:4!null min:8 ├── grouping columns: i:2!null s:4!null ├── key: (2,4) @@ -880,7 +880,7 @@ project norm expect=ReduceGroupingCols SELECT sum(f), i FROM a GROUP BY k, i, f HAVING k=1 ---- -group-by +group-by (streaming) ├── columns: sum:8 i:2!null ├── cardinality: [0 - 1] ├── key: () @@ -1297,7 +1297,7 @@ SELECT sum(DISTINCT k) FROM a GROUP BY i ---- project ├── columns: sum:8!null - └── group-by + └── group-by (hash) ├── columns: i:2!null sum:8!null ├── grouping columns: i:2!null ├── key: (2) @@ -1317,7 +1317,7 @@ SELECT sum(DISTINCT a) FROM abc GROUP BY b ---- project ├── columns: sum:6!null - └── group-by + └── group-by (hash) ├── columns: b:2!null sum:6!null ├── grouping columns: b:2!null ├── key: (2) @@ -1338,7 +1338,7 @@ SELECT sum(DISTINCT a) FROM abc GROUP BY b, c ---- project ├── columns: sum:6!null - └── group-by + └── group-by (hash) ├── columns: b:2!null c:3!null sum:6!null ├── grouping columns: b:2!null c:3!null ├── key: (2,3) @@ -1356,7 +1356,7 @@ SELECT sum(DISTINCT i), avg(DISTINCT f) FROM a GROUP BY k ---- project ├── columns: sum:8!null avg:9 - └── group-by + └── group-by (hash) ├── columns: k:1!null sum:8!null avg:9 ├── grouping columns: k:1!null ├── key: (1) @@ -1378,7 +1378,7 @@ SELECT sum(DISTINCT u), stddev(DISTINCT w), avg(DISTINCT z) FROM uvwz GROUP BY v ---- project ├── columns: sum:8!null stddev:9 avg:10!null - └── group-by + └── group-by (hash) ├── columns: v:2!null sum:8!null stddev:9 avg:10!null ├── grouping columns: v:2!null ├── key: (2) @@ -1465,7 +1465,7 @@ SELECT sum(DISTINCT k) FILTER (WHERE f > 0) FROM a GROUP BY i ---- project ├── columns: sum:9 - └── group-by + └── group-by (hash) ├── columns: i:2!null sum:9 ├── grouping columns: i:2!null ├── key: (2) @@ -1492,7 +1492,7 @@ SELECT sum(DISTINCT a) FILTER (WHERE c > 0) FROM abc GROUP BY b ---- project ├── columns: sum:7 - └── group-by + └── group-by (hash) ├── columns: b:2!null sum:7 ├── grouping columns: b:2!null ├── key: (2) @@ -1517,7 +1517,7 @@ SELECT sum(DISTINCT a) FILTER (WHERE c > 0) FROM abc GROUP BY b, c ---- project ├── columns: sum:7 - └── group-by + └── group-by (hash) ├── columns: b:2!null c:3!null sum:7 ├── grouping columns: b:2!null c:3!null ├── key: (2,3) @@ -1543,7 +1543,7 @@ SELECT sum(DISTINCT i) FILTER (WHERE f > 0), avg(DISTINCT f) FILTER (WHERE i > 0 ---- project ├── columns: sum:9 avg:11 - └── group-by + └── group-by (hash) ├── columns: k:1!null sum:9 avg:11 ├── grouping columns: k:1!null ├── key: (1) @@ -1581,7 +1581,7 @@ GROUP BY v ---- project ├── columns: sum:9 stddev:11 avg:13 - └── group-by + └── group-by (hash) ├── columns: v:2!null sum:9 stddev:11 avg:13 ├── grouping columns: v:2!null ├── key: (2) @@ -2709,7 +2709,7 @@ scalar-group-by norm expect=PushAggDistinctIntoGroupBy SELECT b, count(DISTINCT y) FROM xyzbs GROUP BY b ---- -group-by +group-by (hash) ├── columns: b:4!null count:8!null ├── grouping columns: b:4!null ├── cardinality: [0 - 2] @@ -2729,7 +2729,7 @@ group-by norm expect=PushAggDistinctIntoGroupBy SELECT b, s, count(DISTINCT y) FROM xyzbs GROUP BY b, s ---- -group-by +group-by (hash) ├── columns: b:4!null s:5 count:8!null ├── grouping columns: b:4!null s:5 ├── key: (4,5) @@ -2748,7 +2748,7 @@ group-by norm expect=PushAggDistinctIntoGroupBy SELECT s, corr(DISTINCT y, z) FROM xyzbs GROUP BY s ---- -group-by +group-by (hash) ├── columns: s:5 corr:8 ├── grouping columns: s:5 ├── key: (5) @@ -2772,7 +2772,7 @@ SELECT array_agg(DISTINCT s) FROM (SELECT * FROM a ORDER BY i) GROUP BY f ---- project ├── columns: array_agg:8!null - └── group-by + └── group-by (hash) ├── columns: f:3 array_agg:8!null ├── grouping columns: f:3 ├── internal-ordering: +2 opt(3) @@ -3180,7 +3180,7 @@ SELECT count(z) FROM xyzbs GROUP BY s ---- project ├── columns: count:8!null - └── group-by + └── group-by (hash) ├── columns: s:5 count:8!null ├── grouping columns: s:5 ├── key: (5) @@ -3195,7 +3195,7 @@ SELECT count(1) FROM xyzbs GROUP BY s ---- project ├── columns: count:9!null - └── group-by + └── group-by (hash) ├── columns: s:5 count:9!null ├── grouping columns: s:5 ├── key: (5) @@ -3210,7 +3210,7 @@ SELECT count(1+z) FROM xyzbs GROUP BY s ---- project ├── columns: count:9!null - └── group-by + └── group-by (hash) ├── columns: s:5 count:9!null ├── grouping columns: s:5 ├── key: (5) @@ -3265,7 +3265,7 @@ SELECT count(DISTINCT y) FROM xyzbs GROUP BY z ---- project ├── columns: count:8!null - └── group-by + └── group-by (hash) ├── columns: z:3!null count:8!null ├── grouping columns: z:3!null ├── key: (3) @@ -3327,7 +3327,7 @@ SELECT regr_count(z, x) FROM xyzbs GROUP BY s ---- project ├── columns: regr_count:8!null - └── group-by + └── group-by (hash) ├── columns: s:5 regr_count:8!null ├── grouping columns: s:5 ├── key: (5) @@ -3342,7 +3342,7 @@ SELECT regr_count(1, 1) FROM xyzbs GROUP BY s ---- project ├── columns: regr_count:9!null - └── group-by + └── group-by (hash) ├── columns: s:5 regr_count:9!null ├── grouping columns: s:5 ├── key: (5) @@ -3357,7 +3357,7 @@ SELECT regr_count(1+z, x) FROM xyzbs GROUP BY s ---- project ├── columns: regr_count:9!null - └── group-by + └── group-by (hash) ├── columns: s:5 regr_count:9!null ├── grouping columns: s:5 ├── key: (5) @@ -3558,7 +3558,7 @@ SELECT sum(s) FROM (SELECT y, sum(x) AS s FROM xy GROUP BY x) GROUP BY y ---- project ├── columns: sum:6!null - └── group-by + └── group-by (hash) ├── columns: y:2 sum:6!null ├── grouping columns: y:2 ├── key: (2) @@ -3577,7 +3577,7 @@ SELECT sum(s) FROM (SELECT a, sum(c) AS s FROM abc GROUP BY a, b) GROUP BY a ---- project ├── columns: sum:7!null - └── group-by + └── group-by (hash) ├── columns: a:1!null sum:7!null ├── grouping columns: a:1!null ├── key: (1) @@ -3629,7 +3629,7 @@ scalar-group-by ├── cardinality: [1 - 1] ├── key: () ├── fd: ()-->(6) - ├── group-by + ├── group-by (hash) │ ├── columns: y:2 avg:5!null │ ├── grouping columns: y:2 │ ├── key: (2) @@ -3654,7 +3654,7 @@ scalar-group-by ├── cardinality: [1 - 1] ├── key: () ├── fd: ()-->(7-9) - ├── group-by + ├── group-by (hash) │ ├── columns: y:2 sum:5!null count:6!null │ ├── grouping columns: y:2 │ ├── key: (2) @@ -3683,12 +3683,12 @@ SELECT max(m) FROM (SELECT max(x) AS m, sum(x) AS s FROM xy GROUP BY y) GROUP BY ---- project ├── columns: max:7!null - └── group-by + └── group-by (hash) ├── columns: sum:6!null max:7!null ├── grouping columns: sum:6!null ├── key: (6) ├── fd: (6)-->(7) - ├── group-by + ├── group-by (hash) │ ├── columns: y:2 max:5!null sum:6!null │ ├── grouping columns: y:2 │ ├── key: (2) @@ -3716,7 +3716,7 @@ scalar-group-by ├── cardinality: [1 - 1] ├── key: () ├── fd: ()-->(10) - ├── group-by + ├── group-by (hash) │ ├── columns: u:1!null sum:8!null │ ├── grouping columns: u:1!null │ ├── internal-ordering: -3 opt(1) diff --git a/pkg/sql/opt/norm/testdata/rules/inline b/pkg/sql/opt/norm/testdata/rules/inline index 8fc4d29e2031..0faff9d232b6 100644 --- a/pkg/sql/opt/norm/testdata/rules/inline +++ b/pkg/sql/opt/norm/testdata/rules/inline @@ -1019,7 +1019,7 @@ SELECT EXISTS(SELECT * FROM xy WHERE expr<0) FROM (SELECT k+1 AS expr FROM a) project ├── columns: exists:13!null ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: true_agg:15 rownum:17!null │ ├── grouping columns: rownum:17!null │ ├── immutable diff --git a/pkg/sql/opt/norm/testdata/rules/join b/pkg/sql/opt/norm/testdata/rules/join index 66787616848b..a20d047cc755 100644 --- a/pkg/sql/opt/norm/testdata/rules/join +++ b/pkg/sql/opt/norm/testdata/rules/join @@ -1442,7 +1442,7 @@ project │ │ ├── columns: ol_o_id:12!null ol_d_id:13!null ol_w_id:14!null ol_number:15!null ol_dist_info:21 true_agg:48 │ │ ├── key: (12-15) │ │ ├── fd: (12-15)-->(21,48) - │ │ ├── group-by + │ │ ├── group-by (hash) │ │ │ ├── columns: ol_o_id:12!null ol_d_id:13!null ol_w_id:14!null ol_number:15!null ol_dist_info:21 true_agg:48 │ │ │ ├── grouping columns: ol_o_id:12!null ol_d_id:13!null ol_w_id:14!null ol_number:15!null │ │ │ ├── key: (12-15) @@ -2181,7 +2181,7 @@ project ├── immutable ├── key: (1) ├── fd: ()-->(9), (1)-->(2-5) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2 f:3!null s:4 j:5 sum:9 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -2633,7 +2633,7 @@ values norm expect=EliminateJoinNoColsRight SELECT * FROM xy WHERE EXISTS(SELECT generate_series(x, 10)) ---- -group-by +group-by (hash) ├── columns: x:1!null y:2 ├── grouping columns: x:1!null ├── immutable diff --git a/pkg/sql/opt/norm/testdata/rules/ordering b/pkg/sql/opt/norm/testdata/rules/ordering index 1fc288fb0ad7..2a602c4202d6 100644 --- a/pkg/sql/opt/norm/testdata/rules/ordering +++ b/pkg/sql/opt/norm/testdata/rules/ordering @@ -112,7 +112,7 @@ offset norm SELECT array_agg(b), a, c FROM abcde GROUP BY b, a, c ORDER BY a, b, c ---- -group-by +group-by (streaming) ├── columns: array_agg:8 a:1!null c:3 ├── grouping columns: a:1!null ├── key: (1) diff --git a/pkg/sql/opt/norm/testdata/rules/prune_cols b/pkg/sql/opt/norm/testdata/rules/prune_cols index 3a1961d388c5..f0666bd326ce 100644 --- a/pkg/sql/opt/norm/testdata/rules/prune_cols +++ b/pkg/sql/opt/norm/testdata/rules/prune_cols @@ -470,7 +470,7 @@ project │ ├── immutable │ ├── key: (1) │ ├── fd: ()-->(7), (1)-->(3) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null f:3 sum:7!null │ │ ├── grouping columns: k:1!null │ │ ├── key: (1) @@ -1357,7 +1357,7 @@ distinct-on norm expect=PruneAggCols SELECT s, sumi FROM (SELECT sum(i) sumi, s, min(s||'foo') FROM a GROUP BY s) a ---- -group-by +group-by (hash) ├── columns: s:4 sumi:7 ├── grouping columns: s:4 ├── key: (4) @@ -1480,7 +1480,7 @@ scalar-group-by norm expect=PruneGroupByCols SELECT s, sum(i) FROM a GROUP BY s, s||'foo' ---- -group-by +group-by (hash) ├── columns: s:4 sum:7 ├── grouping columns: s:4 ├── key: (4) @@ -1495,7 +1495,7 @@ group-by norm expect=PruneGroupByCols SELECT avg(s::int+i), s, i FROM a GROUP BY s, i, i+1 ---- -group-by +group-by (hash) ├── columns: avg:8 s:4 i:2 ├── grouping columns: i:2 s:4 ├── immutable @@ -1533,12 +1533,12 @@ project norm expect=PruneGroupByCols SELECT min(sm), i FROM (SELECT s, i, sum(k) sm, avg(k) av FROM a GROUP BY i, s) a GROUP BY i, i+1 ---- -group-by +group-by (hash) ├── columns: min:9!null i:2 ├── grouping columns: i:2 ├── key: (2) ├── fd: (2)-->(9) - ├── group-by + ├── group-by (hash) │ ├── columns: i:2 s:4 sum:7!null │ ├── grouping columns: i:2 s:4 │ ├── key: (2,4) @@ -1684,7 +1684,7 @@ project ├── immutable ├── key: (1) ├── fd: (1)-->(8) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null sum:8 │ ├── grouping columns: k:1!null │ ├── key: (1) diff --git a/pkg/sql/opt/norm/testdata/rules/reject_nulls b/pkg/sql/opt/norm/testdata/rules/reject_nulls index a36913094060..2345864d3bb3 100644 --- a/pkg/sql/opt/norm/testdata/rules/reject_nulls +++ b/pkg/sql/opt/norm/testdata/rules/reject_nulls @@ -221,7 +221,7 @@ project ├── columns: k:1!null max:11!null ├── key: (1) ├── fd: ()-->(11) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null max:11!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -260,7 +260,7 @@ project ├── immutable ├── key: (1) ├── fd: ()-->(11), (1)-->(12) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null sum:11!null max:12!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -333,7 +333,7 @@ project ├── columns: k:1!null min:11!null max:12!null ├── key: (1) ├── fd: ()-->(11), (1)-->(12) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null min:11!null max:12!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -372,7 +372,7 @@ project ├── columns: k:1!null sum:11!null max:12!null ├── key: (1) ├── fd: ()-->(12), (1)-->(11) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null sum:11!null max:12!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -471,7 +471,7 @@ project ├── columns: k:1!null min:11!null max:12 ├── key: (1) ├── fd: ()-->(11), (1)-->(12) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null min:11 max:12 │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -513,7 +513,7 @@ project ├── columns: k:1!null count:11!null ├── key: (1) ├── fd: ()-->(11) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null count:11!null │ ├── grouping columns: k:1!null │ ├── key: (1) @@ -552,7 +552,7 @@ project │ ├── project │ │ ├── columns: exists:22!null │ │ ├── outer: (4) - │ │ ├── group-by + │ │ ├── group-by (hash) │ │ │ ├── columns: ref_1.k:7!null true_agg:21 │ │ │ ├── grouping columns: ref_1.k:7!null │ │ │ ├── outer: (4) @@ -605,7 +605,7 @@ project ├── columns: k:5!null string_agg:12!null ├── key: (5) ├── fd: ()-->(12) - ├── group-by + ├── group-by (hash) │ ├── columns: k:5!null string_agg:12!null │ ├── grouping columns: k:5!null │ ├── key: (5) @@ -656,7 +656,7 @@ project ├── immutable ├── key: (5) ├── fd: ()-->(13) - ├── group-by + ├── group-by (hash) │ ├── columns: k:5 string_agg:13 │ ├── grouping columns: k:5 │ ├── immutable diff --git a/pkg/sql/opt/norm/testdata/rules/scalar b/pkg/sql/opt/norm/testdata/rules/scalar index 658059503f18..a669f287ef65 100644 --- a/pkg/sql/opt/norm/testdata/rules/scalar +++ b/pkg/sql/opt/norm/testdata/rules/scalar @@ -1562,7 +1562,7 @@ SELECT ARRAY(SELECT k FROM a WHERE a.k = b.k) FROM a AS b ---- project ├── columns: array:16 - ├── group-by + ├── group-by (hash) │ ├── columns: b.k:1!null a.k:8!null array_agg:17!null │ ├── grouping columns: b.k:1!null │ ├── key: (8) @@ -1594,7 +1594,7 @@ SELECT ARRAY(SELECT k FROM a WHERE a.i = b.i ORDER BY a.k) FROM a AS b ---- project ├── columns: array:16 - ├── group-by + ├── group-by (hash) │ ├── columns: b.k:1!null a.k:8 array_agg:17 │ ├── grouping columns: b.k:1!null │ ├── internal-ordering: +8 opt(9) @@ -1633,7 +1633,7 @@ SELECT ARRAY(SELECT generate_series(1, a.k) ORDER BY 1 DESC) FROM a project ├── columns: array:10 ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null canary:11 array_agg:12 │ ├── grouping columns: k:1!null │ ├── internal-ordering: -8 @@ -1682,7 +1682,7 @@ SELECT ARRAY(SELECT ARRAY(SELECT k FROM a)[1] FROM a as b WHERE b.k = c.k) FROM ---- project ├── columns: array:24 - ├── group-by + ├── group-by (hash) │ ├── columns: c.k:1!null canary:25!null array_agg:26 │ ├── grouping columns: c.k:1!null │ ├── key: (1) @@ -1726,7 +1726,7 @@ SELECT ARRAY(SELECT ARRAY(SELECT k FROM a WHERE a.k = b.k)[1] FROM a as b WHERE ---- project ├── columns: array:26 - ├── group-by + ├── group-by (hash) │ ├── columns: c.k:1!null canary:27 array_agg:28 │ ├── grouping columns: c.k:1!null │ ├── key: (1) @@ -1744,7 +1744,7 @@ project │ │ │ ├── cardinality: [0 - 1] │ │ │ ├── key: () │ │ │ ├── fd: ()-->(23,27) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: a.k:15!null array_agg:24!null │ │ │ │ ├── outer: (1) │ │ │ │ ├── cardinality: [0 - 1] @@ -1863,7 +1863,7 @@ project │ ├── columns: rel.oid:1 inhrelid:32 array_agg:71 │ ├── scan pg_class [as=rel] │ │ └── columns: rel.oid:1 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: inhrelid:32 array_agg:71 │ │ ├── internal-ordering: +34 opt(32) │ │ ├── outer: (1) diff --git a/pkg/sql/opt/norm/testdata/rules/select b/pkg/sql/opt/norm/testdata/rules/select index 3419558f79b3..7db6aa5c4846 100644 --- a/pkg/sql/opt/norm/testdata/rules/select +++ b/pkg/sql/opt/norm/testdata/rules/select @@ -703,7 +703,7 @@ project │ ├── columns: i:2 f:3 sum:8!null │ ├── key: (2,3) │ ├── fd: ()-->(8) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: i:2 f:3 sum:8 │ │ ├── grouping columns: i:2 f:3 │ │ ├── key: (2,3) @@ -1266,7 +1266,7 @@ inner-join (hash) norm expect=PushSelectIntoGroupBy SELECT * FROM (SELECT i, count(*) FROM a GROUP BY i) a WHERE i=1 ---- -group-by +group-by (streaming) ├── columns: i:2!null count:8!null ├── cardinality: [0 - 1] ├── key: () @@ -1311,7 +1311,7 @@ select ├── columns: k:1!null i:2!null m:8!null ├── key: (1) ├── fd: ()-->(8), (1)==(2), (2)==(1), (1)-->(2) - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null i:2!null max:8 │ ├── grouping columns: k:1!null │ ├── key: (1) diff --git a/pkg/sql/opt/norm/testdata/rules/side_effects b/pkg/sql/opt/norm/testdata/rules/side_effects index b00d8be00483..129dd6ddb4fd 100644 --- a/pkg/sql/opt/norm/testdata/rules/side_effects +++ b/pkg/sql/opt/norm/testdata/rules/side_effects @@ -41,7 +41,7 @@ SELECT avg(f) FROM a WHERE i=5 GROUP BY i+(random()*10)::int, i+1 project ├── columns: avg:8 ├── volatile - └── group-by + └── group-by (hash) ├── columns: avg:8 column9:9 ├── grouping columns: column9:9 ├── volatile diff --git a/pkg/sql/opt/optbuilder/testdata/aggregate b/pkg/sql/opt/optbuilder/testdata/aggregate index 199e7567a182..c11335a6c2cb 100644 --- a/pkg/sql/opt/optbuilder/testdata/aggregate +++ b/pkg/sql/opt/optbuilder/testdata/aggregate @@ -192,7 +192,7 @@ SELECT 1 r FROM kv GROUP BY v ---- project ├── columns: r:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 │ ├── grouping columns: v:2 │ └── project @@ -287,7 +287,7 @@ error (22023): unsupported comparison operator: < build SELECT count(*), k FROM kv GROUP BY k ---- -group-by +group-by (hash) ├── columns: count:7!null k:1!null ├── grouping columns: k:1!null ├── project @@ -301,7 +301,7 @@ group-by build SELECT count(*), k FROM kv GROUP BY 2 ---- -group-by +group-by (hash) ├── columns: count:7!null k:1!null ├── grouping columns: k:1!null ├── project @@ -355,7 +355,7 @@ error (42601): non-integer constant in GROUP BY: 'a' build SELECT count(*), kv.s FROM kv GROUP BY s ---- -group-by +group-by (hash) ├── columns: count:7!null s:4 ├── grouping columns: s:4 ├── project @@ -368,7 +368,7 @@ group-by build SELECT count(*), s FROM kv GROUP BY kv.s ---- -group-by +group-by (hash) ├── columns: count:7!null s:4 ├── grouping columns: s:4 ├── project @@ -381,7 +381,7 @@ group-by build SELECT count(*), kv.s FROM kv GROUP BY kv.s ---- -group-by +group-by (hash) ├── columns: count:7!null s:4 ├── grouping columns: s:4 ├── project @@ -394,7 +394,7 @@ group-by build SELECT count(*), s FROM kv GROUP BY s ---- -group-by +group-by (hash) ├── columns: count:7!null s:4 ├── grouping columns: s:4 ├── project @@ -408,7 +408,7 @@ group-by build SELECT v, count(*), w FROM kv GROUP BY v, w ---- -group-by +group-by (hash) ├── columns: v:2 count:7!null w:3 ├── grouping columns: v:2 w:3 ├── project @@ -422,7 +422,7 @@ group-by build SELECT v, count(*), w FROM kv GROUP BY 1, 3 ---- -group-by +group-by (hash) ├── columns: v:2 count:7!null w:3 ├── grouping columns: v:2 w:3 ├── project @@ -436,7 +436,7 @@ group-by build SELECT count(*), upper(s) FROM kv GROUP BY upper(s) ---- -group-by +group-by (hash) ├── columns: count:7!null upper:8 ├── grouping columns: column8:8 ├── project @@ -454,7 +454,7 @@ SELECT count(*) FROM kv GROUP BY 1+2 ---- project ├── columns: count:7!null - └── group-by + └── group-by (hash) ├── columns: count_rows:7!null column8:8!null ├── grouping columns: column8:8!null ├── project @@ -471,7 +471,7 @@ SELECT count(*) FROM kv GROUP BY length('abc') ---- project ├── columns: count:7!null - └── group-by + └── group-by (hash) ├── columns: count_rows:7!null column8:8 ├── grouping columns: column8:8 ├── project @@ -489,7 +489,7 @@ SELECT count(*), upper(s) FROM kv GROUP BY s ---- project ├── columns: count:7!null upper:8 - ├── group-by + ├── group-by (hash) │ ├── columns: s:4 count_rows:7!null │ ├── grouping columns: s:4 │ ├── project @@ -511,7 +511,7 @@ error (42803): column "s" must appear in the GROUP BY clause or be used in an ag build SELECT count(*), k+v AS r FROM kv GROUP BY k+v ---- -group-by +group-by (hash) ├── columns: count:7!null r:8 ├── grouping columns: column8:8 ├── project @@ -530,7 +530,7 @@ SELECT count(*), k+v AS r FROM kv GROUP BY k, v ---- project ├── columns: count:7!null r:8 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null v:2 count_rows:7!null │ ├── grouping columns: k:1!null v:2 │ ├── project @@ -566,7 +566,7 @@ error (42803): max(): avg(): aggregate function calls cannot be nested build SELECT count(kv.k) AS count_1, kv.v + kv.w AS lx FROM kv GROUP BY kv.v + kv.w ---- -group-by +group-by (hash) ├── columns: count_1:7!null lx:8 ├── grouping columns: column8:8 ├── project @@ -643,7 +643,7 @@ SELECT v, count(k) FROM kv GROUP BY v ORDER BY v sort ├── columns: v:2 count:7!null ├── ordering: +2 - └── group-by + └── group-by (hash) ├── columns: v:2 count:7!null ├── grouping columns: v:2 ├── project @@ -660,7 +660,7 @@ SELECT v, count(k) FROM kv GROUP BY v ORDER BY v DESC sort ├── columns: v:2 count:7!null ├── ordering: -2 - └── group-by + └── group-by (hash) ├── columns: v:2 count:7!null ├── grouping columns: v:2 ├── project @@ -677,7 +677,7 @@ SELECT v, count(k) FROM kv GROUP BY v ORDER BY count(k) DESC sort ├── columns: v:2 count:7!null ├── ordering: -7 - └── group-by + └── group-by (hash) ├── columns: v:2 count:7!null ├── grouping columns: v:2 ├── project @@ -696,7 +696,7 @@ sort ├── ordering: +8 └── project ├── columns: column8:8 v:2 count:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 count:7!null │ ├── grouping columns: v:2 │ ├── project @@ -715,7 +715,7 @@ SELECT v FROM kv GROUP BY v ORDER BY sum(k) sort ├── columns: v:2 [hidden: sum:7!null] ├── ordering: +7 - └── group-by + └── group-by (hash) ├── columns: v:2 sum:7!null ├── grouping columns: v:2 ├── project @@ -732,7 +732,7 @@ SELECT v, count(k) FROM kv GROUP BY v ORDER BY 1 DESC sort ├── columns: v:2 count:7!null ├── ordering: -2 - └── group-by + └── group-by (hash) ├── columns: v:2 count:7!null ├── grouping columns: v:2 ├── project @@ -794,7 +794,7 @@ scalar-group-by build SELECT upper(s), count(DISTINCT k), count(DISTINCT v), count(DISTINCT (v)) FROM kv GROUP BY upper(s) ---- -group-by +group-by (hash) ├── columns: upper:9 count:7!null count:8!null count:8!null ├── grouping columns: column9:9 ├── project @@ -1703,7 +1703,7 @@ SELECT 1 r FROM kv GROUP BY kv.*; ---- project ├── columns: r:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null v:2 w:3 s:4 │ ├── grouping columns: k:1!null v:2 w:3 s:4 │ └── project @@ -1744,7 +1744,7 @@ sort ├── ordering: +2 └── project ├── columns: to_hex:9 b:2 xor_agg:8 - ├── group-by + ├── group-by (hash) │ ├── columns: b:2 xor_agg:7 xor_agg:8 │ ├── grouping columns: b:2 │ ├── project @@ -1858,7 +1858,7 @@ SELECT (b, a) AS r FROM ab GROUP BY (b, a) ---- project ├── columns: r:5 - ├── group-by + ├── group-by (hash) │ ├── columns: a:1!null b:2 │ ├── grouping columns: a:1!null b:2 │ └── project @@ -1874,7 +1874,7 @@ SELECT min(y), (b, a) AS r ---- project ├── columns: min:10 r:11 - ├── group-by + ├── group-by (hash) │ ├── columns: a:1!null b:2 x:5 min:10 │ ├── grouping columns: a:1!null b:2 x:5 │ ├── project @@ -1898,7 +1898,7 @@ SELECT v, count(k) FROM kv GROUP BY v ORDER BY count(k) sort ├── columns: v:2 count:7!null ├── ordering: +7 - └── group-by + └── group-by (hash) ├── columns: v:2 count:7!null ├── grouping columns: v:2 ├── project @@ -1915,7 +1915,7 @@ SELECT v, count(*) FROM kv GROUP BY v ORDER BY count(*) sort ├── columns: v:2 count:7!null ├── ordering: +7 - └── group-by + └── group-by (hash) ├── columns: v:2 count_rows:7!null ├── grouping columns: v:2 ├── project @@ -1931,7 +1931,7 @@ SELECT v, count(1) FROM kv GROUP BY v ORDER BY count(1) sort ├── columns: v:2 count:8!null ├── ordering: +8 - └── group-by + └── group-by (hash) ├── columns: v:2 count:8!null ├── grouping columns: v:2 ├── project @@ -1949,7 +1949,7 @@ SELECT (k+v)/(v+w) AS r FROM kv GROUP BY k+v, v+w; ---- project ├── columns: r:9 - ├── group-by + ├── group-by (hash) │ ├── columns: column7:7 column8:8 │ ├── grouping columns: column7:7 column8:8 │ └── project @@ -1968,7 +1968,7 @@ SELECT sum(t.kv.w), t.kv.v FROM t.kv GROUP BY v, kv.k * w ---- project ├── columns: sum:7 v:2 - └── group-by + └── group-by (hash) ├── columns: t.public.kv.v:2 sum:7 column8:8 ├── grouping columns: t.public.kv.v:2 column8:8 ├── project @@ -1986,7 +1986,7 @@ SELECT sum(t.kv.w), lower(s), t.kv.v + k * t.kv.w AS r, t.kv.v FROM t.kv GROUP B ---- project ├── columns: sum:7 lower:8 r:10 v:2 - ├── group-by + ├── group-by (hash) │ ├── columns: t.public.kv.v:2 sum:7 column8:8 column9:9 │ ├── grouping columns: t.public.kv.v:2 column8:8 column9:9 │ ├── project @@ -2008,7 +2008,7 @@ SELECT b1.b AND abc.c AND b2.b AS r FROM bools b1, bools b2, abc GROUP BY b1.b A ---- project ├── columns: r:16 - ├── group-by + ├── group-by (hash) │ ├── columns: b2.b:5 column15:15 │ ├── grouping columns: b2.b:5 column15:15 │ └── project @@ -2040,7 +2040,7 @@ SELECT b1.b OR abc.c OR b2.b AS r FROM bools b1, bools b2, abc GROUP BY b1.b OR ---- project ├── columns: r:16 - ├── group-by + ├── group-by (hash) │ ├── columns: b2.b:5 column15:15 │ ├── grouping columns: b2.b:5 column15:15 │ └── project @@ -2072,7 +2072,7 @@ SELECT k % w % v AS r FROM kv GROUP BY k % w, v ---- project ├── columns: r:8 - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 column7:7 │ ├── grouping columns: v:2 column7:7 │ └── project @@ -2089,7 +2089,7 @@ SELECT concat(concat(s, a), a) FROM kv, abc GROUP BY concat(s, a), a ---- project ├── columns: concat:14 - ├── group-by + ├── group-by (hash) │ ├── columns: a:7!null column13:13 │ ├── grouping columns: a:7!null column13:13 │ └── project @@ -2116,7 +2116,7 @@ SELECT k < w AND v != 5 AS r FROM kv GROUP BY k < w, v ---- project ├── columns: r:8 - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 column7:7 │ ├── grouping columns: v:2 column7:7 │ └── project @@ -2142,7 +2142,7 @@ SELECT a.bar @> b.baz AND b.baz @> b.baz AS r FROM foo AS a, foo AS b GROUP BY a ---- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: b.baz:7 column11:11 │ ├── grouping columns: b.baz:7 column11:11 │ └── project @@ -2169,7 +2169,7 @@ SELECT b.baz <@ a.bar AND b.baz <@ b.baz AS r FROM foo AS a, foo AS b GROUP BY b ---- project ├── columns: r:12 - ├── group-by + ├── group-by (hash) │ ├── columns: b.baz:7 column11:11 │ ├── grouping columns: b.baz:7 column11:11 │ └── project @@ -2196,7 +2196,7 @@ SELECT date_trunc('second', a.t) - date_trunc('minute', b.t) AS r FROM times a, ---- project ├── columns: r:9 - ├── group-by + ├── group-by (hash) │ ├── columns: column7:7 column8:8 │ ├── grouping columns: column7:7 column8:8 │ └── project @@ -2223,7 +2223,7 @@ error (42803): column "t" must appear in the GROUP BY clause or be used in an ag build SELECT NOT b AS r FROM bools GROUP BY NOT b ---- -group-by +group-by (hash) ├── columns: r:5 ├── grouping columns: column5:5 └── project @@ -2243,7 +2243,7 @@ SELECT NOT b AS r FROM bools GROUP BY b ---- project ├── columns: r:5 - ├── group-by + ├── group-by (hash) │ ├── columns: b:1 │ ├── grouping columns: b:1 │ └── project @@ -2258,7 +2258,7 @@ SELECT +k * (-w) AS r FROM kv GROUP BY +k, -w ---- project ├── columns: r:8 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null column7:7 │ ├── grouping columns: k:1!null column7:7 │ └── project @@ -2275,7 +2275,7 @@ SELECT k * (-w) FROM kv GROUP BY +k, -w ---- project ├── columns: "?column?":8 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null column7:7 │ ├── grouping columns: k:1!null column7:7 │ └── project @@ -2292,7 +2292,7 @@ SELECT +k * (-w) AS r FROM kv GROUP BY k, w ---- project ├── columns: r:7 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null w:3 │ ├── grouping columns: k:1!null w:3 │ └── project @@ -2307,7 +2307,7 @@ SELECT 1 + min(v*2) AS r FROM kv GROUP BY k+3 ---- project ├── columns: r:10 - ├── group-by + ├── group-by (hash) │ ├── columns: min:8 column9:9!null │ ├── grouping columns: column9:9!null │ ├── project @@ -2328,7 +2328,7 @@ SELECT count(*) FROM kv GROUP BY k, k ---- project ├── columns: count:7!null - └── group-by + └── group-by (hash) ├── columns: k:1!null count_rows:7!null ├── grouping columns: k:1!null ├── project @@ -2343,7 +2343,7 @@ SELECT count(upper(s)) FROM kv GROUP BY upper(s) ---- project ├── columns: count:8!null - └── group-by + └── group-by (hash) ├── columns: column7:7 count:8!null ├── grouping columns: column7:7 ├── project @@ -2361,7 +2361,7 @@ SELECT sum(abc.d) FROM kv JOIN abc ON kv.k >= abc.d GROUP BY kv.* ---- project ├── columns: sum:13!null - └── group-by + └── group-by (hash) ├── columns: k:1!null v:2 w:3 s:4 sum:13!null ├── grouping columns: k:1!null v:2 w:3 s:4 ├── project @@ -2493,7 +2493,7 @@ scalar-group-by build SELECT y, count(*) FILTER (WHERE x > 5) FROM xyz GROUP BY y ---- -group-by +group-by (hash) ├── columns: y:2 count:8!null ├── grouping columns: y:2 ├── project @@ -2524,7 +2524,7 @@ sort ├── ordering: +7 └── project ├── columns: max:7!null - └── group-by + └── group-by (hash) ├── columns: v:2 max:7!null ├── grouping columns: v:2 ├── project @@ -2543,7 +2543,7 @@ sort ├── ordering: +7 └── project ├── columns: max:7!null - └── group-by + └── group-by (hash) ├── columns: v:2 max:7!null ├── grouping columns: v:2 ├── project @@ -2562,7 +2562,7 @@ sort ├── ordering: +7 └── project ├── columns: max:7!null - └── group-by + └── group-by (hash) ├── columns: v:2 max:7!null ├── grouping columns: v:2 ├── project @@ -2581,7 +2581,7 @@ sort ├── ordering: +7 └── project ├── columns: max:7!null - └── group-by + └── group-by (hash) ├── columns: v:2 max:7!null ├── grouping columns: v:2 ├── project @@ -2600,7 +2600,7 @@ sort ├── ordering: +8 └── project ├── columns: mk2:8!null max:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 max:7!null │ ├── grouping columns: v:2 │ ├── project @@ -2621,7 +2621,7 @@ SELECT max((k+v)/(k-v)) AS r, (k+v)*(k-v) AS s FROM kv GROUP BY k+v, k-v ---- project ├── columns: r:8 s:11 - ├── group-by + ├── group-by (hash) │ ├── columns: max:8 column9:9 column10:10 │ ├── grouping columns: column9:9 column10:10 │ ├── project @@ -2643,7 +2643,7 @@ SELECT max((k+v)/(k-v)) AS r, (k+v)*(k-v) AS s FROM kv GROUP BY k+v, (k+v)/(k-v) ---- project ├── columns: r:8 s:10 - └── group-by + └── group-by (hash) ├── columns: column7:7 max:8 column9:9 column10:10 ├── grouping columns: column7:7 column9:9 column10:10 ├── project @@ -3659,7 +3659,7 @@ FROM GROUP BY firstCol, secondCol, thirdCol; ---- -group-by +group-by (hash) ├── columns: firstcol:1(int!null) secondcol:2(int) thirdcol:2(int) ├── grouping columns: t.public.xyz.x:1(int!null) t.public.xyz.y:2(int) ├── stats: [rows=1000, distinct(1,2)=1000, null(1,2)=0] @@ -3794,7 +3794,7 @@ SELECT array_agg(col1 ORDER BY col1) FROM tab GROUP BY col2 ---- project ├── columns: array_agg:7 - └── group-by + └── group-by (hash) ├── columns: col2:2!null array_agg:7 ├── grouping columns: col2:2!null ├── window partition=(2) ordering=+1 @@ -3813,7 +3813,7 @@ SELECT array_agg(col1 ORDER BY col1), array_agg(col3 ORDER BY col1) FROM tab GRO ---- project ├── columns: array_agg:7 array_agg:8 - └── group-by + └── group-by (hash) ├── columns: col2:2!null array_agg:7 array_agg:8 ├── grouping columns: col2:2!null ├── window partition=(2) ordering=+1 @@ -3838,7 +3838,7 @@ project ├── columns: array_agg:7 array_agg:8 └── select ├── columns: col2:2!null array_agg:7 array_agg:8 - ├── group-by + ├── group-by (hash) │ ├── columns: col2:2!null array_agg:7 array_agg:8 │ ├── grouping columns: col2:2!null │ ├── window partition=(2) ordering=+1 @@ -3900,7 +3900,7 @@ SELECT array_agg(col1 ORDER BY col1) FROM tab GROUP BY upper(col3) ---- project ├── columns: array_agg:7 - └── group-by + └── group-by (hash) ├── columns: array_agg:7 column8:8 ├── grouping columns: column8:8 ├── window partition=(8) ordering=+1 @@ -3921,7 +3921,7 @@ project build SELECT array_agg(col1 ORDER BY col1), upper(col3) FROM tab GROUP BY upper(col3) ---- -group-by +group-by (hash) ├── columns: array_agg:7 upper:8 ├── grouping columns: column8:8 ├── window partition=(8) ordering=+1 @@ -3944,7 +3944,7 @@ SELECT array_agg(lower(col3)) FROM tab GROUP BY upper(col3) ---- project ├── columns: array_agg:8 - └── group-by + └── group-by (hash) ├── columns: array_agg:8 column9:9 ├── grouping columns: column9:9 ├── project @@ -4031,7 +4031,7 @@ SELECT v FROM kv GROUP BY k ---- project ├── columns: v:2 - └── group-by + └── group-by (hash) ├── columns: k:1!null v:2 ├── grouping columns: k:1!null v:2 └── project @@ -4045,7 +4045,7 @@ SELECT v FROM kv GROUP BY k, v ---- project ├── columns: v:2 - └── group-by + └── group-by (hash) ├── columns: k:1!null v:2 ├── grouping columns: k:1!null v:2 └── project @@ -4058,7 +4058,7 @@ SELECT count(*), k+v FROM kv GROUP BY k ---- project ├── columns: count:7!null "?column?":8 - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null v:2 count_rows:7!null │ ├── grouping columns: k:1!null v:2 │ ├── project @@ -4077,7 +4077,7 @@ project ├── columns: count:7!null └── select ├── columns: k:1!null v:2!null count_rows:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: k:1!null v:2 count_rows:7!null │ ├── grouping columns: k:1!null v:2 │ ├── project @@ -4092,7 +4092,7 @@ project build SELECT k, v, count(*) FROM kv JOIN ab ON a=k GROUP BY k ---- -group-by +group-by (hash) ├── columns: k:1!null v:2 count:11!null ├── grouping columns: k:1!null v:2 ├── project @@ -4119,7 +4119,7 @@ SELECT x, y FROM abxy GROUP BY a, b ---- project ├── columns: x:3 y:4 - └── group-by + └── group-by (hash) ├── columns: a:1!null b:2!null x:3 y:4 ├── grouping columns: a:1!null b:2!null x:3 y:4 └── project @@ -4133,7 +4133,7 @@ SELECT x, y FROM abxy GROUP BY x, a, b ---- project ├── columns: x:3 y:4 - └── group-by + └── group-by (hash) ├── columns: a:1!null b:2!null x:3 y:4 ├── grouping columns: a:1!null b:2!null x:3 y:4 └── project @@ -4146,7 +4146,7 @@ SELECT x, y FROM abxy GROUP BY x, y, a, b ---- project ├── columns: x:3 y:4 - └── group-by + └── group-by (hash) ├── columns: a:1!null b:2!null x:3 y:4 ├── grouping columns: a:1!null b:2!null x:3 y:4 └── project @@ -4159,7 +4159,7 @@ SELECT x, y FROM abxy NATURAL JOIN ab GROUP BY a, b ---- project ├── columns: x:3 y:4 - └── group-by + └── group-by (hash) ├── columns: abxy.a:1!null abxy.b:2!null x:3 y:4 ├── grouping columns: abxy.a:1!null abxy.b:2!null x:3 y:4 └── project @@ -4180,7 +4180,7 @@ SELECT x, y FROM abxy NATURAL JOIN ab GROUP BY a, b, x ---- project ├── columns: x:3 y:4 - └── group-by + └── group-by (hash) ├── columns: abxy.a:1!null abxy.b:2!null x:3 y:4 ├── grouping columns: abxy.a:1!null abxy.b:2!null x:3 y:4 └── project @@ -4198,7 +4198,7 @@ project build SELECT abxy.*, ab.* FROM abxy, ab GROUP BY abxy.a, abxy.b, ab.a ---- -group-by +group-by (hash) ├── columns: a:1!null b:2!null x:3 y:4 a:7!null b:8 ├── grouping columns: abxy.a:1!null abxy.b:2!null x:3 y:4 ab.a:7!null ab.b:8 └── project @@ -4223,7 +4223,7 @@ SELECT x FROM (SELECT a, b, x FROM abxy EXCEPT SELECT a, b, 1 FROM ab) GROUP BY ---- project ├── columns: x:3 - └── group-by + └── group-by (hash) ├── columns: abxy.a:1!null abxy.b:2 x:3 ├── grouping columns: abxy.a:1!null abxy.b:2 x:3 └── except @@ -4249,7 +4249,7 @@ SELECT v, w FROM kv FULL JOIN ab ON k=a GROUP BY k ---- project ├── columns: v:2 w:3 - └── group-by + └── group-by (hash) ├── columns: k:1 v:2 w:3 ├── grouping columns: k:1 v:2 w:3 └── project @@ -4273,7 +4273,7 @@ error (42803): column "table_schema" must appear in the GROUP BY clause or be us build SELECT x + 1 AS z FROM abxy GROUP BY z ---- -group-by +group-by (hash) ├── columns: z:7 ├── grouping columns: z:7 └── project @@ -4289,7 +4289,7 @@ SELECT (x % 10) AS x FROM abxy GROUP BY x ---- project ├── columns: x:7 - ├── group-by + ├── group-by (hash) │ ├── columns: abxy.x:3 │ ├── grouping columns: abxy.x:3 │ └── project @@ -4312,7 +4312,7 @@ project └── subquery [as=x:13] └── max1-row ├── columns: v:8 - └── group-by + └── group-by (hash) ├── columns: v:8 ├── grouping columns: v:8 └── project @@ -4329,7 +4329,7 @@ error (42803): sum(): aggregate functions are not allowed in GROUP BY build SELECT x + 1 FROM abxy GROUP BY "?column?" ---- -group-by +group-by (hash) ├── columns: "?column?":7 ├── grouping columns: "?column?":7 └── project @@ -4361,7 +4361,7 @@ error (42702): GROUP BY "x" is ambiguous build SELECT (x + 1) AS u, (x + 1) AS u FROM abxy GROUP BY u ---- -group-by +group-by (hash) ├── columns: u:7 u:7 ├── grouping columns: u:7 └── project @@ -4382,7 +4382,7 @@ SELECT sum(x + 1) AS x, sum(y + 1) AS x FROM abxy GROUP BY x ---- project ├── columns: x:8 x:10 - └── group-by + └── group-by (hash) ├── columns: x:3 sum:8 sum:10 ├── grouping columns: x:3 ├── project @@ -4420,7 +4420,7 @@ project ├── columns: sum:7 └── select ├── columns: t.y:4 sum:7 - ├── group-by + ├── group-by (hash) │ ├── columns: t.y:4 sum:7 │ ├── grouping columns: t.y:4 │ ├── project @@ -4475,7 +4475,7 @@ GROUP BY ten ---- select ├── columns: ten:5 sum:20 - ├── group-by + ├── group-by (hash) │ ├── columns: a.ten:5 sum:20 │ ├── grouping columns: a.ten:5 │ ├── project @@ -4698,7 +4698,7 @@ project ├── columns: c0:1(int) └── select ├── columns: c0:1(int) min:6(float!null) - ├── group-by + ├── group-by (hash) │ ├── columns: c0:1(int) min:6(float) │ ├── grouping columns: c0:1(int) │ ├── project diff --git a/pkg/sql/opt/optbuilder/testdata/distinct b/pkg/sql/opt/optbuilder/testdata/distinct index ff253cb93e47..62d1579e39c8 100644 --- a/pkg/sql/opt/optbuilder/testdata/distinct +++ b/pkg/sql/opt/optbuilder/testdata/distinct @@ -215,7 +215,7 @@ distinct-on ├── grouping columns: max:6!null └── project ├── columns: max:6!null - └── group-by + └── group-by (hash) ├── columns: x:1!null max:6!null ├── grouping columns: x:1!null ├── project @@ -275,7 +275,7 @@ distinct-on ├── columns: r:7!null s:8!null max:6 ├── select │ ├── columns: x:1!null y:2!null max:6 - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: x:1!null y:2 max:6 │ │ ├── grouping columns: x:1!null y:2 │ │ ├── project diff --git a/pkg/sql/opt/optbuilder/testdata/distinct_on b/pkg/sql/opt/optbuilder/testdata/distinct_on index 20b8cca83daf..cd0f339c26bf 100644 --- a/pkg/sql/opt/optbuilder/testdata/distinct_on +++ b/pkg/sql/opt/optbuilder/testdata/distinct_on @@ -278,7 +278,7 @@ SELECT DISTINCT ON(y) min(x) FROM xyz GROUP BY y distinct-on ├── columns: min:8 [hidden: y:2] ├── grouping columns: y:2 - ├── group-by + ├── group-by (hash) │ ├── columns: y:2 min:8 │ ├── grouping columns: y:2 │ ├── project @@ -302,7 +302,7 @@ distinct-on ├── columns: min:8!null └── select ├── columns: y:2 min:8!null - ├── group-by + ├── group-by (hash) │ ├── columns: y:2 min:8 │ ├── grouping columns: y:2 │ ├── project diff --git a/pkg/sql/opt/optbuilder/testdata/having b/pkg/sql/opt/optbuilder/testdata/having index 299bb7e4e89a..31cdc50aa48e 100644 --- a/pkg/sql/opt/optbuilder/testdata/having +++ b/pkg/sql/opt/optbuilder/testdata/having @@ -30,7 +30,7 @@ SELECT s, count(*) FROM kv GROUP BY s HAVING count(*) > 1 ---- select ├── columns: s:4 count:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: s:4 count_rows:7!null │ ├── grouping columns: s:4 │ ├── project @@ -108,7 +108,7 @@ project ├── columns: "?column?":7!null ├── select │ ├── columns: k:1!null v:2!null - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: k:1!null v:2 │ │ ├── grouping columns: k:1!null v:2 │ │ └── project @@ -130,7 +130,7 @@ SELECT count(*), k+w AS r FROM kv GROUP BY k+w HAVING (k+w) > 5 ---- select ├── columns: count:7!null r:8!null - ├── group-by + ├── group-by (hash) │ ├── columns: count_rows:7!null column8:8 │ ├── grouping columns: column8:8 │ ├── project @@ -157,7 +157,7 @@ project ├── columns: max:7 └── select ├── columns: v:2!null max:7 - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 max:7 │ ├── grouping columns: v:2 │ ├── project @@ -177,7 +177,7 @@ project ├── columns: sum:7 └── select ├── columns: sum:7 column8:8!null - ├── group-by + ├── group-by (hash) │ ├── columns: sum:7 column8:8 │ ├── grouping columns: column8:8 │ ├── project @@ -199,7 +199,7 @@ project ├── columns: sum:7!null └── select ├── columns: sum:7!null column8:8 - ├── group-by + ├── group-by (hash) │ ├── columns: sum:7 column8:8 │ ├── grouping columns: column8:8 │ ├── project @@ -221,7 +221,7 @@ project ├── columns: v:2 └── select ├── columns: t.public.kv.v:2 column7:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: t.public.kv.v:2 column7:7 │ ├── grouping columns: t.public.kv.v:2 column7:7 │ └── project @@ -243,7 +243,7 @@ SELECT upper(s), count(s), count(upper(s)) FROM t.kv GROUP BY upper(s) HAVING co ---- select ├── columns: upper:8 count:7!null count:9!null - ├── group-by + ├── group-by (hash) │ ├── columns: count:7!null column8:8 count:9!null │ ├── grouping columns: column8:8 │ ├── project @@ -269,7 +269,7 @@ project ├── ordering: +7 └── select ├── columns: v:2 sum:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 sum:7!null │ ├── grouping columns: v:2 │ ├── project @@ -292,7 +292,7 @@ sort ├── columns: sum:7!null └── select ├── columns: v:2 sum:7!null max:8!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 sum:7!null max:8!null │ ├── grouping columns: v:2 │ ├── project @@ -317,7 +317,7 @@ sort ├── columns: sum:7!null └── select ├── columns: v:2!null sum:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 sum:7!null │ ├── grouping columns: v:2 │ ├── project @@ -340,7 +340,7 @@ sort ├── columns: max:7!null └── select ├── columns: v:2 max:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 max:7!null │ ├── grouping columns: v:2 │ ├── project @@ -363,7 +363,7 @@ sort ├── columns: max:7!null └── select ├── columns: v:2 max:7!null - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 max:7!null │ ├── grouping columns: v:2 │ ├── project diff --git a/pkg/sql/opt/optbuilder/testdata/limit b/pkg/sql/opt/optbuilder/testdata/limit index 9eecb5fc009d..7a4edf8637e3 100644 --- a/pkg/sql/opt/optbuilder/testdata/limit +++ b/pkg/sql/opt/optbuilder/testdata/limit @@ -136,22 +136,26 @@ limit ├── columns: sum:6 [hidden: v:2] ├── internal-ordering: -2 ├── ordering: -2 - ├── sort + ├── project │ ├── columns: v:2 sum:6 │ ├── ordering: -2 │ ├── limit hint: 10.00 - │ └── project - │ ├── columns: v:2 sum:6 - │ └── group-by - │ ├── columns: k:1!null v:2 sum:6 - │ ├── grouping columns: k:1!null v:2 - │ ├── project - │ │ ├── columns: k:1!null v:2 w:3 - │ │ └── scan t - │ │ └── columns: k:1!null v:2 w:3 crdb_internal_mvcc_timestamp:4 tableoid:5 - │ └── aggregations - │ └── sum [as=sum:6] - │ └── w:3 + │ └── group-by (partial streaming) + │ ├── columns: k:1!null v:2 sum:6 + │ ├── grouping columns: k:1!null v:2 + │ ├── ordering: -2 + │ ├── limit hint: 10.00 + │ ├── sort + │ │ ├── columns: k:1!null v:2 w:3 + │ │ ├── ordering: -2 + │ │ ├── limit hint: 10.00 + │ │ └── project + │ │ ├── columns: k:1!null v:2 w:3 + │ │ └── scan t + │ │ └── columns: k:1!null v:2 w:3 crdb_internal_mvcc_timestamp:4 tableoid:5 + │ └── aggregations + │ └── sum [as=sum:6] + │ └── w:3 └── 10 build diff --git a/pkg/sql/opt/optbuilder/testdata/projection-reuse b/pkg/sql/opt/optbuilder/testdata/projection-reuse index 0cc72c3a7edf..3353f0e89b1a 100644 --- a/pkg/sql/opt/optbuilder/testdata/projection-reuse +++ b/pkg/sql/opt/optbuilder/testdata/projection-reuse @@ -70,7 +70,7 @@ sort build SELECT random(), random() FROM ab GROUP BY random() ---- -group-by +group-by (hash) ├── columns: random:6 random:6 ├── grouping columns: column6:6 └── project diff --git a/pkg/sql/opt/optbuilder/testdata/subquery b/pkg/sql/opt/optbuilder/testdata/subquery index 7f7b2abddd2b..03aaef669d53 100644 --- a/pkg/sql/opt/optbuilder/testdata/subquery +++ b/pkg/sql/opt/optbuilder/testdata/subquery @@ -1475,7 +1475,7 @@ project ├── columns: count_rows:16!null └── project ├── columns: count_rows:16!null - └── group-by + └── group-by (hash) ├── columns: count_rows:16!null a:17 ├── grouping columns: a:17 ├── project @@ -1506,7 +1506,7 @@ project └── subquery [as=y:17] └── max1-row ├── columns: column16:16 - └── group-by + └── group-by (hash) ├── columns: column16:16 ├── grouping columns: column16:16 └── project @@ -1539,7 +1539,7 @@ project ├── columns: r:18!null ├── select │ ├── columns: a:16!null a:17!null - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: a:16 a:17 │ │ ├── grouping columns: a:16 a:17 │ │ └── project @@ -1569,7 +1569,7 @@ project ├── columns: array:13 ├── select │ ├── columns: t1.a:6 count_rows:11!null a:12!null - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: t1.a:6 count_rows:11!null a:12 │ │ ├── grouping columns: t1.a:6 a:12 │ │ ├── project @@ -1618,7 +1618,7 @@ project ├── columns: r:21 └── project ├── columns: r:21 - ├── group-by + ├── group-by (hash) │ ├── columns: a:20 │ ├── grouping columns: a:20 │ └── project @@ -1662,7 +1662,7 @@ project │ ├── columns: i:9 max:15 │ ├── ordering: +9 │ ├── limit hint: 1.00 - │ └── group-by + │ └── group-by (hash) │ ├── columns: i:9 max:15 │ ├── grouping columns: i:9 │ ├── project @@ -1849,7 +1849,7 @@ SELECT (SELECT row(max(t1.a), max(t2.a), max(t1.a + t2.a)) FROM t1) FROM t2 GROU ---- project ├── columns: row:17 - ├── group-by + ├── group-by (hash) │ ├── columns: t2.a:1 max:13 │ ├── grouping columns: t2.a:1 │ ├── project @@ -1936,7 +1936,7 @@ project │ ├── columns: max:11 │ └── project │ ├── columns: max:11 - │ └── group-by + │ └── group-by (hash) │ ├── columns: t1.b:7 max:11 │ ├── grouping columns: t1.b:7 │ ├── project @@ -1951,7 +1951,7 @@ project ├── columns: max:19 └── project ├── columns: max:19 - ├── group-by + ├── group-by (hash) │ ├── columns: t1.b:13 │ ├── grouping columns: t1.b:13 │ └── project @@ -1971,7 +1971,7 @@ GROUP BY t2.b; ---- project ├── columns: array:20 array:21 - ├── group-by + ├── group-by (hash) │ ├── columns: t2.b:2 max:18 │ ├── grouping columns: t2.b:2 │ ├── project @@ -2013,7 +2013,7 @@ GROUP BY t2.a, t2.b; ---- project ├── columns: array:30 array:31 array:32 - ├── group-by + ├── group-by (hash) │ ├── columns: t2.a:1 t2.b:2 max:19 │ ├── grouping columns: t2.a:1 t2.b:2 │ ├── project @@ -2029,7 +2029,7 @@ project ├── array-flatten [as=array:30] │ └── project │ ├── columns: max:11 - │ └── group-by + │ └── group-by (hash) │ ├── columns: max:11 b:12 │ ├── grouping columns: b:12 │ ├── project @@ -2044,7 +2044,7 @@ project ├── array-flatten [as=array:31] │ └── project │ ├── columns: max:21 - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: b:20 │ │ ├── grouping columns: b:20 │ │ └── project @@ -2058,7 +2058,7 @@ project └── array-flatten [as=array:32] └── project ├── columns: max:28 - └── group-by + └── group-by (hash) ├── columns: max:28 b:29 ├── grouping columns: b:29 ├── project @@ -2106,7 +2106,7 @@ project │ │ ├── columns: max:23 │ │ └── project │ │ ├── columns: max:23 - │ │ └── group-by + │ │ └── group-by (hash) │ │ ├── columns: max:23 b:24 │ │ ├── grouping columns: b:24 │ │ ├── project @@ -2186,7 +2186,7 @@ error (42803): aggregate functions are not allowed in WHERE build SELECT s FROM a WHERE (SELECT count(i) >= 100 FROM a) GROUP BY s ---- -group-by +group-by (hash) ├── columns: s:4 ├── grouping columns: s:4 └── project diff --git a/pkg/sql/opt/optbuilder/testdata/window b/pkg/sql/opt/optbuilder/testdata/window index e58021e95b78..36c692c7d150 100644 --- a/pkg/sql/opt/optbuilder/testdata/window +++ b/pkg/sql/opt/optbuilder/testdata/window @@ -659,7 +659,7 @@ project ├── columns: v:2 row_number:11 └── window partition=(10) ├── columns: v:2 avg:10!null row_number:11 - ├── group-by + ├── group-by (hash) │ ├── columns: v:2 avg:10!null │ ├── grouping columns: v:2 │ ├── project diff --git a/pkg/sql/opt/optbuilder/testdata/with b/pkg/sql/opt/optbuilder/testdata/with index baf35ab656e6..99e3f487dc7b 100644 --- a/pkg/sql/opt/optbuilder/testdata/with +++ b/pkg/sql/opt/optbuilder/testdata/with @@ -946,7 +946,7 @@ with &2 (included_parts) │ │ └── filters (true) │ └── filters │ └── p.part:13 = sub_part:10 - └── group-by + └── group-by (hash) ├── columns: sub_part:19 sum:22 ├── grouping columns: sub_part:19 ├── project diff --git a/pkg/sql/opt/optgen/cmd/optgen/metadata.go b/pkg/sql/opt/optgen/cmd/optgen/metadata.go index c248d4fe8bc9..e48a96bde743 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/metadata.go +++ b/pkg/sql/opt/optgen/cmd/optgen/metadata.go @@ -200,6 +200,7 @@ func newMetadata(compiled *lang.CompiledExpr, pkg string) *metadata { "WithID": {fullName: "opt.WithID", passByVal: true}, "Ordering": {fullName: "opt.Ordering", passByVal: true}, "OrderingChoice": {fullName: "props.OrderingChoice", passByVal: true}, + "GroupingOrder": {fullName: "memo.GroupingOrder", passByVal: true}, "TupleOrdinal": {fullName: "memo.TupleOrdinal", passByVal: true}, "ScanLimit": {fullName: "memo.ScanLimit", passByVal: true}, "ScanFlags": {fullName: "memo.ScanFlags", passByVal: true}, diff --git a/pkg/sql/opt/optgen/exprgen/testdata/groupby b/pkg/sql/opt/optgen/exprgen/testdata/groupby index c78637d61177..bfafd48658b3 100644 --- a/pkg/sql/opt/optgen/exprgen/testdata/groupby +++ b/pkg/sql/opt/optgen/exprgen/testdata/groupby @@ -23,7 +23,7 @@ expr (NoOrdering) ) ---- -group-by +group-by (streaming) ├── columns: a:1(int) ├── grouping columns: t.public.abc.a:1(int) ├── internal-ordering: +1 @@ -61,7 +61,7 @@ expr (NoOrdering) ) ---- -group-by +group-by (hash) ├── columns: a:1(int) ├── grouping columns: t.public.abc.a:1(int) ├── cardinality: [0 - 10] diff --git a/pkg/sql/opt/ordering/group_by.go b/pkg/sql/opt/ordering/group_by.go index 9ec5fc48d615..d5b718796e38 100644 --- a/pkg/sql/opt/ordering/group_by.go +++ b/pkg/sql/opt/ordering/group_by.go @@ -108,7 +108,7 @@ func StreamingGroupingColOrdering( for i := range inputOrdering.Columns { // Get any grouping column from the set. Normally there would be at most one // because we have rules that remove redundant grouping columns. - cols := inputOrdering.Columns[i].Group.Intersection(g.GroupingCols) + cols := inputOrdering.Group(i).Intersection(g.GroupingCols) colID, ok := cols.Next(0) if !ok { // This group refers to a column that is not a grouping column. diff --git a/pkg/sql/opt/ordering/scan.go b/pkg/sql/opt/ordering/scan.go index c11ea1cd48a3..417cb1b32ce5 100644 --- a/pkg/sql/opt/ordering/scan.go +++ b/pkg/sql/opt/ordering/scan.go @@ -146,7 +146,7 @@ func scanBuildProvided(expr memo.RelExpr, required *props.OrderingChoice) opt.Or // Column not in output; we are done. break } - direction := (indexCol.Descending != reverse) // != is bool XOR + direction := indexCol.Descending != reverse // != is bool XOR provided = append(provided, opt.MakeOrderingColumn(colID, direction)) } diff --git a/pkg/sql/opt/props/ordering_choice.go b/pkg/sql/opt/props/ordering_choice.go index 6e0090e325f4..f199f6c08d7d 100644 --- a/pkg/sql/opt/props/ordering_choice.go +++ b/pkg/sql/opt/props/ordering_choice.go @@ -189,7 +189,7 @@ func ParseOrdering(str string) opt.Ordering { panic(errors.AssertionFailedf("invalid ordering %s", str)) } for i := range prov.Columns { - if prov.Columns[i].Group.Len() != 1 { + if prov.Group(i).Len() != 1 { panic(errors.AssertionFailedf("invalid ordering %s", str)) } } @@ -247,7 +247,7 @@ func (oc *OrderingChoice) ToOrdering() opt.Ordering { func (oc *OrderingChoice) ColSet() opt.ColSet { var cs opt.ColSet for i := range oc.Columns { - cs.UnionWith(oc.Columns[i].Group) + cs.UnionWith(oc.Group(i)) } return cs } @@ -581,7 +581,7 @@ func (oc *OrderingChoice) SubsetOfCols(cs opt.ColSet) bool { return false } for i := range oc.Columns { - if !oc.Columns[i].Group.SubsetOf(cs) { + if !oc.Group(i).SubsetOf(cs) { return false } } @@ -603,7 +603,7 @@ func (oc *OrderingChoice) SubsetOfCols(cs opt.ColSet) bool { // func (oc *OrderingChoice) CanProjectCols(cs opt.ColSet) bool { for i := range oc.Columns { - if !oc.Columns[i].Group.Intersects(cs) { + if !oc.Group(i).Intersects(cs) { return false } } @@ -797,9 +797,9 @@ func (oc *OrderingChoice) RestrictToCols(cols opt.ColSet) { oc.Optional = oc.Optional.Intersection(cols) } for i := range oc.Columns { - if !oc.Columns[i].Group.SubsetOf(cols) { - oc.Columns[i].Group = oc.Columns[i].Group.Intersection(cols) - if oc.Columns[i].Group.Empty() { + if !oc.Group(i).SubsetOf(cols) { + oc.Columns[i].Group = oc.Group(i).Intersection(cols) + if oc.Group(i).Empty() { oc.Columns = oc.Columns[:i] break } @@ -849,22 +849,22 @@ func (oc OrderingChoice) PrefixIntersection( result.Columns = append(result.Columns, suffix...) return result, true case prefixHelper.empty() && len(oc.Columns) > 0 && len(suffix) > 0 && - oc.Columns[0].Group.Intersects(suffix[0].Group) && + oc.Group(0).Intersects(suffix[0].Group) && oc.Columns[0].Descending == suffix[0].Descending: // is empty, and and agree on the first column, so // emit that column, remove it from both, and loop. newCol := oc.Columns[0] - newCol.Group = oc.Columns[0].Group.Intersection(suffix[0].Group) + newCol.Group = oc.Group(0).Intersection(suffix[0].Group) result.Columns = append(result.Columns, newCol) oc.Columns = oc.Columns[1:] suffix = suffix[1:] - case len(oc.Columns) > 0 && prefixHelper.intersects(oc.Columns[0].Group): + case len(oc.Columns) > 0 && prefixHelper.intersects(oc.Group(0)): // contains the first column in , so emit it and remove it // from both. result.Columns = append(result.Columns, oc.Columns[0]) - prefixHelper.differenceWith(oc.Columns[0].Group) + prefixHelper.differenceWith(oc.Group(0)) oc.Columns = oc.Columns[1:] default: // If no rule applied, fail. @@ -897,6 +897,14 @@ func (oc *OrderingChoice) Equals(rhs *OrderingChoice) bool { return true } +// Group returns the group of this instance's column col. +func (oc *OrderingChoice) Group(col int) opt.ColSet { + if col < 0 || col >= len(oc.Columns) { + return opt.ColSet{} + } + return oc.Columns[col].Group +} + func (oc OrderingChoice) String() string { var buf bytes.Buffer oc.Format(&buf) diff --git a/pkg/sql/opt/xform/coster.go b/pkg/sql/opt/xform/coster.go index 8fe884ad6c11..1ee039bf3fe2 100644 --- a/pkg/sql/opt/xform/coster.go +++ b/pkg/sql/opt/xform/coster.go @@ -1205,9 +1205,10 @@ func (c *coster) computeGroupingCost(grouping memo.RelExpr, required *physical.R // If this is a streaming GroupBy with a limit hint, l, we only need to // process enough input rows to output l rows. - isStreaming := isStreamingAggregation(private, required) - if isStreaming && grouping.Op() == opt.GroupByOp && required.LimitHint > 0 { + streamingType := private.GroupingOrderType(&required.Ordering) + if (streamingType != memo.NoStreaming) && grouping.Op() == opt.GroupByOp && required.LimitHint > 0 { inputRowCount = streamingGroupByInputLimitHint(inputRowCount, outputRowCount, required.LimitHint) + outputRowCount = math.Min(outputRowCount, required.LimitHint) } // Cost per row depends on the number of grouping columns and the number of @@ -1219,7 +1220,7 @@ func (c *coster) computeGroupingCost(grouping memo.RelExpr, required *physical.R // // The cost is chosen so that it's always less than the cost to sort the // input. - if groupingColCount > 0 && !isStreaming { + if groupingColCount > 0 && streamingType != memo.Streaming { // Add the cost to build the hash table. cost += memo.Cost(inputRowCount) * cpuCostFactor @@ -1542,17 +1543,6 @@ func localityMatchScore(zone cat.Zone, locality roachpb.Locality) float64 { return (constraintScore*2 + leaseScore) / 3 } -// isStreamingAggregation returns true if the GroupingPrivate indicates that -// streaming aggregation will be performed during execution with the required -// physical properties. Currently, streaming aggregation is performed when all -// the grouping columns are ordered. The execution engine does not support -// streaming aggregation with partially ordered grouping columns. -func isStreamingAggregation(g *memo.GroupingPrivate, required *physical.Required) bool { - groupingColCount := g.GroupingCols.Len() - return groupingColCount > 0 && - groupingColCount == len(ordering.StreamingGroupingColOrdering(g, &required.Ordering)) -} - // streamingGroupByLimitHint calculates an appropriate limit hint for the input // to a streaming GroupBy expression. func streamingGroupByInputLimitHint( diff --git a/pkg/sql/opt/xform/groupby_funcs.go b/pkg/sql/opt/xform/groupby_funcs.go index 04a6907c726d..429379de6150 100644 --- a/pkg/sql/opt/xform/groupby_funcs.go +++ b/pkg/sql/opt/xform/groupby_funcs.go @@ -12,6 +12,7 @@ package xform import ( "github.com/cockroachdb/cockroach/pkg/sql/opt" + "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" "github.com/cockroachdb/cockroach/pkg/sql/opt/ordering" "github.com/cockroachdb/cockroach/pkg/sql/opt/props" @@ -197,7 +198,7 @@ func (c *CustomFuncs) GenerateStreamingGroupBy( } if intraIdx < len(intraOrd.Columns) && - intraOrd.Columns[intraIdx].Group.Contains(oCol) && + intraOrd.Group(intraIdx).Contains(oCol) && intraOrd.Columns[intraIdx].Descending == o[oIdx].Descending() { // Column matches the one in the ordering. intraIdx++ @@ -352,7 +353,7 @@ func (c *CustomFuncs) SplitGroupByScanIntoUnionScans( break } if len(intraOrd.Columns) > 0 && - intraOrd.Columns[0].Group.Contains(col) { + intraOrd.Group(0).Contains(col) { // Column matches the one in the ordering. keyPrefixLength = i break @@ -394,3 +395,84 @@ func (c *CustomFuncs) MakeGroupingPrivate( ErrorOnDup: errorText, } } + +// GenerateLimitedGroupByScans enumerates all non-inverted secondary indexes on +// the given Scan operator's table and generates an alternate Scan operator for +// each index that includes a partial set of needed columns specified in the +// ScanOpDef. An IndexJoin is constructed to add missing columns. A GroupBy and +// Limit are also constructed to make an equivalent expression for the memo. +// +// For cases where the Scan's secondary index covers all needed columns, see +// GenerateIndexScans, which does not construct an IndexJoin. +func (c *CustomFuncs) GenerateLimitedGroupByScans( + grp memo.RelExpr, + sp *memo.ScanPrivate, + aggs memo.AggregationsExpr, + gp *memo.GroupingPrivate, + limit opt.ScalarExpr, + required props.OrderingChoice, +) { + // If the required ordering and grouping columns do not share columns, then + // this optimization is not beneficial. + if !required.Any() && !required.Group(0).Intersects(gp.GroupingCols) && !required.Optional.Intersects(gp.GroupingCols) { + return + } + // Iterate over all non-inverted and non-partial secondary indexes. + var pkCols opt.ColSet + var iter scanIndexIter + iter.Init(c.e.evalCtx, c.e.f, c.e.mem, &c.im, sp, nil /* filters */, rejectPrimaryIndex|rejectInvertedIndexes) + iter.ForEach(func(index cat.Index, filters memo.FiltersExpr, indexCols opt.ColSet, isCovering bool, constProj memo.ProjectionsExpr) { + // The iterator only produces pseudo-partial indexes (the predicate is + // true) because no filters are passed to iter.Init to imply a partial + // index predicate. constProj is a projection of constant values based + // on a partial index predicate. It should always be empty because a + // pseudo-partial index cannot hold a column constant. If it is not, we + // panic to avoid performing a logically incorrect transformation. + if len(constProj) != 0 { + panic(errors.AssertionFailedf("expected constProj to be empty")) + } + + // If the secondary index includes the set of needed columns, then this + // case does not need a limited group by and will be covered in + // GenerateIndexScans. + if isCovering { + return + } + + // Calculate the PK columns once. + if pkCols.Empty() { + pkCols = c.PrimaryKeyCols(sp.Table) + } + + // If the first index column is not in the grouping columns, then there is + // no benefit to exploring this index. + if col := sp.Table.ColumnID(index.Column(0).Ordinal()); !gp.GroupingCols.Contains(col) { + return + } + + // If the index doesn't contain any of the required order columns, then + // there is no benefit to exploring this index. + if !required.Any() && !required.Group(0).Intersects(indexCols) { + return + } + + // Scan whatever columns we need which are available from the index. + newScanPrivate := *sp + newScanPrivate.Index = index.Ordinal() + newScanPrivate.Cols = indexCols.Intersection(sp.Cols) + // If the index is not covering, scan the needed index columns plus + // primary key columns. + newScanPrivate.Cols.UnionWith(pkCols) + input := c.e.f.ConstructScan(&newScanPrivate) + // Construct an IndexJoin operator that provides the columns missing from + // the index. + input = c.e.f.ConstructIndexJoin(input, &memo.IndexJoinPrivate{ + Table: sp.Table, + Cols: sp.Cols, + }) + // Reconstruct the GroupBy and Limit so the new expression in the memo is + // equivalent. + input = c.e.f.ConstructGroupBy(input, aggs, gp) + grp.Memo().AddLimitToGroup(&memo.LimitExpr{Limit: limit, Ordering: required, Input: input}, grp) + }) +} diff --git a/pkg/sql/opt/xform/limit_funcs.go b/pkg/sql/opt/xform/limit_funcs.go index 07541addb2a4..01ed5e63c9b5 100644 --- a/pkg/sql/opt/xform/limit_funcs.go +++ b/pkg/sql/opt/xform/limit_funcs.go @@ -195,7 +195,7 @@ func (c *CustomFuncs) SplitLimitedScanIntoUnionScans( } keyPrefixLength := cons.Columns.Count() for i := 0; i < cons.Columns.Count(); i++ { - if limitOrdering.Columns[0].Group.Contains(cons.Columns.Get(i).ID()) { + if limitOrdering.Group(0).Contains(cons.Columns.Get(i).ID()) { keyPrefixLength = i break } diff --git a/pkg/sql/opt/xform/physical_props.go b/pkg/sql/opt/xform/physical_props.go index abf73e952455..8e000a410943 100644 --- a/pkg/sql/opt/xform/physical_props.go +++ b/pkg/sql/opt/xform/physical_props.go @@ -134,7 +134,8 @@ func BuildChildPhysicalProps( // For streaming GroupBy expressions we can estimate the number of input // rows needed to produce LimitHint output rows. - if isStreamingAggregation(private, parentProps) { + streamingType := private.GroupingOrderType(&parentProps.Ordering) + if streamingType != memo.NoStreaming { if input, ok := parent.Child(nth).(memo.RelExpr); ok { inputRows := input.Relational().Stats.RowCount childProps.LimitHint = streamingGroupByInputLimitHint(inputRows, outputRows, parentProps.LimitHint) diff --git a/pkg/sql/opt/xform/rules/groupby.opt b/pkg/sql/opt/xform/rules/groupby.opt index 1f9afc7a5254..d1da81a12d15 100644 --- a/pkg/sql/opt/xform/rules/groupby.opt +++ b/pkg/sql/opt/xform/rules/groupby.opt @@ -339,3 +339,72 @@ (ErrorOnDup $private) ) ) + +# GenerateLimitedGroupByScans generates a set of Scan alternatives for +# each matching index on the scanned table, and an IndexJoin to supply columns +# missing from the index. This differs from GenerateIndexScans, which does not +# generate index joins for non-covering indexes. +# +# This rule is useful when we have a partially ordered GROUP BY column and a +# LIMIT that limits how many rows to aggregate, as in the following example: +# +# CREATE TABLE t (a INT, b INT, INDEX (a)) +# SELECT a, b, count(*) FROM t GROUP BY a, b LIMIT 10 +# +# Without this rule, the following expression with a full scan would be +# generated: +# limit +# ├── columns: a:1 b:2 count:6!null +# ├── group-by +# │ ├── columns: a:1 b:2 count_rows:6!null +# │ ├── grouping columns: a:1 b:2 +# │ ├── limit hint: 10.00 +# │ ├── scan t +# │ │ └── columns: a:1 b:2 +# │ └── aggregations +# │ └── count-rows [as=count_rows:6] +# └── 10 +# +# When GenerateLimitedGroupByScans is applied, we can explore the following +# expression: +# limit +# ├── columns: a:1 b:2 count:6!null +# ├── group-by +# │ ├── columns: a:1 b:2 count_rows:6!null +# │ ├── grouping columns: a:1 b:2 +# │ ├── limit hint: 10.00 +# │ ├── index-join t +# │ │ ├── columns: a:1 b:2 +# │ │ ├── ordering: +1 +# │ │ ├── limit hint: 10.00 +# │ │ └── scan t@secondary +# │ │ ├── columns: a:1 rowid:3!null +# │ │ ├── ordering: +1 +# │ │ └── limit hint: 10.00 +# │ └── aggregations +# │ └── count-rows [as=count_rows:6] +# └── 10 +# +# This can have better performance than the original expression, since this +# allows us to explore a group by with partially ordered grouping columns +# provided by the index and use an index join to supply the remaining grouping +# columns. Then we would not necessarily need a full scan on t due to the limit +# hint. +[GenerateLimitedGroupByScans, Explore] +(Limit + (GroupBy + (Scan $scanPrivate:* & (IsCanonicalScan $scanPrivate)) + $aggs:* + $groupbyPrivate:* & (IsCanonicalGroupBy $groupbyPrivate) + ) + $limitExpr:(Const $limit:*) & (IsPositiveInt $limit) + $ordering:* +) +=> +(GenerateLimitedGroupByScans + $scanPrivate + $aggs + $groupbyPrivate + $limitExpr + $ordering +) diff --git a/pkg/sql/opt/xform/scan_funcs.go b/pkg/sql/opt/xform/scan_funcs.go index 64bde1cdc4fd..ab870c075d17 100644 --- a/pkg/sql/opt/xform/scan_funcs.go +++ b/pkg/sql/opt/xform/scan_funcs.go @@ -34,7 +34,8 @@ import ( // up", when the Scan operator is wrapped by an operator that constrains // or limits scan output in some way (e.g. Select, Limit, InnerJoin). // Index joins are only lower cost when their input does not include all -// rows from the table. See ConstrainScans and LimitScans for cases where +// rows from the table. See GenerateConstrainedScans, +// GenerateLimitedScans, and GenerateLimitedGroupByScans for cases where // index joins are introduced into the memo. func (c *CustomFuncs) GenerateIndexScans(grp memo.RelExpr, scanPrivate *memo.ScanPrivate) { // Iterate over all non-inverted and non-partial secondary indexes. diff --git a/pkg/sql/opt/xform/testdata/coster/groupby b/pkg/sql/opt/xform/testdata/coster/groupby index 7cd98b22fa29..f198cbe91e7b 100644 --- a/pkg/sql/opt/xform/testdata/coster/groupby +++ b/pkg/sql/opt/xform/testdata/coster/groupby @@ -3,13 +3,17 @@ CREATE TABLE a (k INT PRIMARY KEY, i INT, s STRING, d DECIMAL NOT NULL) ---- exec-ddl -CREATE TABLE b (k INT PRIMARY KEY, a INT, b INT, c INT, INDEX (a, b)) +CREATE TABLE b (k INT PRIMARY KEY, a INT, b INT, c INT, INDEX (a, b), INDEX (a, b, c)) +---- + +exec-ddl +CREATE TABLE c (a INT, b INT, c INT, d INT, INDEX (a), INDEX (b, c, d)) ---- opt SELECT max(k), min(k), i, s FROM a GROUP BY i, s ---- -group-by +group-by (hash) ├── columns: max:7!null min:8!null i:2 s:3 ├── grouping columns: i:2 s:3 ├── stats: [rows=1000, distinct(2,3)=1000, null(2,3)=0.1] @@ -31,7 +35,7 @@ group-by opt SELECT a, count(*) FROM b GROUP BY a ---- -group-by +group-by (streaming) ├── columns: a:2 count:7!null ├── grouping columns: a:2 ├── internal-ordering: +2 @@ -50,7 +54,7 @@ group-by opt SELECT a, b, count(*) FROM b GROUP BY a, b ---- -group-by +group-by (streaming) ├── columns: a:2 b:3 count:7!null ├── grouping columns: a:2 b:3 ├── internal-ordering: +2,+3 @@ -77,7 +81,7 @@ limit ├── cost: 121.16 ├── key: (2) ├── fd: (2)-->(7) - ├── group-by + ├── group-by (streaming) │ ├── columns: a:2 count_rows:7!null │ ├── grouping columns: a:2 │ ├── internal-ordering: +2 @@ -106,7 +110,7 @@ limit ├── cost: 34.96 ├── key: (2,3) ├── fd: (2,3)-->(7) - ├── group-by + ├── group-by (streaming) │ ├── columns: a:2 b:3 count_rows:7!null │ ├── grouping columns: a:2 b:3 │ ├── internal-ordering: +2,+3 @@ -124,3 +128,204 @@ limit │ └── aggregations │ └── count-rows [as=count_rows:7] └── 10 + +# Partially ordered group by with a limit hint. +opt +SELECT a, c, count(*) FROM c GROUP BY a, c LIMIT 10 +---- +limit + ├── columns: a:1 c:3 count:8!null + ├── cardinality: [0 - 10] + ├── stats: [rows=10] + ├── cost: 641.98 + ├── key: (1,3) + ├── fd: (1,3)-->(8) + ├── group-by (partial streaming) + │ ├── columns: a:1 c:3 count_rows:8!null + │ ├── grouping columns: a:1 c:3 + │ ├── internal-ordering: +1 + │ ├── stats: [rows=1000, distinct(1,3)=1000, null(1,3)=0.1] + │ ├── cost: 641.87 + │ ├── key: (1,3) + │ ├── fd: (1,3)-->(8) + │ ├── limit hint: 10.00 + │ ├── index-join c + │ │ ├── columns: a:1 c:3 + │ │ ├── stats: [rows=1000, distinct(1,3)=1000, null(1,3)=0.1] + │ │ ├── cost: 631.44 + │ │ ├── ordering: +1 + │ │ ├── limit hint: 10.00 + │ │ └── scan c@c_a_idx + │ │ ├── columns: a:1 rowid:5!null + │ │ ├── stats: [rows=1000, distinct(1)=100, null(1)=10] + │ │ ├── cost: 24.42 + │ │ ├── key: (5) + │ │ ├── fd: (5)-->(1) + │ │ ├── ordering: +1 + │ │ └── limit hint: 10.00 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 + +opt +SELECT b, d, count(*) FROM c GROUP BY b, d LIMIT 10 +---- +limit + ├── columns: b:2 d:4 count:8!null + ├── cardinality: [0 - 10] + ├── stats: [rows=10] + ├── cost: 35.16 + ├── key: (2,4) + ├── fd: (2,4)-->(8) + ├── group-by (partial streaming) + │ ├── columns: b:2 d:4 count_rows:8!null + │ ├── grouping columns: b:2 d:4 + │ ├── internal-ordering: +2 + │ ├── stats: [rows=1000, distinct(2,4)=1000, null(2,4)=0.1] + │ ├── cost: 35.05 + │ ├── key: (2,4) + │ ├── fd: (2,4)-->(8) + │ ├── limit hint: 10.00 + │ ├── scan c@c_b_c_d_idx + │ │ ├── columns: b:2 d:4 + │ │ ├── stats: [rows=1000, distinct(2,4)=1000, null(2,4)=0.1] + │ │ ├── cost: 24.62 + │ │ ├── ordering: +2 + │ │ └── limit hint: 10.00 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 + +opt +SELECT b, a, count(*) FROM c GROUP BY b, a LIMIT 10 +---- +limit + ├── columns: b:2 a:1 count:8!null + ├── cardinality: [0 - 10] + ├── stats: [rows=10] + ├── cost: 641.98 + ├── key: (1,2) + ├── fd: (1,2)-->(8) + ├── group-by (partial streaming) + │ ├── columns: a:1 b:2 count_rows:8!null + │ ├── grouping columns: a:1 b:2 + │ ├── internal-ordering: +1 + │ ├── stats: [rows=1000, distinct(1,2)=1000, null(1,2)=0.1] + │ ├── cost: 641.87 + │ ├── key: (1,2) + │ ├── fd: (1,2)-->(8) + │ ├── limit hint: 10.00 + │ ├── index-join c + │ │ ├── columns: a:1 b:2 + │ │ ├── stats: [rows=1000, distinct(1,2)=1000, null(1,2)=0.1] + │ │ ├── cost: 631.44 + │ │ ├── ordering: +1 + │ │ ├── limit hint: 10.00 + │ │ └── scan c@c_a_idx + │ │ ├── columns: a:1 rowid:5!null + │ │ ├── stats: [rows=1000, distinct(1)=100, null(1)=10] + │ │ ├── cost: 24.42 + │ │ ├── key: (5) + │ │ ├── fd: (5)-->(1) + │ │ ├── ordering: +1 + │ │ └── limit hint: 10.00 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 + +opt +SELECT b, c, a, count(*) FROM c GROUP BY b, c, a LIMIT 10 +---- +limit + ├── columns: b:2 c:3 a:1 count:8!null + ├── cardinality: [0 - 10] + ├── stats: [rows=10] + ├── cost: 642.38 + ├── key: (1-3) + ├── fd: (1-3)-->(8) + ├── group-by (partial streaming) + │ ├── columns: a:1 b:2 c:3 count_rows:8!null + │ ├── grouping columns: a:1 b:2 c:3 + │ ├── internal-ordering: +2,+3 + │ ├── stats: [rows=1000, distinct(1-3)=1000, null(1-3)=0.001] + │ ├── cost: 642.27 + │ ├── key: (1-3) + │ ├── fd: (1-3)-->(8) + │ ├── limit hint: 10.00 + │ ├── index-join c + │ │ ├── columns: a:1 b:2 c:3 + │ │ ├── stats: [rows=1000, distinct(1-3)=1000, null(1-3)=0.001] + │ │ ├── cost: 631.74 + │ │ ├── ordering: +2,+3 + │ │ ├── limit hint: 10.00 + │ │ └── scan c@c_b_c_d_idx + │ │ ├── columns: b:2 c:3 rowid:5!null + │ │ ├── stats: [rows=1000, distinct(2,3)=1000, null(2,3)=0.1] + │ │ ├── cost: 24.72 + │ │ ├── key: (5) + │ │ ├── fd: (5)-->(2,3) + │ │ ├── ordering: +2,+3 + │ │ └── limit hint: 10.00 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 + +exec-ddl +CREATE TABLE f ( + filename + STRING PRIMARY KEY, + file_id + UUID DEFAULT gen_random_uuid() NOT NULL UNIQUE, + file_size + INT8 NOT NULL, + username + STRING NOT NULL, + upload_time + TIMESTAMP DEFAULT now() +) +---- + +exec-ddl +CREATE TABLE p (file_id UUID, byte_offset INT8, payload BYTES, PRIMARY KEY (file_id, byte_offset)) +---- + +# Non-scalar group-by with no grouping columns should be streaming: #71768 +opt +SELECT f.file_id, sum_int(length(p.payload)) FROM f f LEFT OUTER JOIN p p ON p.file_id = f.file_id WHERE f.filename = 'abc' GROUP BY f.file_id +---- +group-by (streaming) + ├── columns: file_id:2!null sum_int:14 + ├── cardinality: [0 - 1] + ├── immutable + ├── stats: [rows=1] + ├── cost: 46.15 + ├── key: () + ├── fd: ()-->(2,14) + ├── project + │ ├── columns: column13:13 f.file_id:2!null + │ ├── immutable + │ ├── stats: [rows=10] + │ ├── cost: 45.92 + │ ├── fd: ()-->(2) + │ ├── left-join (lookup p) + │ │ ├── columns: filename:1!null f.file_id:2!null p.file_id:8 payload:10 + │ │ ├── key columns: [2] = [8] + │ │ ├── stats: [rows=10, distinct(8)=1, null(8)=0] + │ │ ├── cost: 45.7 + │ │ ├── fd: ()-->(1,2,8) + │ │ ├── scan f + │ │ │ ├── columns: filename:1!null f.file_id:2!null + │ │ │ ├── constraint: /1: [/'abc' - /'abc'] + │ │ │ ├── cardinality: [0 - 1] + │ │ │ ├── stats: [rows=1, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0] + │ │ │ ├── cost: 5.08 + │ │ │ ├── key: () + │ │ │ └── fd: ()-->(1,2) + │ │ └── filters (true) + │ └── projections + │ └── length(payload:10) [as=column13:13, outer=(10), immutable] + └── aggregations + ├── sum-int [as=sum_int:14, outer=(13)] + │ └── column13:13 + └── const-agg [as=f.file_id:2, outer=(2)] + └── f.file_id:2 diff --git a/pkg/sql/opt/xform/testdata/external/customer b/pkg/sql/opt/xform/testdata/external/customer index 3d7fbdb89b34..306c11c0c6d7 100644 --- a/pkg/sql/opt/xform/testdata/external/customer +++ b/pkg/sql/opt/xform/testdata/external/customer @@ -283,7 +283,7 @@ CREATE TABLE vehicles ( opt select v.owner_id, count(*) from rides r, vehicles v where v.id = r.vehicle_id group by v.owner_id ---- -group-by +group-by (hash) ├── columns: owner_id:14 count:20!null ├── grouping columns: owner_id:14 ├── key: (14) @@ -327,7 +327,7 @@ CREATE TABLE data ( opt SELECT id, sum(value) FROM data GROUP BY id ---- -group-by +group-by (streaming) ├── columns: id:1 sum:16 ├── grouping columns: id:1 ├── internal-ordering: +1 diff --git a/pkg/sql/opt/xform/testdata/external/hibernate b/pkg/sql/opt/xform/testdata/external/hibernate index 91e4911d8f89..bb85ba792ec5 100644 --- a/pkg/sql/opt/xform/testdata/external/hibernate +++ b/pkg/sql/opt/xform/testdata/external/hibernate @@ -395,7 +395,7 @@ project ├── has-placeholder ├── key: (1,2) ├── fd: (1,2)-->(3-6,17), (2)==(17), (17)==(2) - ├── group-by + ├── group-by (hash) │ ├── columns: defaultaud0_.id:1!null defaultaud0_.rev:2!null defaultaud0_.revtype:3!null defaultaud0_.created_on:4 defaultaud0_.firstname:5 defaultaud0_.lastname:6 max:17!null │ ├── grouping columns: defaultaud0_.id:1!null defaultaud0_.rev:2!null │ ├── has-placeholder @@ -510,7 +510,7 @@ sort ├── has-placeholder ├── key: (1,2) ├── fd: (1,2)-->(3-8,21), (2)==(21), (21)==(2) - ├── group-by + ├── group-by (hash) │ ├── columns: queryaudit0_.id:1!null queryaudit0_.rev:2!null queryaudit0_.revtype:3!null queryaudit0_.revend:4 queryaudit0_.created_on:5 queryaudit0_.firstname:6 queryaudit0_.lastname:7 queryaudit0_.address_id:8 max:21!null │ ├── grouping columns: queryaudit0_.id:1!null queryaudit0_.rev:2!null │ ├── has-placeholder @@ -740,7 +740,7 @@ project ├── has-placeholder ├── key: (1) ├── fd: ()-->(14), (1)-->(2-4) - ├── group-by + ├── group-by (hash) │ ├── columns: phone0_.id:1!null phone_number:2 phone_type:3 person_id:4 max:14!null │ ├── grouping columns: phone0_.id:1!null │ ├── key: (1) @@ -800,7 +800,7 @@ project ├── columns: person0_.id:1!null address:2 createdon:3 name:4 nickname:5 version:6!null count:16!null ├── key: (1) ├── fd: ()-->(16), (1)-->(2-6) - ├── group-by + ├── group-by (hash) │ ├── columns: person0_.id:1!null address:2 createdon:3 name:4 nickname:5 version:6!null count:16!null │ ├── grouping columns: person0_.id:1!null │ ├── key: (1) @@ -861,7 +861,7 @@ project ├── has-placeholder ├── key: (1) ├── fd: ()-->(14), (1)-->(2-4) - ├── group-by + ├── group-by (hash) │ ├── columns: phone0_.id:1!null phone_number:2 phone_type:3 person_id:4 min:14!null │ ├── grouping columns: phone0_.id:1!null │ ├── key: (1) @@ -921,7 +921,7 @@ project ├── columns: person0_.id:1!null address:2 createdon:3 name:4 nickname:5 version:6!null max:16!null ├── key: (1) ├── fd: ()-->(16), (1)-->(2-6) - ├── group-by + ├── group-by (hash) │ ├── columns: person0_.id:1!null address:2 createdon:3 name:4 nickname:5 version:6!null max:16!null │ ├── grouping columns: person0_.id:1!null │ ├── key: (1) @@ -1215,7 +1215,7 @@ project ├── columns: person0_.id:1!null address:2 createdon:3 name:4 nickname:5 version:6!null phones2_.id:9!null phones2_.order_id:13!null max:23!null ├── key: (9) ├── fd: (1)-->(2-6), (9)-->(1-6,13,23), (13)==(23), (23)==(13) - ├── group-by + ├── group-by (hash) │ ├── columns: person0_.id:1!null address:2 createdon:3 name:4 nickname:5 version:6!null phones2_.id:9!null phones2_.order_id:13 max:23!null │ ├── grouping columns: phones2_.id:9!null │ ├── key: (9) @@ -1501,7 +1501,7 @@ project ├── columns: id:1!null email:2 currentproject_id:3 count:11!null ├── key: (1) ├── fd: ()-->(11), (1)-->(2,3) - ├── group-by + ├── group-by (hash) │ ├── columns: id:1!null email:2 currentproject_id:3 count:11!null │ ├── grouping columns: id:1!null │ ├── key: (1) @@ -1874,7 +1874,7 @@ project ├── has-placeholder ├── key: (1) ├── fd: ()-->(4), (1)-->(2,3,13) - ├── group-by + ├── group-by (hash) │ ├── columns: bids0_.id:1!null amount:2 createddatetime:3 auctionid:4!null true_agg:15 │ ├── grouping columns: bids0_.id:1!null │ ├── has-placeholder @@ -1999,7 +1999,7 @@ project ├── immutable ├── key: (8) ├── fd: ()-->(1-3,6,7), (8)-->(9,26) - ├── group-by + ├── group-by (hash) │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 sum:25 │ ├── grouping columns: lineitems1_.productid:8 │ ├── immutable @@ -2126,7 +2126,7 @@ project ├── immutable ├── key: (1,6,7,11-13) ├── fd: (1)-->(2,3), (6,7)-->(8), (11-13)-->(14), (17)-->(18-20), (1,6,7,11-13)-->(2,3,8,14,17-20,44,45) - ├── group-by + ├── group-by (hash) │ ├── columns: customer0_.customerid:1!null name:2!null address:3!null orders1_.customerid:6 orders1_.ordernumber:7 orderdate:8 lineitems2_.customerid:11 lineitems2_.ordernumber:12 lineitems2_.productid:13 lineitems2_.quantity:14 product3_.productid:17 product3_.description:18 product3_.cost:19 product3_.numberavailable:20 sum:36 sum:43 │ ├── grouping columns: customer0_.customerid:1!null orders1_.customerid:6 orders1_.ordernumber:7 lineitems2_.customerid:11 lineitems2_.ordernumber:12 lineitems2_.productid:13 │ ├── immutable @@ -2136,7 +2136,7 @@ project │ │ ├── columns: customer0_.customerid:1!null name:2!null address:3!null orders1_.customerid:6 orders1_.ordernumber:7 orderdate:8 lineitems2_.customerid:11 lineitems2_.ordernumber:12 lineitems2_.productid:13 lineitems2_.quantity:14 product3_.productid:17 product3_.description:18 product3_.cost:19 product3_.numberavailable:20 sum:36 li.productid:39 li.quantity:40 │ │ ├── immutable │ │ ├── fd: (1)-->(2,3), (6,7)-->(8), (11-13)-->(14), (17)-->(18-20), (1,6,7,11-13)-->(2,3,8,14,17-20,36) - │ │ ├── group-by + │ │ ├── group-by (hash) │ │ │ ├── columns: customer0_.customerid:1!null name:2!null address:3!null orders1_.customerid:6 orders1_.ordernumber:7 orderdate:8 lineitems2_.customerid:11 lineitems2_.ordernumber:12 lineitems2_.productid:13 lineitems2_.quantity:14 product3_.productid:17 product3_.description:18 product3_.cost:19 product3_.numberavailable:20 sum:36 │ │ │ ├── grouping columns: customer0_.customerid:1!null orders1_.customerid:6 orders1_.ordernumber:7 lineitems2_.customerid:11 lineitems2_.ordernumber:12 lineitems2_.productid:13 │ │ │ ├── immutable @@ -2295,7 +2295,7 @@ project ├── immutable ├── key: (8) ├── fd: ()-->(1-3,6,7), (8)-->(9,26) - ├── group-by + ├── group-by (hash) │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null lineitems1_.customerid:6 lineitems1_.ordernumber:7 lineitems1_.productid:8 lineitems1_.quantity:9 sum:25 │ ├── grouping columns: lineitems1_.productid:8 │ ├── immutable @@ -2389,7 +2389,7 @@ project ├── immutable ├── key: (1,2) ├── fd: (1,2)-->(3,20) - ├── group-by + ├── group-by (hash) │ ├── columns: order0_.customerid:1!null order0_.ordernumber:2!null orderdate:3!null sum:19 │ ├── grouping columns: order0_.customerid:1!null order0_.ordernumber:2!null │ ├── immutable @@ -2491,7 +2491,7 @@ WHERE ) ); ---- -group-by +group-by (hash) ├── columns: studenti1_26_0_:1!null name2_26_0_:2!null address_3_26_0_:3 address_4_26_0_:4 preferre5_26_0_:5!null ├── grouping columns: this_.studentid:1!null ├── key: (1) @@ -2500,7 +2500,7 @@ group-by │ ├── columns: this_.studentid:1!null name:2!null address_city:3 address_state:4 preferredcoursecode:5!null enrolment_.studentid:8!null enrolment_.coursecode:9!null enrolment_.year:11!null max:20!null │ ├── key: (1,8,9) │ ├── fd: (1)-->(2-5), (8,9)-->(11), (1,8,9)-->(2-5,11,20), (11)==(20), (20)==(11) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: this_.studentid:1!null name:2!null address_city:3 address_state:4 preferredcoursecode:5!null enrolment_.studentid:8!null enrolment_.coursecode:9!null enrolment_.year:11!null max:20!null │ │ ├── grouping columns: this_.studentid:1!null enrolment_.studentid:8!null enrolment_.coursecode:9!null │ │ ├── key: (1,8,9) diff --git a/pkg/sql/opt/xform/testdata/external/liquibase b/pkg/sql/opt/xform/testdata/external/liquibase index 982d3d2e01fc..a9881318772b 100644 --- a/pkg/sql/opt/xform/testdata/external/liquibase +++ b/pkg/sql/opt/xform/testdata/external/liquibase @@ -164,7 +164,7 @@ project ├── columns: oid:1!null schemaname:31!null tablename:2!null relacl:26 tableowner:155 description:156 relkind:17!null cluster:106 hasoids:20!null hasindexes:13!null hasrules:22!null tablespace:37 param:27 hastriggers:23!null unlogged:15!null ftoptions:136 srvname:140 reltuples:10!null inhtable:157!null inhschemaname:79 inhtablename:50 ├── stable ├── fd: ()-->(31), (1)-->(2,10,13,15,17,20,22,23,26,27,37,155,156), (2)-->(1,10,13,15,17,20,22,23,26,27,37,155,156) - ├── group-by + ├── group-by (hash) │ ├── columns: c.oid:1!null c.relname:2!null c.relowner:5!null c.reltuples:10!null c.relhasindex:13!null c.relpersistence:15!null c.relkind:17!null c.relhasoids:20!null c.relhasrules:22!null c.relhastriggers:23!null c.relacl:26 c.reloptions:27 n.nspname:31!null spcname:37 c2.relname:50 n2.nspname:79 ci.relname:106 ftoptions:136 srvname:140 count_rows:154!null rownum:158!null │ ├── grouping columns: rownum:158!null │ ├── key: (158) diff --git a/pkg/sql/opt/xform/testdata/external/navicat b/pkg/sql/opt/xform/testdata/external/navicat index c5b556dfe368..2fdef7864097 100644 --- a/pkg/sql/opt/xform/testdata/external/navicat +++ b/pkg/sql/opt/xform/testdata/external/navicat @@ -168,7 +168,7 @@ sort ├── columns: tableowner:155 description:156 inhtable:157!null c.oid:1!null c.relname:2!null c.reltuples:10!null c.relhasindex:13!null c.relpersistence:15!null c.relkind:17!null c.relhasoids:20!null c.relhasrules:22!null c.relhastriggers:23!null c.relacl:26 c.reloptions:27 n.nspname:31!null spcname:37 c2.relname:50 n2.nspname:79 ci.relname:106 ftoptions:136 srvname:140 ├── stable ├── fd: ()-->(31), (1)-->(2,10,13,15,17,20,22,23,26,27,37,155,156), (2)-->(1,10,13,15,17,20,22,23,26,27,37,155,156) - ├── group-by + ├── group-by (hash) │ ├── columns: c.oid:1!null c.relname:2!null c.relowner:5!null c.reltuples:10!null c.relhasindex:13!null c.relpersistence:15!null c.relkind:17!null c.relhasoids:20!null c.relhasrules:22!null c.relhastriggers:23!null c.relacl:26 c.reloptions:27 n.nspname:31!null spcname:37 c2.relname:50 n2.nspname:79 ci.relname:106 ftoptions:136 srvname:140 count_rows:154!null rownum:158!null │ ├── grouping columns: rownum:158!null │ ├── key: (158) diff --git a/pkg/sql/opt/xform/testdata/external/nova b/pkg/sql/opt/xform/testdata/external/nova index b8d964df75c8..af01738cca81 100644 --- a/pkg/sql/opt/xform/testdata/external/nova +++ b/pkg/sql/opt/xform/testdata/external/nova @@ -193,7 +193,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-12,14,15,27) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -365,7 +365,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: ()-->(11), (1)-->(2-10,12,14,15,38), (7)-->(1-6,8-10,12,14,15), (2)-->(1,3-10,12,14,15) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11!null is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:38 │ │ │ │ ├── grouping columns: flavors.id:1!null │ │ │ │ ├── internal-ordering: +1 opt(11) @@ -386,7 +386,7 @@ sort │ │ │ │ │ │ ├── key: (1) │ │ │ │ │ │ ├── fd: ()-->(11), (1)-->(2-10,12,14,15,35), (7)-->(1-6,8-10,12,14,15), (2)-->(1,3-10,12,14,15) │ │ │ │ │ │ ├── ordering: +1 opt(11) [actual: +1] - │ │ │ │ │ │ ├── group-by + │ │ │ │ │ │ ├── group-by (streaming) │ │ │ │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11!null is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:35 │ │ │ │ │ │ │ ├── grouping columns: flavors.id:1!null │ │ │ │ │ │ │ ├── has-placeholder @@ -623,7 +623,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: ()-->(13), (1)-->(2-12,14-16,30), (7,13)~~>(1-6,8-12,14-16), (2,13)~~>(1,3-12,14-16) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ ├── grouping columns: instance_types.id:1!null │ │ │ │ ├── internal-ordering: +1 opt(13) @@ -785,7 +785,7 @@ sort │ ├── has-placeholder │ ├── key: (1) │ ├── fd: ()-->(13), (1)-->(2-12,14-16,40), (7,13)~~>(1-6,8-12,14-16), (2,13)~~>(1,3-12,14-16) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:40 │ │ ├── grouping columns: instance_types.id:1!null │ │ ├── internal-ordering: +1 opt(13) @@ -977,7 +977,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-16,30) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: instance_types.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -1157,7 +1157,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-16,30) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -1322,7 +1322,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-12,14,15,27) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -1479,7 +1479,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-12,14,15,27) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -1648,7 +1648,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: (1)-->(2-12,14,15,27), (7)-->(1-6,8-12,14,15), (2)-->(1,3-12,14,15) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ │ ├── grouping columns: flavors.id:1!null │ │ │ │ ├── internal-ordering: +1 @@ -1833,7 +1833,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: ()-->(13), (1)-->(2-12,14-16,30), (7,13)~~>(1-6,8-12,14-16), (2,13)~~>(1,3-12,14-16) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ ├── grouping columns: instance_types.id:1!null │ │ │ │ ├── internal-ordering: +1 opt(13) @@ -2027,7 +2027,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-16,30) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -2167,7 +2167,7 @@ sort │ ├── has-placeholder │ ├── key: (1) │ ├── fd: (1)-->(2-12,14,15,35), (7)-->(1-6,8-12,14,15), (2)-->(1,3-12,14,15) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:35 │ │ ├── grouping columns: flavors.id:1!null │ │ ├── internal-ordering: +1 @@ -2359,7 +2359,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: ()-->(13), (1)-->(2-12,14-16,30), (7)-->(1-6,8-12,14-16), (2,13)~~>(1,3-12,14-16) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ ├── grouping columns: instance_types.id:1!null │ │ │ │ ├── internal-ordering: +1 opt(13) @@ -2540,7 +2540,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: (1)-->(2-12,14,15,27), (7)-->(1-6,8-12,14,15), (2)-->(1,3-12,14,15) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ │ ├── grouping columns: flavors.id:1!null │ │ │ │ ├── internal-ordering: +1 @@ -2739,7 +2739,7 @@ sort │ │ │ ├── has-placeholder │ │ │ ├── key: (1) │ │ │ ├── fd: ()-->(11,13), (1)-->(2-10,12,14-16,43), (7,13)~~>(1-6,8-10,12,14-16), (2,13)~~>(1,3-10,12,14-16) - │ │ │ ├── group-by + │ │ │ ├── group-by (streaming) │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11!null is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:43 │ │ │ │ ├── grouping columns: instance_types.id:1!null │ │ │ │ ├── internal-ordering: +1 opt(11,13) @@ -2760,7 +2760,7 @@ sort │ │ │ │ │ │ ├── key: (1) │ │ │ │ │ │ ├── fd: ()-->(11,13), (1)-->(2-10,12,14-16,40), (7,13)~~>(1-6,8-10,12,14-16), (2,13)~~>(1,3-10,12,14-16) │ │ │ │ │ │ ├── ordering: +1 opt(11,13) [actual: +1] - │ │ │ │ │ │ ├── group-by + │ │ │ │ │ │ ├── group-by (streaming) │ │ │ │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7 swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11!null is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:40 │ │ │ │ │ │ │ ├── grouping columns: instance_types.id:1!null │ │ │ │ │ │ │ ├── has-placeholder @@ -3005,7 +3005,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-16,30) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: instance_types.id:1!null name:2 memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 instance_types.deleted:13!null instance_types.deleted_at:14 instance_types.created_at:15 instance_types.updated_at:16 true_agg:30 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -3176,7 +3176,7 @@ project │ │ │ │ ├── has-placeholder │ │ │ │ ├── key: () │ │ │ │ ├── fd: ()-->(1-12,14,15,27) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ │ │ ├── cardinality: [0 - 1] │ │ │ │ │ ├── has-placeholder @@ -3334,7 +3334,7 @@ sort │ │ ├── has-placeholder │ │ ├── key: (1) │ │ ├── fd: (1)-->(2-15,27), (7)-->(1-6,8-15), (2)-->(1,3-15) - │ │ ├── group-by + │ │ ├── group-by (streaming) │ │ │ ├── columns: flavors.id:1!null name:2!null memory_mb:3!null vcpus:4!null root_gb:5 ephemeral_gb:6 flavorid:7!null swap:8!null rxtx_factor:9 vcpu_weight:10 disabled:11 is_public:12 description:13 flavors.created_at:14 flavors.updated_at:15 true_agg:27 │ │ │ ├── grouping columns: flavors.id:1!null │ │ │ ├── internal-ordering: +1 diff --git a/pkg/sql/opt/xform/testdata/external/postgis-tutorial b/pkg/sql/opt/xform/testdata/external/postgis-tutorial index fe53a3233158..4ed4a4deddf1 100644 --- a/pkg/sql/opt/xform/testdata/external/postgis-tutorial +++ b/pkg/sql/opt/xform/testdata/external/postgis-tutorial @@ -236,7 +236,7 @@ sort ├── immutable ├── key: (3) ├── fd: (3)-->(19,22,23) - ├── group-by + ├── group-by (hash) │ ├── columns: name:3 sum:19 sum:20 sum:21 │ ├── grouping columns: name:3 │ ├── immutable @@ -349,7 +349,7 @@ sort ├── immutable ├── key: (31) ├── fd: (31)-->(36,38,39) - ├── group-by + ├── group-by (hash) │ ├── columns: route:31 sum:35 sum:36 sum:37 │ ├── grouping columns: route:31 │ ├── immutable @@ -510,7 +510,7 @@ sort └── project ├── columns: popn_per_sqkm:20 name:15!null ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: name:15!null n.geom:16!null sum:19 │ ├── grouping columns: name:15!null n.geom:16!null │ ├── immutable diff --git a/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx b/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx index f527e0978845..a22e322b5403 100644 --- a/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx +++ b/pkg/sql/opt/xform/testdata/external/postgis-tutorial-idx @@ -312,7 +312,7 @@ sort ├── immutable ├── key: (3) ├── fd: (3)-->(21,24,25) - ├── group-by + ├── group-by (hash) │ ├── columns: name:3 sum:21 sum:22 sum:23 │ ├── grouping columns: name:3 │ ├── immutable @@ -437,7 +437,7 @@ sort ├── immutable ├── key: (33) ├── fd: (33)-->(38,40,41) - ├── group-by + ├── group-by (hash) │ ├── columns: route:33 sum:37 sum:38 sum:39 │ ├── grouping columns: route:33 │ ├── immutable @@ -621,7 +621,7 @@ sort └── project ├── columns: popn_per_sqkm:22 name:16!null ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: name:16!null n.geom:17!null sum:21 │ ├── grouping columns: name:16!null n.geom:17!null │ ├── immutable diff --git a/pkg/sql/opt/xform/testdata/external/tpcc b/pkg/sql/opt/xform/testdata/external/tpcc index fb962b8636af..923f53368df7 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc +++ b/pkg/sql/opt/xform/testdata/external/tpcc @@ -1101,7 +1101,7 @@ scalar-group-by │ │ ├── key: (1) │ │ ├── fd: (1)-->(9) │ │ └── ordering: +1 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: d_w_id:13!null sum:25 │ │ ├── grouping columns: d_w_id:13!null │ │ ├── key: (13) @@ -1135,7 +1135,7 @@ FROM new_order GROUP BY no_d_id, no_w_id ORDER BY no_w_id, no_d_id ---- -group-by +group-by (streaming) ├── columns: max:6!null [hidden: no_d_id:2!null no_w_id:3!null] ├── grouping columns: no_d_id:2!null no_w_id:3!null ├── key: (2,3) @@ -1155,7 +1155,7 @@ FROM "order" GROUP BY o_d_id, o_w_id ORDER BY o_w_id, o_d_id ---- -group-by +group-by (streaming) ├── columns: max:11!null [hidden: o_d_id:2!null o_w_id:3!null] ├── grouping columns: o_d_id:2!null o_w_id:3!null ├── key: (2,3) @@ -1190,7 +1190,7 @@ scalar-group-by │ ├── immutable │ ├── key: (2,3) │ ├── fd: (2,3)-->(6-8) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: no_d_id:2!null no_w_id:3!null max:6!null min:7!null count_rows:8!null │ │ ├── grouping columns: no_d_id:2!null no_w_id:3!null │ │ ├── internal-ordering: +3,+2 @@ -1217,7 +1217,7 @@ FROM "order" GROUP BY o_w_id, o_d_id ORDER BY o_w_id, o_d_id ---- -group-by +group-by (streaming) ├── columns: sum:11 [hidden: o_d_id:2!null o_w_id:3!null] ├── grouping columns: o_d_id:2!null o_w_id:3!null ├── key: (2,3) @@ -1236,7 +1236,7 @@ FROM order_line GROUP BY ol_w_id, ol_d_id ORDER BY ol_w_id, ol_d_id ---- -group-by +group-by (streaming) ├── columns: count:13!null [hidden: ol_d_id:2!null ol_w_id:3!null] ├── grouping columns: ol_d_id:2!null ol_w_id:3!null ├── key: (2,3) @@ -1328,7 +1328,7 @@ except-all │ ├── key: (1-3) │ ├── fd: (1-3)-->(7) │ └── ordering: +3,+2,-1 - └── group-by + └── group-by (streaming) ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null count_rows:23!null ├── grouping columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ├── key: (11-13) @@ -1361,7 +1361,7 @@ except-all ├── internal-ordering: +3,+2,-1,+13 ├── key: (1-3) ├── fd: (1-3)-->(13) - ├── group-by + ├── group-by (streaming) │ ├── columns: ol_o_id:1!null ol_d_id:2!null ol_w_id:3!null count_rows:13!null │ ├── grouping columns: ol_o_id:1!null ol_d_id:2!null ol_w_id:3!null │ ├── key: (1-3) diff --git a/pkg/sql/opt/xform/testdata/external/tpcc-later-stats b/pkg/sql/opt/xform/testdata/external/tpcc-later-stats index 67aea57d269a..92ea17f63b95 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc-later-stats +++ b/pkg/sql/opt/xform/testdata/external/tpcc-later-stats @@ -1103,7 +1103,7 @@ scalar-group-by │ │ ├── key: (1) │ │ ├── fd: (1)-->(9) │ │ └── ordering: +1 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: d_w_id:13!null sum:25 │ │ ├── grouping columns: d_w_id:13!null │ │ ├── key: (13) @@ -1137,7 +1137,7 @@ FROM new_order GROUP BY no_d_id, no_w_id ORDER BY no_w_id, no_d_id ---- -group-by +group-by (streaming) ├── columns: max:6!null [hidden: no_d_id:2!null no_w_id:3!null] ├── grouping columns: no_d_id:2!null no_w_id:3!null ├── key: (2,3) @@ -1157,7 +1157,7 @@ FROM "order" GROUP BY o_d_id, o_w_id ORDER BY o_w_id, o_d_id ---- -group-by +group-by (streaming) ├── columns: max:11!null [hidden: o_d_id:2!null o_w_id:3!null] ├── grouping columns: o_d_id:2!null o_w_id:3!null ├── key: (2,3) @@ -1192,7 +1192,7 @@ scalar-group-by │ ├── immutable │ ├── key: (2,3) │ ├── fd: (2,3)-->(6-8) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: no_d_id:2!null no_w_id:3!null max:6!null min:7!null count_rows:8!null │ │ ├── grouping columns: no_d_id:2!null no_w_id:3!null │ │ ├── internal-ordering: +3,+2 @@ -1219,7 +1219,7 @@ FROM "order" GROUP BY o_w_id, o_d_id ORDER BY o_w_id, o_d_id ---- -group-by +group-by (streaming) ├── columns: sum:11 [hidden: o_d_id:2!null o_w_id:3!null] ├── grouping columns: o_d_id:2!null o_w_id:3!null ├── key: (2,3) @@ -1238,7 +1238,7 @@ FROM order_line GROUP BY ol_w_id, ol_d_id ORDER BY ol_w_id, ol_d_id ---- -group-by +group-by (streaming) ├── columns: count:13!null [hidden: ol_d_id:2!null ol_w_id:3!null] ├── grouping columns: ol_d_id:2!null ol_w_id:3!null ├── key: (2,3) @@ -1330,7 +1330,7 @@ except-all │ ├── key: (1-3) │ ├── fd: (1-3)-->(7) │ └── ordering: +3,+2,-1 - └── group-by + └── group-by (streaming) ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null count_rows:23!null ├── grouping columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ├── key: (11-13) @@ -1363,7 +1363,7 @@ except-all ├── internal-ordering: +3,+2,-1,+13 ├── key: (1-3) ├── fd: (1-3)-->(13) - ├── group-by + ├── group-by (streaming) │ ├── columns: ol_o_id:1!null ol_d_id:2!null ol_w_id:3!null count_rows:13!null │ ├── grouping columns: ol_o_id:1!null ol_d_id:2!null ol_w_id:3!null │ ├── key: (1-3) diff --git a/pkg/sql/opt/xform/testdata/external/tpcc-no-stats b/pkg/sql/opt/xform/testdata/external/tpcc-no-stats index 911431bade11..9f10a492344a 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcc-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpcc-no-stats @@ -1097,7 +1097,7 @@ scalar-group-by │ ├── immutable │ ├── key: (13) │ ├── fd: (1)-->(9), (13)-->(25), (1)==(13), (13)==(1) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: d_w_id:13!null sum:25 │ │ ├── grouping columns: d_w_id:13!null │ │ ├── internal-ordering: +13 @@ -1131,7 +1131,7 @@ FROM new_order GROUP BY no_d_id, no_w_id ORDER BY no_w_id, no_d_id ---- -group-by +group-by (streaming) ├── columns: max:6!null [hidden: no_d_id:2!null no_w_id:3!null] ├── grouping columns: no_d_id:2!null no_w_id:3!null ├── key: (2,3) @@ -1151,7 +1151,7 @@ FROM "order" GROUP BY o_d_id, o_w_id ORDER BY o_w_id, o_d_id ---- -group-by +group-by (streaming) ├── columns: max:11!null [hidden: o_d_id:2!null o_w_id:3!null] ├── grouping columns: o_d_id:2!null o_w_id:3!null ├── key: (2,3) @@ -1186,7 +1186,7 @@ scalar-group-by │ ├── immutable │ ├── key: (2,3) │ ├── fd: (2,3)-->(6-8) - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: no_d_id:2!null no_w_id:3!null max:6!null min:7!null count_rows:8!null │ │ ├── grouping columns: no_d_id:2!null no_w_id:3!null │ │ ├── internal-ordering: +3,+2 @@ -1213,7 +1213,7 @@ FROM "order" GROUP BY o_w_id, o_d_id ORDER BY o_w_id, o_d_id ---- -group-by +group-by (streaming) ├── columns: sum:11 [hidden: o_d_id:2!null o_w_id:3!null] ├── grouping columns: o_d_id:2!null o_w_id:3!null ├── key: (2,3) @@ -1232,7 +1232,7 @@ FROM order_line GROUP BY ol_w_id, ol_d_id ORDER BY ol_w_id, ol_d_id ---- -group-by +group-by (streaming) ├── columns: count:13!null [hidden: ol_d_id:2!null ol_w_id:3!null] ├── grouping columns: ol_d_id:2!null ol_w_id:3!null ├── key: (2,3) @@ -1342,7 +1342,7 @@ except-all │ ├── key: (1-3) │ ├── fd: (1-3)-->(7) │ └── ordering: +3,+2,-1 - └── group-by + └── group-by (streaming) ├── columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null count_rows:23!null ├── grouping columns: ol_o_id:11!null ol_d_id:12!null ol_w_id:13!null ├── key: (11-13) @@ -1375,7 +1375,7 @@ except-all ├── internal-ordering: +3,+2,-1,+13 ├── key: (1-3) ├── fd: (1-3)-->(13) - ├── group-by + ├── group-by (streaming) │ ├── columns: ol_o_id:1!null ol_d_id:2!null ol_w_id:3!null count_rows:13!null │ ├── grouping columns: ol_o_id:1!null ol_d_id:2!null ol_w_id:3!null │ ├── key: (1-3) diff --git a/pkg/sql/opt/xform/testdata/external/tpce b/pkg/sql/opt/xform/testdata/external/tpce index 482affba3ab1..538970abe435 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce +++ b/pkg/sql/opt/xform/testdata/external/tpce @@ -46,7 +46,7 @@ sort ├── immutable ├── key: (49) ├── fd: (49)-->(56) - ├── group-by + ├── group-by (hash) │ ├── columns: b_name:49!null sum:55!null │ ├── grouping columns: b_name:49!null │ ├── immutable @@ -137,7 +137,7 @@ sort ├── immutable ├── key: (3) ├── fd: (3)-->(56) - ├── group-by + ├── group-by (hash) │ ├── columns: b_name:3!null sum:55!null │ ├── grouping columns: b_name:3!null │ ├── immutable @@ -320,7 +320,7 @@ top-k ├── immutable ├── key: (1) ├── fd: (1)-->(23,24) - ├── group-by + ├── group-by (streaming) │ ├── columns: ca_id:1!null customer_account.ca_bal:6!null sum:22 │ ├── grouping columns: ca_id:1!null │ ├── internal-ordering: +1 @@ -396,7 +396,7 @@ top-k ├── immutable ├── key: (1) ├── fd: (1)-->(23,24) - ├── group-by + ├── group-by (streaming) │ ├── columns: ca_id:1!null customer_account.ca_bal:6!null sum:22 │ ├── grouping columns: ca_id:1!null │ ├── internal-ordering: +1 diff --git a/pkg/sql/opt/xform/testdata/external/tpce-no-stats b/pkg/sql/opt/xform/testdata/external/tpce-no-stats index 174c1a87bb6e..1a098087d3c6 100644 --- a/pkg/sql/opt/xform/testdata/external/tpce-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpce-no-stats @@ -43,7 +43,7 @@ sort ├── immutable ├── key: (49) ├── fd: (49)-->(56) - ├── group-by + ├── group-by (hash) │ ├── columns: b_name:49!null sum:55!null │ ├── grouping columns: b_name:49!null │ ├── immutable @@ -134,7 +134,7 @@ sort ├── immutable ├── key: (3) ├── fd: (3)-->(56) - ├── group-by + ├── group-by (hash) │ ├── columns: b_name:3!null sum:55!null │ ├── grouping columns: b_name:3!null │ ├── immutable @@ -342,7 +342,7 @@ top-k ├── immutable ├── key: (1) ├── fd: (1)-->(23,24) - ├── group-by + ├── group-by (streaming) │ ├── columns: ca_id:1!null customer_account.ca_bal:6!null sum:22 │ ├── grouping columns: ca_id:1!null │ ├── internal-ordering: +1 @@ -418,7 +418,7 @@ top-k ├── immutable ├── key: (1) ├── fd: (1)-->(23,24) - ├── group-by + ├── group-by (streaming) │ ├── columns: ca_id:1!null customer_account.ca_bal:6!null sum:22 │ ├── grouping columns: ca_id:1!null │ ├── internal-ordering: +1 diff --git a/pkg/sql/opt/xform/testdata/external/tpch b/pkg/sql/opt/xform/testdata/external/tpch index 381bec604e62..c0b45bd72e33 100644 --- a/pkg/sql/opt/xform/testdata/external/tpch +++ b/pkg/sql/opt/xform/testdata/external/tpch @@ -46,7 +46,7 @@ sort ├── key: (9,10) ├── fd: (9,10)-->(19,20,22,24-28) ├── ordering: +9,+10 - └── group-by + └── group-by (hash) ├── columns: l_returnflag:9!null l_linestatus:10!null sum:19!null sum:20!null sum:22!null sum:24!null avg:25!null avg:26!null avg:27!null count_rows:28!null ├── grouping columns: l_returnflag:9!null l_linestatus:10!null ├── immutable @@ -159,7 +159,7 @@ project ├── columns: p_partkey:1!null p_mfgr:3!null s_name:13!null s_address:14!null s_phone:16!null s_acctbal:17!null s_comment:18!null ps_partkey:21!null ps_suppkey:22!null ps_supplycost:24!null n_name:29!null min:66!null ├── key: (21,22) ├── fd: (1)-->(3), (21,22)-->(1,3,13,14,16-18,24,29,66), (1)==(21), (21)==(1), (22)-->(13,14,16-18,29), (24)==(66), (66)==(24) - ├── group-by + ├── group-by (hash) │ ├── columns: p_partkey:1!null p_mfgr:3!null s_name:13!null s_address:14!null s_phone:16!null s_acctbal:17!null s_comment:18!null ps_partkey:21!null ps_suppkey:22!null ps_supplycost:24!null n_name:29!null min:66!null │ ├── grouping columns: ps_partkey:21!null ps_suppkey:22!null │ ├── key: (21,22) @@ -324,7 +324,7 @@ top-k ├── key: (22) ├── fd: (22)-->(15,18,41) ├── ordering: -41,+15 - └── group-by + └── group-by (hash) ├── columns: o_orderdate:15!null o_shippriority:18!null l_orderkey:22!null sum:41!null ├── grouping columns: l_orderkey:22!null ├── immutable @@ -416,7 +416,7 @@ sort ├── key: (6) ├── fd: (6)-->(30) ├── ordering: +6 - └── group-by + └── group-by (hash) ├── columns: o_orderpriority:6!null count_rows:30!null ├── grouping columns: o_orderpriority:6!null ├── key: (6) @@ -486,7 +486,7 @@ sort ├── key: (50) ├── fd: (50)-->(61) ├── ordering: -61 - └── group-by + └── group-by (hash) ├── columns: n_name:50!null sum:61!null ├── grouping columns: n_name:50!null ├── immutable @@ -671,7 +671,7 @@ sort ├── key: (50,56,61) ├── fd: (50,56,61)-->(63) ├── ordering: +50,+56,+61 - └── group-by + └── group-by (hash) ├── columns: n1.n_name:50!null n2.n_name:56!null l_year:61 sum:63!null ├── grouping columns: n1.n_name:50!null n2.n_name:56!null l_year:61 ├── immutable @@ -799,7 +799,7 @@ sort ├── immutable ├── key: (77) ├── fd: (77)-->(82) - ├── group-by + ├── group-by (hash) │ ├── columns: o_year:77 sum:80!null sum:81!null │ ├── grouping columns: o_year:77 │ ├── immutable @@ -956,7 +956,7 @@ sort ├── key: (58,63) ├── fd: (58,63)-->(65) ├── ordering: +58,-63 - └── group-by + └── group-by (hash) ├── columns: n_name:58!null o_year:63 sum:65!null ├── grouping columns: n_name:58!null o_year:63 ├── immutable @@ -1091,7 +1091,7 @@ top-k ├── key: (1) ├── fd: (1)-->(2,3,5,6,8,41,47) ├── ordering: -47 - └── group-by + └── group-by (hash) ├── columns: c_custkey:1!null c_name:2!null c_address:3!null c_phone:5!null c_acctbal:6!null c_comment:8!null n_name:41!null sum:47!null ├── grouping columns: c_custkey:1!null ├── immutable @@ -1204,7 +1204,7 @@ sort ├── immutable ├── key: (1) ├── fd: (1)-->(24) - ├── group-by + ├── group-by (hash) │ ├── columns: ps_partkey:1!null sum:24!null │ ├── grouping columns: ps_partkey:1!null │ ├── immutable @@ -1351,7 +1351,7 @@ sort ├── key: (26) ├── fd: (26)-->(31,33) ├── ordering: +26 - └── group-by + └── group-by (hash) ├── columns: l_shipmode:26!null sum:31!null sum:33!null ├── grouping columns: l_shipmode:26!null ├── key: (26) @@ -1423,12 +1423,12 @@ sort ├── key: (22) ├── fd: (22)-->(23) ├── ordering: -23,-22 - └── group-by + └── group-by (hash) ├── columns: count:22!null count_rows:23!null ├── grouping columns: count:22!null ├── key: (22) ├── fd: (22)-->(23) - ├── group-by + ├── group-by (hash) │ ├── columns: c_custkey:1!null count:22!null │ ├── grouping columns: c_custkey:1!null │ ├── key: (1) @@ -1603,7 +1603,7 @@ project │ ├── immutable │ ├── key: (12) │ ├── fd: (12)-->(29) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: l_suppkey:12!null sum:29!null │ │ ├── grouping columns: l_suppkey:12!null │ │ ├── immutable @@ -1634,7 +1634,7 @@ project │ ├── immutable │ ├── key: () │ ├── fd: ()-->(50) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: l_suppkey:32!null sum:49!null │ │ ├── grouping columns: l_suppkey:32!null │ │ ├── immutable @@ -1711,7 +1711,7 @@ sort ├── key: (11-13) ├── fd: (11-13)-->(28) ├── ordering: -28,+11,+12,+13 - └── group-by + └── group-by (hash) ├── columns: p_brand:11!null p_type:12!null p_size:13!null count:28!null ├── grouping columns: p_brand:11!null p_type:12!null p_size:13!null ├── key: (11-13) @@ -1818,7 +1818,7 @@ project │ │ │ │ ├── immutable │ │ │ │ ├── key: (19) │ │ │ │ ├── fd: (19)-->(49) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: p_partkey:19!null avg:48!null │ │ │ │ │ ├── grouping columns: p_partkey:19!null │ │ │ │ │ ├── internal-ordering: +(19|31) opt(22,25) @@ -1919,7 +1919,7 @@ top-k ├── key: (11) ├── fd: (1)-->(2), (11)-->(1,2,14,15,59) ├── ordering: -14,+15 - └── group-by + └── group-by (hash) ├── columns: c_custkey:1!null c_name:2!null o_orderkey:11!null o_totalprice:14!null o_orderdate:15!null sum:59!null ├── grouping columns: o_orderkey:11!null ├── key: (11) @@ -1953,7 +1953,7 @@ top-k │ │ │ │ ├── key: (40) │ │ │ │ ├── fd: (40)-->(58) │ │ │ │ ├── ordering: +40 - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: l_orderkey:40!null sum:58!null │ │ │ │ │ ├── grouping columns: l_orderkey:40!null │ │ │ │ │ ├── key: (40) @@ -2172,7 +2172,7 @@ sort │ │ │ ├── immutable │ │ │ ├── key: (16,17) │ │ │ ├── fd: (16,17)-->(18,52) - │ │ │ ├── group-by + │ │ │ ├── group-by (hash) │ │ │ │ ├── columns: ps_partkey:16!null ps_suppkey:17!null ps_availqty:18!null sum:52!null │ │ │ │ ├── grouping columns: ps_partkey:16!null ps_suppkey:17!null │ │ │ │ ├── key: (16,17) @@ -2268,7 +2268,7 @@ top-k ├── key: (2) ├── fd: (2)-->(81) ├── ordering: -81,+2 - └── group-by + └── group-by (hash) ├── columns: s_name:2!null count_rows:81!null ├── grouping columns: s_name:2!null ├── key: (2) @@ -2387,7 +2387,7 @@ sort ├── key: (33) ├── fd: (33)-->(34,35) ├── ordering: +33 - └── group-by + └── group-by (hash) ├── columns: cntrycode:33 count_rows:34!null sum:35!null ├── grouping columns: cntrycode:33 ├── immutable diff --git a/pkg/sql/opt/xform/testdata/external/tpch-no-stats b/pkg/sql/opt/xform/testdata/external/tpch-no-stats index 1b77c00602da..6e61dc648487 100644 --- a/pkg/sql/opt/xform/testdata/external/tpch-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpch-no-stats @@ -37,7 +37,7 @@ ORDER BY l_returnflag, l_linestatus; ---- -group-by +group-by (streaming) ├── columns: l_returnflag:9!null l_linestatus:10!null sum_qty:19!null sum_base_price:20!null sum_disc_price:22!null sum_charge:24!null avg_qty:25!null avg_price:26!null avg_disc:27!null count_order:28!null ├── grouping columns: l_returnflag:9!null l_linestatus:10!null ├── immutable @@ -160,7 +160,7 @@ project ├── columns: p_partkey:1!null p_mfgr:3!null s_name:13!null s_address:14!null s_phone:16!null s_acctbal:17!null s_comment:18!null ps_partkey:21!null ps_suppkey:22!null ps_supplycost:24!null n_name:29!null min:66!null ├── key: (21,22) ├── fd: (1)-->(3), (21,22)-->(1,3,13,14,16-18,24,29,66), (1)==(21), (21)==(1), (22)-->(13,14,16-18,29), (24)==(66), (66)==(24) - ├── group-by + ├── group-by (hash) │ ├── columns: p_partkey:1!null p_mfgr:3!null s_name:13!null s_address:14!null s_phone:16!null s_acctbal:17!null s_comment:18!null ps_partkey:21!null ps_suppkey:22!null ps_supplycost:24!null n_name:29!null min:66!null │ ├── grouping columns: ps_partkey:21!null ps_suppkey:22!null │ ├── key: (21,22) @@ -301,7 +301,7 @@ top-k ├── key: (22) ├── fd: (22)-->(15,18,41) ├── ordering: -41,+15 - └── group-by + └── group-by (hash) ├── columns: o_orderdate:15!null o_shippriority:18!null l_orderkey:22!null sum:41!null ├── grouping columns: l_orderkey:22!null ├── immutable @@ -402,7 +402,7 @@ sort ├── key: (6) ├── fd: (6)-->(30) ├── ordering: +6 - └── group-by + └── group-by (hash) ├── columns: o_orderpriority:6!null count_rows:30!null ├── grouping columns: o_orderpriority:6!null ├── key: (6) @@ -477,7 +477,7 @@ sort ├── key: (50) ├── fd: (50)-->(61) ├── ordering: -61 - └── group-by + └── group-by (hash) ├── columns: n_name:50!null sum:61!null ├── grouping columns: n_name:50!null ├── immutable @@ -648,7 +648,7 @@ ORDER BY cust_nation, l_year; ---- -group-by +group-by (streaming) ├── columns: supp_nation:50!null cust_nation:56!null l_year:61 revenue:63!null ├── grouping columns: n1.n_name:50!null n2.n_name:56!null l_year:61 ├── immutable @@ -794,7 +794,7 @@ sort ├── immutable ├── key: (77) ├── fd: (77)-->(82) - ├── group-by + ├── group-by (hash) │ ├── columns: o_year:77 sum:80!null sum:81!null │ ├── grouping columns: o_year:77 │ ├── immutable @@ -943,7 +943,7 @@ sort ├── key: (58,63) ├── fd: (58,63)-->(65) ├── ordering: +58,-63 - └── group-by + └── group-by (hash) ├── columns: n_name:58!null o_year:63 sum:65!null ├── grouping columns: n_name:58!null o_year:63 ├── immutable @@ -1054,7 +1054,7 @@ top-k ├── key: (1) ├── fd: (1)-->(2,3,5,6,8,41,47) ├── ordering: -47 - └── group-by + └── group-by (hash) ├── columns: c_custkey:1!null c_name:2!null c_address:3!null c_phone:5!null c_acctbal:6!null c_comment:8!null n_name:41!null sum:47!null ├── grouping columns: c_custkey:1!null ├── immutable @@ -1158,7 +1158,7 @@ sort ├── immutable ├── key: (1) ├── fd: (1)-->(24) - ├── group-by + ├── group-by (hash) │ ├── columns: ps_partkey:1!null sum:24!null │ ├── grouping columns: ps_partkey:1!null │ ├── immutable @@ -1299,7 +1299,7 @@ sort ├── key: (26) ├── fd: (26)-->(31,33) ├── ordering: +26 - └── group-by + └── group-by (hash) ├── columns: l_shipmode:26!null sum:31!null sum:33!null ├── grouping columns: l_shipmode:26!null ├── key: (26) @@ -1367,12 +1367,12 @@ sort ├── key: (22) ├── fd: (22)-->(23) ├── ordering: -23,-22 - └── group-by + └── group-by (hash) ├── columns: count:22!null count_rows:23!null ├── grouping columns: count:22!null ├── key: (22) ├── fd: (22)-->(23) - ├── group-by + ├── group-by (hash) │ ├── columns: c_custkey:1!null count:22!null │ ├── grouping columns: c_custkey:1!null │ ├── key: (1) @@ -1542,7 +1542,7 @@ project │ ├── immutable │ ├── key: (12) │ ├── fd: (12)-->(29) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: l_suppkey:12!null sum:29!null │ │ ├── grouping columns: l_suppkey:12!null │ │ ├── immutable @@ -1572,7 +1572,7 @@ project │ ├── immutable │ ├── key: () │ ├── fd: ()-->(50) - │ ├── group-by + │ ├── group-by (hash) │ │ ├── columns: l_suppkey:32!null sum:49!null │ │ ├── grouping columns: l_suppkey:32!null │ │ ├── immutable @@ -1648,7 +1648,7 @@ sort ├── key: (11-13) ├── fd: (11-13)-->(28) ├── ordering: -28,+11,+12,+13 - └── group-by + └── group-by (hash) ├── columns: p_brand:11!null p_type:12!null p_size:13!null count:28!null ├── grouping columns: p_brand:11!null p_type:12!null p_size:13!null ├── key: (11-13) @@ -1756,7 +1756,7 @@ project │ │ │ │ ├── immutable │ │ │ │ ├── key: (19) │ │ │ │ ├── fd: (19)-->(49) - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: p_partkey:19!null avg:48!null │ │ │ │ │ ├── grouping columns: p_partkey:19!null │ │ │ │ │ ├── internal-ordering: +(19|31) opt(22,25) @@ -1857,7 +1857,7 @@ top-k ├── key: (11) ├── fd: (1)-->(2), (11)-->(1,2,14,15,59) ├── ordering: -14,+15 - └── group-by + └── group-by (streaming) ├── columns: c_custkey:1!null c_name:2!null o_orderkey:11!null o_totalprice:14!null o_orderdate:15!null sum:59!null ├── grouping columns: o_orderkey:11!null ├── internal-ordering: +(11|22) @@ -1901,7 +1901,7 @@ top-k │ │ │ │ ├── key: (40) │ │ │ │ ├── fd: (40)-->(58) │ │ │ │ ├── ordering: +40 - │ │ │ │ ├── group-by + │ │ │ │ ├── group-by (streaming) │ │ │ │ │ ├── columns: l_orderkey:40!null sum:58!null │ │ │ │ │ ├── grouping columns: l_orderkey:40!null │ │ │ │ │ ├── key: (40) @@ -2106,7 +2106,7 @@ sort │ │ │ ├── immutable │ │ │ ├── key: (16,17) │ │ │ ├── fd: (16,17)-->(18,52) - │ │ │ ├── group-by + │ │ │ ├── group-by (hash) │ │ │ │ ├── columns: ps_partkey:16!null ps_suppkey:17!null ps_availqty:18!null sum:52!null │ │ │ │ ├── grouping columns: ps_partkey:16!null ps_suppkey:17!null │ │ │ │ ├── key: (16,17) @@ -2201,7 +2201,7 @@ top-k ├── key: (2) ├── fd: (2)-->(81) ├── ordering: -81,+2 - └── group-by + └── group-by (hash) ├── columns: s_name:2!null count_rows:81!null ├── grouping columns: s_name:2!null ├── key: (2) @@ -2302,7 +2302,7 @@ GROUP BY ORDER BY cntrycode; ---- -group-by +group-by (streaming) ├── columns: cntrycode:33 numcust:34!null totacctbal:35!null ├── grouping columns: cntrycode:33 ├── immutable diff --git a/pkg/sql/opt/xform/testdata/external/trading b/pkg/sql/opt/xform/testdata/external/trading index eedc02a1967c..278e7d378d3a 100644 --- a/pkg/sql/opt/xform/testdata/external/trading +++ b/pkg/sql/opt/xform/testdata/external/trading @@ -812,7 +812,7 @@ project │ ├── key: (10) │ ├── fd: (1)-->(2-6), (2,4,5)~~>(1,3,6), (10)-->(1-6,11-17,30), (17)-->(10-16), (1)==(10), (10)==(1) │ ├── ordering: +2,+4,+5 - │ └── group-by + │ └── group-by (streaming) │ ├── columns: id:1!null name:2!null rarity:3 setname:4 number:5!null isfoil:6!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null sum:30 │ ├── grouping columns: cardsinfo.cardid:10!null │ ├── internal-ordering: +(1|10) opt(9) @@ -949,7 +949,7 @@ sort ├── key: (45) ├── fd: (45)-->(40,42,44) ├── ordering: +45 - └── group-by + └── group-by (hash) ├── columns: sum:40!null sum:42!null sum:44!null column45:45 ├── grouping columns: column45:45 ├── stable @@ -1099,7 +1099,7 @@ top-k ├── key: (10) ├── fd: (10)-->(16) ├── ordering: -16 - └── group-by + └── group-by (hash) ├── columns: accountname:10!null sum:16!null ├── grouping columns: accountname:10!null ├── key: (10) @@ -1483,7 +1483,7 @@ update cardsinfo [as=ci] ├── columns: actualinventory_new:39 ci.dealerid:17!null ci.cardid:18!null buyprice:19!null sellprice:20!null discount:21!null desiredinventory:22!null actualinventory:23!null maxinventory:24!null ci.version:25!null c:28!null q:29!null ├── key: (18) ├── fd: ()-->(17), (18)-->(19-25,28,29,39), (25)-->(18-24), (18)==(28), (28)==(18) - ├── group-by + ├── group-by (hash) │ ├── columns: ci.dealerid:17!null ci.cardid:18!null buyprice:19!null sellprice:20!null discount:21!null desiredinventory:22!null actualinventory:23!null maxinventory:24!null ci.version:25!null c:28!null q:29!null sum_int:37 │ ├── grouping columns: ci.cardid:18!null │ ├── key: (18) diff --git a/pkg/sql/opt/xform/testdata/external/trading-mutation b/pkg/sql/opt/xform/testdata/external/trading-mutation index 8b796596bc4b..24e03d7a303b 100644 --- a/pkg/sql/opt/xform/testdata/external/trading-mutation +++ b/pkg/sql/opt/xform/testdata/external/trading-mutation @@ -816,7 +816,7 @@ project │ ├── key: (10) │ ├── fd: (1)-->(2-6), (2,4,5)~~>(1,3,6), (10)-->(1-6,11-17,36), (17)-->(10-16), (1)==(10), (10)==(1) │ ├── ordering: +2,+4,+5 - │ └── group-by + │ └── group-by (streaming) │ ├── columns: id:1!null name:2!null rarity:3 setname:4 number:5!null isfoil:6!null cardsinfo.cardid:10!null cardsinfo.buyprice:11!null cardsinfo.sellprice:12!null cardsinfo.discount:13!null desiredinventory:14!null actualinventory:15!null maxinventory:16!null cardsinfo.version:17!null sum:36 │ ├── grouping columns: cardsinfo.cardid:10!null │ ├── internal-ordering: +(1|10) opt(9) @@ -953,7 +953,7 @@ sort ├── key: (53) ├── fd: (53)-->(48,50,52) ├── ordering: +53 - └── group-by + └── group-by (hash) ├── columns: sum:48!null sum:50!null sum:52!null column53:53 ├── grouping columns: column53:53 ├── stable @@ -1103,7 +1103,7 @@ top-k ├── key: (10) ├── fd: (10)-->(18) ├── ordering: -18 - └── group-by + └── group-by (hash) ├── columns: accountname:10!null sum:18!null ├── grouping columns: accountname:10!null ├── key: (10) @@ -1500,7 +1500,7 @@ update cardsinfo [as=ci] ├── immutable ├── key: (22) ├── fd: ()-->(21,50,51), (22)-->(23-33,36,37,49,53), (29)-->(22-28,30-33), (22)==(36), (36)==(22) - ├── group-by + ├── group-by (hash) │ ├── columns: ci.dealerid:21!null ci.cardid:22!null buyprice:23!null sellprice:24!null discount:25!null desiredinventory:26!null actualinventory:27!null maxinventory:28!null ci.version:29!null discountbuyprice:30 notes:31 oldinventory:32 ci.extra:33 c:36!null q:37!null sum_int:47 │ ├── grouping columns: ci.cardid:22!null │ ├── key: (22) diff --git a/pkg/sql/opt/xform/testdata/physprops/limit_hint b/pkg/sql/opt/xform/testdata/physprops/limit_hint index 54d83f307ad0..724368a715b5 100644 --- a/pkg/sql/opt/xform/testdata/physprops/limit_hint +++ b/pkg/sql/opt/xform/testdata/physprops/limit_hint @@ -288,7 +288,7 @@ limit ├── cardinality: [0 - 1] ├── key: () ├── fd: ()-->(2,6) - ├── group-by + ├── group-by (streaming) │ ├── columns: y:2 count_rows:6!null │ ├── grouping columns: y:2 │ ├── internal-ordering: +2 @@ -318,7 +318,7 @@ limit ├── stats: [rows=1e-07] ├── key: (2) ├── fd: (2)-->(6) - ├── group-by + ├── group-by (streaming) │ ├── columns: y:2 count_rows:6!null │ ├── grouping columns: y:2 │ ├── internal-ordering: +2 opt(3) @@ -350,7 +350,7 @@ limit ├── cardinality: [0 - 1] ├── key: () ├── fd: ()-->(3,6) - ├── group-by + ├── group-by (hash) │ ├── columns: z:3 count_rows:6!null │ ├── grouping columns: z:3 │ ├── key: (3) diff --git a/pkg/sql/opt/xform/testdata/physprops/ordering b/pkg/sql/opt/xform/testdata/physprops/ordering index 87705e22072d..d681e6a1b7c5 100644 --- a/pkg/sql/opt/xform/testdata/physprops/ordering +++ b/pkg/sql/opt/xform/testdata/physprops/ordering @@ -427,7 +427,7 @@ scalar-group-by opt SELECT a, min(b) FROM abc GROUP BY a ORDER BY a ---- -group-by +group-by (streaming) ├── columns: a:1!null min:6!null ├── grouping columns: a:1!null ├── key: (1) @@ -443,7 +443,7 @@ group-by opt SELECT a, b, min(c) FROM abc GROUP BY a, b ORDER BY a ---- -group-by +group-by (streaming) ├── columns: a:1!null b:2!null min:6!null ├── grouping columns: a:1!null b:2!null ├── internal-ordering: +1,+2 @@ -461,7 +461,7 @@ group-by opt SELECT a, b, min(c) FROM abc GROUP BY a, b ORDER BY a, b ---- -group-by +group-by (streaming) ├── columns: a:1!null b:2!null min:6!null ├── grouping columns: a:1!null b:2!null ├── key: (1,2) @@ -478,7 +478,7 @@ group-by opt SELECT a, b, min(c) FROM abc GROUP BY b, a ORDER BY a, b ---- -group-by +group-by (streaming) ├── columns: a:1!null b:2!null min:6!null ├── grouping columns: a:1!null b:2!null ├── key: (1,2) @@ -501,7 +501,7 @@ sort (segmented) ├── key: (1,2) ├── fd: (1,2)-->(6) ├── ordering: +1,+6 - └── group-by + └── group-by (streaming) ├── columns: a:1!null b:2!null min:6!null ├── grouping columns: a:1!null b:2!null ├── internal-ordering: +1,+2 @@ -520,7 +520,7 @@ sort (segmented) opt SELECT a, b, array_agg(c) FROM (SELECT * FROM abc ORDER BY c) GROUP BY a, b ORDER BY a, b ---- -group-by +group-by (streaming) ├── columns: a:1!null b:2!null array_agg:6!null ├── grouping columns: a:1!null b:2!null ├── internal-ordering: +3 opt(1,2) @@ -538,7 +538,7 @@ group-by opt SELECT a, b, array_agg(c) FROM (SELECT * FROM abc ORDER BY a, b, c) GROUP BY a, b ORDER BY a, b ---- -group-by +group-by (streaming) ├── columns: a:1!null b:2!null array_agg:6!null ├── grouping columns: a:1!null b:2!null ├── internal-ordering: +3 opt(1,2) @@ -556,7 +556,7 @@ group-by opt SELECT a, b, array_agg(c) FROM (SELECT * FROM abc ORDER BY b, c, a) GROUP BY b, a ORDER BY a, b ---- -group-by +group-by (streaming) ├── columns: a:1!null b:2!null array_agg:6!null ├── grouping columns: a:1!null b:2!null ├── internal-ordering: +3 opt(1,2) @@ -576,7 +576,7 @@ group-by opt SELECT sum(c) FROM abc WHERE a = 1 GROUP BY b ORDER BY b ---- -group-by +group-by (streaming) ├── columns: sum:6!null [hidden: b:2!null] ├── grouping columns: b:2!null ├── key: (2) @@ -598,7 +598,7 @@ SELECT sum(d) FROM abcd GROUP BY a, b, c ---- project ├── columns: sum:8 - └── group-by + └── group-by (hash) ├── columns: a:1 b:2 c:3 sum:8 ├── grouping columns: a:1 b:2 c:3 ├── key: (1-3) @@ -615,7 +615,7 @@ SELECT sum(a) FROM abcd GROUP BY b, c, d ---- project ├── columns: sum:8 - └── group-by + └── group-by (hash) ├── columns: b:2 c:3 d:4 sum:8 ├── grouping columns: b:2 c:3 d:4 ├── key: (2-4) @@ -631,7 +631,7 @@ SELECT array_agg(d) FROM (SELECT * FROM abcd ORDER BY c) GROUP BY a, b ---- project ├── columns: array_agg:8 - └── group-by + └── group-by (hash) ├── columns: a:1 b:2 array_agg:8 ├── grouping columns: a:1 b:2 ├── internal-ordering: +3 opt(1,2) diff --git a/pkg/sql/opt/xform/testdata/physprops/presentation b/pkg/sql/opt/xform/testdata/physprops/presentation index 160f12e53da6..1a4190c68e81 100644 --- a/pkg/sql/opt/xform/testdata/physprops/presentation +++ b/pkg/sql/opt/xform/testdata/physprops/presentation @@ -68,7 +68,7 @@ inner-join (cross) opt SELECT max(y), y, y, x FROM a GROUP BY a.x, a.y ---- -group-by +group-by (streaming) ├── columns: max:5 y:2 y:2 x:1!null ├── grouping columns: x:1!null ├── internal-ordering: +1 diff --git a/pkg/sql/opt/xform/testdata/ruleprops/orderings b/pkg/sql/opt/xform/testdata/ruleprops/orderings index 83ddaefaa1c7..ecd9721174e7 100644 --- a/pkg/sql/opt/xform/testdata/ruleprops/orderings +++ b/pkg/sql/opt/xform/testdata/ruleprops/orderings @@ -73,7 +73,7 @@ project opt SELECT min(b), a FROM abc GROUP BY a ---- -group-by +group-by (streaming) ├── columns: min:7 a:1 ├── grouping columns: a:1 ├── internal-ordering: +1 @@ -93,7 +93,7 @@ group-by opt SELECT min(b), c FROM abc GROUP BY c ---- -group-by +group-by (hash) ├── columns: min:7 c:3 ├── grouping columns: c:3 ├── key: (3) @@ -114,7 +114,7 @@ group-by opt SELECT array_agg(a), b, c FROM (SELECT * FROM abc ORDER BY b, a) GROUP BY b, c ---- -group-by +group-by (hash) ├── columns: array_agg:7 b:2 c:3 ├── grouping columns: b:2 c:3 ├── internal-ordering: +1 opt(2,3) diff --git a/pkg/sql/opt/xform/testdata/rules/groupby b/pkg/sql/opt/xform/testdata/rules/groupby index 414f5b1b3768..ea040625bd47 100644 --- a/pkg/sql/opt/xform/testdata/rules/groupby +++ b/pkg/sql/opt/xform/testdata/rules/groupby @@ -303,7 +303,7 @@ SELECT min(y) FROM xyz GROUP BY y ---- project ├── columns: min:6 - └── group-by + └── group-by (streaming) ├── columns: y:2 min:6 ├── grouping columns: y:2 ├── internal-ordering: +2 @@ -323,7 +323,7 @@ SELECT max(y) FROM xyz GROUP BY y ---- project ├── columns: max:6 - └── group-by + └── group-by (streaming) ├── columns: y:2 max:6 ├── grouping columns: y:2 ├── internal-ordering: +2 @@ -343,7 +343,7 @@ SELECT min(y) FROM xyz GROUP BY x ---- project ├── columns: min:6 - └── group-by + └── group-by (streaming) ├── columns: x:1!null min:6 ├── grouping columns: x:1!null ├── internal-ordering: +1 @@ -363,7 +363,7 @@ project opt expect-not=ReplaceScalarMinMaxWithLimit SELECT x,min(y) FROM xyz GROUP BY x,y ---- -group-by +group-by (streaming) ├── columns: x:1!null min:6 ├── grouping columns: x:1!null ├── internal-ordering: +1 @@ -383,7 +383,7 @@ group-by opt expect-not=ReplaceScalarMinMaxWithLimit SELECT x,max(y) FROM xyz GROUP BY x,y ---- -group-by +group-by (streaming) ├── columns: x:1!null max:6 ├── grouping columns: x:1!null ├── internal-ordering: +1 @@ -405,7 +405,7 @@ SELECT min(x), count(y) FROM xyz GROUP BY x,y ---- project ├── columns: min:6!null count:7!null - └── group-by + └── group-by (streaming) ├── columns: x:1!null min:6!null count:7!null ├── grouping columns: x:1!null ├── internal-ordering: +1 @@ -429,7 +429,7 @@ SELECT max(x), count(y) FROM xyz GROUP BY x,y ---- project ├── columns: max:6!null count:7!null - └── group-by + └── group-by (streaming) ├── columns: x:1!null max:6!null count:7!null ├── grouping columns: x:1!null ├── internal-ordering: +1 @@ -889,7 +889,7 @@ SELECT min(k) FROM kuvw WHERE v = 5 GROUP BY v, w ---- project ├── columns: min:7!null - └── group-by + └── group-by (streaming) ├── columns: w:4 min:7!null ├── grouping columns: w:4 ├── internal-ordering: +4 opt(3) @@ -911,7 +911,7 @@ SELECT max(k) FROM kuvw WHERE v = 5 GROUP BY v, w ---- project ├── columns: max:7!null - └── group-by + └── group-by (streaming) ├── columns: w:4 max:7!null ├── grouping columns: w:4 ├── internal-ordering: +4 opt(3) @@ -935,7 +935,7 @@ SELECT min(w) FROM kuvw WHERE v > 5 AND w IS NOT NULL GROUP BY v ---- project ├── columns: min:7!null - └── group-by + └── group-by (streaming) ├── columns: v:3!null min:7!null ├── grouping columns: v:3!null ├── internal-ordering: +3 @@ -962,7 +962,7 @@ SELECT max(w) FROM kuvw WHERE v > 5 GROUP BY v ---- project ├── columns: max:7 - └── group-by + └── group-by (streaming) ├── columns: v:3!null max:7 ├── grouping columns: v:3!null ├── internal-ordering: +3 @@ -981,7 +981,7 @@ project opt expect-not=ReplaceMinWithLimit SELECT min(w), min(k) FROM kuvw WHERE v = 5 AND w IS NOT NULL GROUP BY v ---- -group-by +group-by (streaming) ├── columns: min:7!null min:8!null ├── cardinality: [0 - 1] ├── key: () @@ -1002,7 +1002,7 @@ group-by opt expect-not=ReplaceMaxWithLimit SELECT max(w), max(k) FROM kuvw WHERE v = 5 GROUP BY v ---- -group-by +group-by (streaming) ├── columns: max:7 max:8!null ├── cardinality: [0 - 1] ├── key: () @@ -1022,7 +1022,7 @@ group-by opt expect-not=ReplaceMinWithLimit SELECT min(k), max(k) FROM kuvw WHERE w = 5 GROUP BY w ---- -group-by +group-by (streaming) ├── columns: min:7!null max:8!null ├── cardinality: [0 - 1] ├── key: () @@ -1042,7 +1042,7 @@ group-by opt expect-not=ReplaceMaxWithLimit SELECT max(w), count(w) FROM kuvw WHERE v = 5 GROUP BY v ---- -group-by +group-by (streaming) ├── columns: max:7 count:8!null ├── cardinality: [0 - 1] ├── key: () @@ -1063,7 +1063,7 @@ group-by opt expect-not=ReplaceMinWithLimit SELECT min(w) FROM kuvw WHERE v = 5 GROUP BY v ---- -group-by +group-by (streaming) ├── columns: min:7 ├── cardinality: [0 - 1] ├── key: () @@ -2453,7 +2453,7 @@ project │ ├── key: (2,3) │ ├── fd: (2,3)-->(10) │ ├── limit hint: 1.00 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: a:2!null b:3!null count_rows:10!null │ │ ├── grouping columns: a:2!null b:3!null │ │ ├── internal-ordering: +2,+3 @@ -2509,7 +2509,7 @@ project │ ├── key: (5,6) │ ├── fd: (5,6)-->(10) │ ├── limit hint: 1.00 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: d:5!null e:6!null count_rows:10!null │ │ ├── grouping columns: d:5!null e:6!null │ │ ├── internal-ordering: +5,+6 @@ -2575,7 +2575,7 @@ project │ ├── key: (2) │ ├── fd: (2)-->(10) │ ├── limit hint: 1.00 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: a:2!null count_rows:10!null │ │ ├── grouping columns: a:2!null │ │ ├── internal-ordering: +2 @@ -2633,7 +2633,7 @@ project │ ├── key: (5) │ ├── fd: (5)-->(10) │ ├── limit hint: 1.00 - │ ├── group-by + │ ├── group-by (streaming) │ │ ├── columns: d:5!null count_rows:10!null │ │ ├── grouping columns: d:5!null │ │ ├── internal-ordering: +5 @@ -2877,7 +2877,7 @@ SELECT a, array_agg(b) FROM (SELECT * FROM regional ORDER BY b) GROUP BY a ---- -group-by +group-by (streaming) ├── columns: a:2!null array_agg:10!null ├── grouping columns: a:2!null ├── internal-ordering: +2,+3 @@ -2908,7 +2908,7 @@ SELECT a, array_agg(c) FROM (SELECT * FROM regional ORDER BY c) GROUP BY a ---- -group-by +group-by (hash) ├── columns: a:2!null array_agg:10 ├── grouping columns: a:2!null ├── internal-ordering: +4 opt(2) @@ -2944,7 +2944,7 @@ SELECT a, array_agg(r) FROM (SELECT * FROM regional ORDER BY r) GROUP BY a ---- -group-by +group-by (hash) ├── columns: a:2!null array_agg:10!null ├── grouping columns: a:2!null ├── internal-ordering: +1 opt(2) @@ -2976,7 +2976,7 @@ CREATE TABLE abcd ( opt expect=EliminateIndexJoinOrProjectInsideGroupBy SELECT max(b), a FROM abcd WHERE c > 0 GROUP BY a ---- -group-by +group-by (streaming) ├── columns: max:8 a:1 ├── grouping columns: a:1 ├── internal-ordering: +1 @@ -2995,7 +2995,7 @@ group-by opt expect=EliminateIndexJoinOrProjectInsideGroupBy SELECT count(*), c FROM abcd WHERE d = 1 GROUP BY c ---- -group-by +group-by (streaming) ├── columns: count:8!null c:3 ├── grouping columns: c:3 ├── internal-ordering: +3 @@ -3150,7 +3150,7 @@ upsert xyz opt expect-not=EliminateIndexJoinOrProjectInsideGroupBy SELECT max(b), c FROM abcd WHERE c > 0 GROUP BY c ---- -group-by +group-by (hash) ├── columns: max:8 c:3!null ├── grouping columns: c:3!null ├── key: (3) @@ -3174,7 +3174,7 @@ group-by opt expect-not=EliminateIndexJoinOrProjectInsideGroupBy SELECT count(*), d FROM abcd WHERE d = 1 GROUP BY d ---- -group-by +group-by (streaming) ├── columns: count:8!null d:4!null ├── cardinality: [0 - 1] ├── key: () @@ -3194,7 +3194,7 @@ group-by opt expect-not=EliminateIndexJoinOrProjectInsideGroupBy SELECT max(c), a FROM abcd WHERE c > 0 GROUP BY a ---- -group-by +group-by (hash) ├── columns: max:8!null a:1 ├── grouping columns: a:1 ├── key: (1) @@ -3218,7 +3218,7 @@ group-by opt expect-not=EliminateIndexJoinOrProjectInsideGroupBy SELECT max(d), c FROM abcd WHERE d = 1 GROUP BY c ---- -group-by +group-by (streaming) ├── columns: max:8!null c:3 ├── grouping columns: c:3 ├── internal-ordering: +3 opt(4) @@ -3243,7 +3243,7 @@ SELECT a, array_agg(b) FROM (SELECT a, b FROM abcd WHERE c > 0 ORDER BY c) GROUP BY a ---- -group-by +group-by (hash) ├── columns: a:1 array_agg:8 ├── grouping columns: a:1 ├── internal-ordering: +3 opt(1) @@ -3266,3 +3266,230 @@ group-by └── aggregations └── array-agg [as=array_agg:8, outer=(2)] └── b:2 + +# -------------------------------------------------- +# GenerateLimitedGroupByScans +# -------------------------------------------------- + +exec-ddl +CREATE TABLE defg ( +d INT, +e INT, +f INT, +g INT, +INDEX dd (d), +INDEX dfg (d, f, g), +INDEX df (d, f), +INDEX gd (g, d), +INDEX gg (g) +) +---- + +memo +SELECT d, e, count(*) FROM defg GROUP BY d, e LIMIT 10 +---- +memo (optimized, ~16KB, required=[presentation: d:1,e:2,count:8]) + ├── G1: (limit G2 G3) (limit G4 G3) (limit G5 G3) (limit G6 G3) + │ └── [presentation: d:1,e:2,count:8] + │ ├── best: (limit G4="[limit hint: 10.00]" G3) + │ └── cost: 641.98 + ├── G2: (group-by G7 G8 cols=(1,2)) (group-by G7 G8 cols=(1,2),ordering=+1) + │ └── [limit hint: 10.00] + │ ├── best: (group-by G7 G8 cols=(1,2)) + │ └── cost: 1144.90 + ├── G3: (const 10) + ├── G4: (group-by G9 G8 cols=(1,2)) (group-by G9 G8 cols=(1,2),ordering=+1) + │ └── [limit hint: 10.00] + │ ├── best: (group-by G9="[ordering: +1] [limit hint: 10.00]" G8 cols=(1,2),ordering=+1) + │ └── cost: 641.87 + ├── G5: (group-by G10 G8 cols=(1,2)) (group-by G10 G8 cols=(1,2),ordering=+1) + │ └── [limit hint: 10.00] + │ ├── best: (group-by G10="[ordering: +1] [limit hint: 10.00]" G8 cols=(1,2),ordering=+1) + │ └── cost: 642.07 + ├── G6: (group-by G11 G8 cols=(1,2)) (group-by G11 G8 cols=(1,2),ordering=+1) + │ └── [limit hint: 10.00] + │ ├── best: (group-by G11="[ordering: +1] [limit hint: 10.00]" G8 cols=(1,2),ordering=+1) + │ └── cost: 641.97 + ├── G7: (scan defg,cols=(1,2)) + │ ├── [ordering: +1] [limit hint: 10.00] + │ │ ├── best: (sort G7) + │ │ └── cost: 1334.20 + │ └── [] + │ ├── best: (scan defg,cols=(1,2)) + │ └── cost: 1094.72 + ├── G8: (aggregations G12) + ├── G9: (index-join G13 defg,cols=(1,2)) + │ ├── [ordering: +1] [limit hint: 10.00] + │ │ ├── best: (index-join G13="[ordering: +1] [limit hint: 10.00]" defg,cols=(1,2)) + │ │ └── cost: 631.44 + │ └── [] + │ ├── best: (index-join G13 defg,cols=(1,2)) + │ └── cost: 7134.44 + ├── G10: (index-join G14 defg,cols=(1,2)) + │ ├── [ordering: +1] [limit hint: 10.00] + │ │ ├── best: (index-join G14="[ordering: +1] [limit hint: 10.00]" defg,cols=(1,2)) + │ │ └── cost: 631.64 + │ └── [] + │ ├── best: (index-join G14 defg,cols=(1,2)) + │ └── cost: 7154.64 + ├── G11: (index-join G15 defg,cols=(1,2)) + │ ├── [ordering: +1] [limit hint: 10.00] + │ │ ├── best: (index-join G15="[ordering: +1] [limit hint: 10.00]" defg,cols=(1,2)) + │ │ └── cost: 631.54 + │ └── [] + │ ├── best: (index-join G15 defg,cols=(1,2)) + │ └── cost: 7144.54 + ├── G12: (count-rows) + ├── G13: (scan defg@dd,cols=(1,5)) + │ ├── [ordering: +1] [limit hint: 10.00] + │ │ ├── best: (scan defg@dd,cols=(1,5)) + │ │ └── cost: 24.42 + │ └── [] + │ ├── best: (scan defg@dd,cols=(1,5)) + │ └── cost: 1064.42 + ├── G14: (scan defg@dfg,cols=(1,5)) + │ ├── [ordering: +1] [limit hint: 10.00] + │ │ ├── best: (scan defg@dfg,cols=(1,5)) + │ │ └── cost: 24.62 + │ └── [] + │ ├── best: (scan defg@dfg,cols=(1,5)) + │ └── cost: 1084.62 + └── G15: (scan defg@df,cols=(1,5)) + ├── [ordering: +1] [limit hint: 10.00] + │ ├── best: (scan defg@df,cols=(1,5)) + │ └── cost: 24.52 + └── [] + ├── best: (scan defg@df,cols=(1,5)) + └── cost: 1074.52 + +# Rule will trigger, but because no order is specified and an index matches, +# this will result in a streaming group by. +opt expect=GenerateLimitedGroupByScans +SELECT d, g, count(*) FROM defg GROUP BY d, g LIMIT 10 +---- +limit + ├── columns: d:1 g:4 count:8!null + ├── cardinality: [0 - 10] + ├── key: (1,4) + ├── fd: (1,4)-->(8) + ├── group-by (streaming) + │ ├── columns: d:1 g:4 count_rows:8!null + │ ├── grouping columns: d:1 g:4 + │ ├── internal-ordering: +4,+1 + │ ├── key: (1,4) + │ ├── fd: (1,4)-->(8) + │ ├── limit hint: 10.00 + │ ├── scan defg@gd + │ │ ├── columns: d:1 g:4 + │ │ ├── ordering: +4,+1 + │ │ └── limit hint: 10.00 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 + +opt expect=GenerateLimitedGroupByScans +SELECT d, e, count(*) FROM defg GROUP BY d, e ORDER BY d LIMIT 10 +---- +limit + ├── columns: d:1 e:2 count:8!null + ├── internal-ordering: +1 + ├── cardinality: [0 - 10] + ├── key: (1,2) + ├── fd: (1,2)-->(8) + ├── ordering: +1 + ├── group-by (partial streaming) + │ ├── columns: d:1 e:2 count_rows:8!null + │ ├── grouping columns: d:1 e:2 + │ ├── key: (1,2) + │ ├── fd: (1,2)-->(8) + │ ├── ordering: +1 + │ ├── limit hint: 10.00 + │ ├── index-join defg + │ │ ├── columns: d:1 e:2 + │ │ ├── ordering: +1 + │ │ ├── limit hint: 10.00 + │ │ └── scan defg@dd + │ │ ├── columns: d:1 rowid:5!null + │ │ ├── key: (5) + │ │ ├── fd: (5)-->(1) + │ │ ├── ordering: +1 + │ │ └── limit hint: 10.00 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 + +# GenerateLimitedGroupByScans will not result in an index scan since the order +# by is a column not in an index. +opt expect-not=GenerateLimitedGroupByScans +SELECT d, e, count(*) FROM defg GROUP BY d, e ORDER BY e LIMIT 10 +---- +top-k + ├── columns: d:1 e:2 count:8!null + ├── internal-ordering: +2 + ├── k: 10 + ├── cardinality: [0 - 10] + ├── key: (1,2) + ├── fd: (1,2)-->(8) + ├── ordering: +2 + └── group-by (hash) + ├── columns: d:1 e:2 count_rows:8!null + ├── grouping columns: d:1 e:2 + ├── key: (1,2) + ├── fd: (1,2)-->(8) + ├── scan defg + │ └── columns: d:1 e:2 + └── aggregations + └── count-rows [as=count_rows:8] + +# GenerateLimitedGroupByScans will be triggered, but not add an index +# scan to the memo since the order by and group by don't share columns. +memo +SELECT d, e, count(*) FROM defg GROUP BY d, e ORDER BY count(*) LIMIT 10 +---- +memo (optimized, ~5KB, required=[presentation: d:1,e:2,count:8] [ordering: +8]) + ├── G1: (limit G2 G3 ordering=+8) (top-k G2 &{10 +8}) + │ ├── [presentation: d:1,e:2,count:8] [ordering: +8] + │ │ ├── best: (top-k G2 &{10 +8}) + │ │ └── cost: 1231.64 + │ └── [] + │ ├── best: (top-k G2 &{10 +8}) + │ └── cost: 1231.64 + ├── G2: (group-by G4 G5 cols=(1,2)) (group-by G4 G5 cols=(1,2),ordering=+1) + │ ├── [ordering: +8] [limit hint: 10.00] + │ │ ├── best: (sort G2) + │ │ └── cost: 1394.38 + │ └── [] + │ ├── best: (group-by G4 G5 cols=(1,2)) + │ └── cost: 1144.90 + ├── G3: (const 10) + ├── G4: (scan defg,cols=(1,2)) + │ ├── [ordering: +1] + │ │ ├── best: (sort G4) + │ │ └── cost: 1334.20 + │ └── [] + │ ├── best: (scan defg,cols=(1,2)) + │ └── cost: 1094.72 + ├── G5: (aggregations G6) + └── G6: (count-rows) + +# Rule does not apply because the grouping columns are not the first columns of +# an index. +opt expect-not=GenerateLimitedGroupByScans +SELECT f, e, count(*) FROM defg GROUP BY f, e LIMIT 10 +---- +limit + ├── columns: f:3 e:2 count:8!null + ├── cardinality: [0 - 10] + ├── key: (2,3) + ├── fd: (2,3)-->(8) + ├── group-by (hash) + │ ├── columns: e:2 f:3 count_rows:8!null + │ ├── grouping columns: e:2 f:3 + │ ├── key: (2,3) + │ ├── fd: (2,3)-->(8) + │ ├── limit hint: 10.00 + │ ├── scan defg + │ │ └── columns: e:2 f:3 + │ └── aggregations + │ └── count-rows [as=count_rows:8] + └── 10 diff --git a/pkg/sql/opt/xform/testdata/rules/join b/pkg/sql/opt/xform/testdata/rules/join index 3aa2d0265b70..e5d534ccd665 100644 --- a/pkg/sql/opt/xform/testdata/rules/join +++ b/pkg/sql/opt/xform/testdata/rules/join @@ -5457,7 +5457,7 @@ GROUP BY n.name, n.geom project ├── columns: name:16!null popn_per_sqkm:22 ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: name:16!null n.geom:17!null sum:21 │ ├── grouping columns: name:16!null n.geom:17!null │ ├── immutable @@ -6055,7 +6055,7 @@ with &1 (q) └── project ├── columns: count:43 count_rows:31!null ├── immutable - ├── group-by + ├── group-by (hash) │ ├── columns: n.boroname:25 count_rows:31!null │ ├── grouping columns: n.boroname:25 │ ├── immutable diff --git a/pkg/sql/opt_exec_factory.go b/pkg/sql/opt_exec_factory.go index 31d939e90220..a1852828e05d 100644 --- a/pkg/sql/opt_exec_factory.go +++ b/pkg/sql/opt_exec_factory.go @@ -462,9 +462,12 @@ func (ef *execFactory) ConstructGroupBy( groupColOrdering colinfo.ColumnOrdering, aggregations []exec.AggInfo, reqOrdering exec.OutputOrdering, + groupingOrderType exec.GroupingOrderType, ) (exec.Node, error) { inputPlan := input.(planNode) inputCols := planColumns(inputPlan) + // TODO(harding): Use groupingOrder to determine when to use a hash + // aggregator. n := &groupNode{ plan: inputPlan, funcs: make([]*aggregateFuncHolder, 0, len(groupCols)+len(aggregations)), diff --git a/pkg/sql/testdata/explain_tree b/pkg/sql/testdata/explain_tree index 33f1cca79a5a..8f1623448f67 100644 --- a/pkg/sql/testdata/explain_tree +++ b/pkg/sql/testdata/explain_tree @@ -94,7 +94,7 @@ network usage: 0 B (0 messages) │ render cid: (cid)[int] │ render sum: (sum)[decimal] │ - └── • group + └── • group (hash) │ columns: (cid int, sum decimal) │ estimated row count: 98 (missing stats) │ aggregate 0: sum(value) @@ -127,7 +127,7 @@ children: - name: render attrs: [] children: - - name: group + - name: group (hash) attrs: - key: group by value: cid From a0481588eb18b238ea166057bf10bda8897f1449 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 14:45:29 +0100 Subject: [PATCH 198/205] kvserver: remove two uses of bootstrapRangeOnly This is a deprecated testing tool that complicates `testContext`. This commit phases it out in two out of three tests that use it. Release note: None --- pkg/kv/kvserver/replica_test.go | 14 +++----------- pkg/kv/kvserver/stats_test.go | 26 -------------------------- 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index 278171eb4413..cbeac162d7d7 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -6373,24 +6373,16 @@ func TestRangeStatsComputation(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) tc := testContext{ - bootstrapMode: bootstrapRangeOnly, + bootstrapMode: bootstrapRangeWithMetadata, } stopper := stop.NewStopper() defer stopper.Stop(context.Background()) tc.Start(t, stopper) ctx := context.Background() - baseStats := initialStats() - // The initial stats contain an empty lease and no prior read summary, but - // there will be an initial nontrivial lease requested with the first write - // below. This lease acquisition will, in turn, create a prior read summary. - baseStats.Add(enginepb.MVCCStats{ - SysBytes: 66, - SysCount: 1, - }) + baseStats := tc.repl.GetMVCCStats() - // Our clock might not be set to zero. - baseStats.LastUpdateNanos = tc.manualClock.UnixNano() + require.NoError(t, verifyRangeStats(tc.engine, tc.repl.RangeID, baseStats)) // Put a value. pArgs := putArgs([]byte("a"), []byte("value1")) diff --git a/pkg/kv/kvserver/stats_test.go b/pkg/kv/kvserver/stats_test.go index f8b81a74f997..a3ee8fee9e8a 100644 --- a/pkg/kv/kvserver/stats_test.go +++ b/pkg/kv/kvserver/stats_test.go @@ -20,34 +20,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/stop" - "github.com/kr/pretty" ) -// initialStats are the stats for a Replica which has been created through -// bootstrapRangeOnly. These stats are not empty because we call -// writeInitialState(). -func initialStats() enginepb.MVCCStats { - return enginepb.MVCCStats{ - SysBytes: 66, - SysCount: 2, - } -} -func TestRangeStatsEmpty(t *testing.T) { - defer leaktest.AfterTest(t)() - defer log.Scope(t).Close(t) - tc := testContext{ - bootstrapMode: bootstrapRangeOnly, - } - stopper := stop.NewStopper() - defer stopper.Stop(context.Background()) - tc.Start(t, stopper) - - ms := tc.repl.GetMVCCStats() - if exp := initialStats(); !reflect.DeepEqual(ms, exp) { - t.Errorf("unexpected stats diff(exp, actual):\n%s", pretty.Diff(exp, ms)) - } -} - func TestRangeStatsInit(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) From 215c46f109dccc8c5592f99dd61bdd2939a79504 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 14:46:15 +0100 Subject: [PATCH 199/205] kvserver: remove TestReplicaLaziness This artificial test wasn't adding anything. Besides, we are not very concerned about whether the raft groups are initialized lazily or not. One could argue that at this point the laziness is a problem since it can make things look good when the system would become unstable once all raft groups are initialized. In practice, the queues go through all replicas every ~10 minutes anyway and after that has happened once, all raft groups will have been instantiated. Since this test uses the deprecated mode `bootstrapRangeOnly`, remove it. Release note: None --- pkg/kv/kvserver/replica_test.go | 44 --------------------------------- 1 file changed, 44 deletions(-) diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index cbeac162d7d7..cb2961cd1564 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -4788,50 +4788,6 @@ func TestErrorsDontCarryWriteTooOldFlag(t *testing.T) { require.False(t, pErr.GetTxn().WriteTooOld) } -// TestReplicaLaziness verifies that Raft Groups are brought up lazily. -func TestReplicaLaziness(t *testing.T) { - defer leaktest.AfterTest(t)() - defer log.Scope(t).Close(t) - // testWithAction is a function that creates an uninitialized Raft group, - // calls the supplied function, and then tests that the Raft group is - // initialized. - testWithAction := func(action func() roachpb.Request) { - tc := testContext{bootstrapMode: bootstrapRangeOnly} - stopper := stop.NewStopper() - defer stopper.Stop(context.Background()) - tc.Start(t, stopper) - - if status := tc.repl.RaftStatus(); status != nil { - t.Fatalf("expected raft group to not be initialized, got RaftStatus() of %v", status) - } - var ba roachpb.BatchRequest - request := action() - ba.Add(request) - if _, pErr := tc.Sender().Send(context.Background(), ba); pErr != nil { - t.Fatalf("unexpected error: %s", pErr) - } - - if tc.repl.RaftStatus() == nil { - t.Fatalf("expected raft group to be initialized") - } - } - - testWithAction(func() roachpb.Request { - put := putArgs(roachpb.Key("a"), []byte("value")) - return &put - }) - - testWithAction(func() roachpb.Request { - get := getArgs(roachpb.Key("a")) - return &get - }) - - testWithAction(func() roachpb.Request { - scan := scanArgs(roachpb.Key("a"), roachpb.Key("b")) - return scan - }) -} - // TestBatchRetryCantCommitIntents tests that transactional retries cannot // commit intents. // It also tests current behavior - that a retried transactional batch can lay From 0896c341f4df342879d2c0a89083ed94de59163c Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 14:51:58 +0100 Subject: [PATCH 200/205] kvserver: remove testContext.bootstrapMode This is the reward for the preceding two commits. Release note: None --- pkg/kv/kvserver/replica_test.go | 180 ++++++++++---------------------- 1 file changed, 58 insertions(+), 122 deletions(-) diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index cb2961cd1564..a7e51b895652 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -107,45 +107,6 @@ func allSpansGuard() *concurrency.Guard { } } -func testRangeDescriptor() *roachpb.RangeDescriptor { - return &roachpb.RangeDescriptor{ - RangeID: 1, - StartKey: roachpb.RKeyMin, - EndKey: roachpb.RKeyMax, - InternalReplicas: []roachpb.ReplicaDescriptor{ - { - ReplicaID: 1, - NodeID: 1, - StoreID: 1, - }, - }, - NextReplicaID: 2, - } -} - -// boostrapMode controls how the first range is created in testContext. -type bootstrapMode int - -const ( - // Use Store.WriteInitialData, which writes the range descriptor and other - // metadata. Most tests should use this mode because it more closely resembles - // the real world. - bootstrapRangeWithMetadata bootstrapMode = iota - // Create a range with NewRange and Store.AddRangeTest. The store's data - // will be persisted but metadata will not. - // - // Tests which run in this mode play fast and loose; they want - // a Replica which doesn't have too many moving parts, but then - // may still exercise a sizable amount of code, be it by accident - // or design. We bootstrap them here with what's absolutely - // necessary to not immediately crash on a Raft command, but - // nothing more. - // If you read this and you're writing a new test, try not to - // use this mode - it's deprecated and tends to get in the way - // of new development. - bootstrapRangeOnly -) - // leaseExpiry returns a duration in nanos after which any range lease the // Replica may hold is expired. It is more precise than LeaseExpiration // in that it returns the minimal duration necessary. @@ -181,14 +142,13 @@ func upToDateRaftStatus(repls []roachpb.ReplicaDescriptor) *raft.Status { // will be used as-is. type testContext struct { testing.TB - transport *RaftTransport - store *Store - repl *Replica - rangeID roachpb.RangeID - gossip *gossip.Gossip - engine storage.Engine - manualClock *hlc.ManualClock - bootstrapMode bootstrapMode + transport *RaftTransport + store *Store + repl *Replica + rangeID roachpb.RangeID + gossip *gossip.Gossip + engine storage.Engine + manualClock *hlc.ManualClock } func (tc *testContext) Clock() *hlc.Clock { @@ -256,80 +216,58 @@ func (tc *testContext) StartWithStoreConfigAndVersion( } stopper.AddCloser(tc.engine) } - if tc.store == nil { - cv := clusterversion.ClusterVersion{Version: bootstrapVersion} - cfg.Gossip = tc.gossip - cfg.Transport = tc.transport - cfg.StorePool = NewTestStorePool(cfg) - // Create a test sender without setting a store. This is to deal with the - // circular dependency between the test sender and the store. The actual - // store will be passed to the sender after it is created and bootstrapped. - factory := &testSenderFactory{} - cfg.DB = kv.NewDB(cfg.AmbientCtx, factory, cfg.Clock, stopper) - - require.NoError(t, WriteClusterVersion(ctx, tc.engine, cv)) - if err := InitEngine(ctx, tc.engine, roachpb.StoreIdent{ - ClusterID: uuid.MakeV4(), - NodeID: 1, - StoreID: 1, - }); err != nil { - t.Fatal(err) - } - if err := clusterversion.Initialize(ctx, cv.Version, &cfg.Settings.SV); err != nil { - t.Fatal(err) - } - tc.store = NewStore(ctx, cfg, tc.engine, &roachpb.NodeDescriptor{NodeID: 1}) - // Now that we have our actual store, monkey patch the factory used in cfg.DB. - factory.setStore(tc.store) - // We created the store without a real KV client, so it can't perform splits - // or merges. - tc.store.splitQueue.SetDisabled(true) - tc.store.mergeQueue.SetDisabled(true) - - if tc.repl == nil && tc.bootstrapMode == bootstrapRangeWithMetadata { - if err := WriteInitialClusterData( - ctx, tc.store.Engine(), - nil, /* initialValues */ - bootstrapVersion, - 1 /* numStores */, nil /* splits */, cfg.Clock.PhysicalNow(), - cfg.TestingKnobs, - ); err != nil { - t.Fatal(err) - } - } - if err := tc.store.Start(ctx, stopper); err != nil { - t.Fatal(err) - } - tc.store.WaitForInit() + require.Nil(t, tc.store) + cv := clusterversion.ClusterVersion{Version: bootstrapVersion} + cfg.Gossip = tc.gossip + cfg.Transport = tc.transport + cfg.StorePool = NewTestStorePool(cfg) + // Create a test sender without setting a store. This is to deal with the + // circular dependency between the test sender and the store. The actual + // store will be passed to the sender after it is created and bootstrapped. + factory := &testSenderFactory{} + cfg.DB = kv.NewDB(cfg.AmbientCtx, factory, cfg.Clock, stopper) + + require.NoError(t, WriteClusterVersion(ctx, tc.engine, cv)) + if err := InitEngine(ctx, tc.engine, roachpb.StoreIdent{ + ClusterID: uuid.MakeV4(), + NodeID: 1, + StoreID: 1, + }); err != nil { + t.Fatal(err) } - - realRange := tc.repl == nil - - if realRange { - if tc.bootstrapMode == bootstrapRangeOnly { - testDesc := testRangeDescriptor() - if err := stateloader.WriteInitialRangeState( - ctx, tc.store.Engine(), *testDesc, roachpb.Version{}, - ); err != nil { - t.Fatal(err) - } - repl, err := newReplica(ctx, testDesc, tc.store, 1) - if err != nil { - t.Fatal(err) - } - if err := tc.store.AddReplica(repl); err != nil { - t.Fatal(err) - } - } - var err error - tc.repl, err = tc.store.GetReplica(1) - if err != nil { - t.Fatal(err) - } - tc.rangeID = tc.repl.RangeID + if err := clusterversion.Initialize(ctx, cv.Version, &cfg.Settings.SV); err != nil { + t.Fatal(err) + } + tc.store = NewStore(ctx, cfg, tc.engine, &roachpb.NodeDescriptor{NodeID: 1}) + // Now that we have our actual store, monkey patch the factory used in cfg.DB. + factory.setStore(tc.store) + // We created the store without a real KV client, so it can't perform splits + // or merges. + tc.store.splitQueue.SetDisabled(true) + tc.store.mergeQueue.SetDisabled(true) + + require.Nil(t, tc.repl) + if err := WriteInitialClusterData( + ctx, tc.store.Engine(), + nil, /* initialValues */ + bootstrapVersion, + 1 /* numStores */, nil /* splits */, cfg.Clock.PhysicalNow(), + cfg.TestingKnobs, + ); err != nil { + t.Fatal(err) } + if err := tc.store.Start(ctx, stopper); err != nil { + t.Fatal(err) + } + tc.store.WaitForInit() + var err error + tc.repl, err = tc.store.GetReplica(1) + if err != nil { + t.Fatal(err) + } + tc.rangeID = tc.repl.RangeID - if err := tc.initConfigs(realRange, t); err != nil { + if err := tc.initConfigs(t); err != nil { t.Fatal(err) } } @@ -365,7 +303,7 @@ func (tc *testContext) SendWrapped(args roachpb.Request) (roachpb.Response, *roa } // initConfigs creates default configuration entries. -func (tc *testContext) initConfigs(realRange bool, t testing.TB) error { +func (tc *testContext) initConfigs(t testing.TB) error { // Put an empty system config into gossip so that gossip callbacks get // run. We're using a fake config, but it's hooked into SystemConfig. if err := tc.gossip.AddInfoProto(gossip.KeySystemConfig, @@ -6328,9 +6266,7 @@ func verifyRangeStats( func TestRangeStatsComputation(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) - tc := testContext{ - bootstrapMode: bootstrapRangeWithMetadata, - } + tc := testContext{} stopper := stop.NewStopper() defer stopper.Stop(context.Background()) tc.Start(t, stopper) From 8ce08d89e42bb846ce08fbb4b333ddf379a3ecc2 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 14:59:38 +0100 Subject: [PATCH 201/205] kvserver: drop useless on-disk engine in a test Release note: None --- pkg/kv/kvserver/raft_log_queue_test.go | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/pkg/kv/kvserver/raft_log_queue_test.go b/pkg/kv/kvserver/raft_log_queue_test.go index fb5ca76e1221..0fdeace53ea3 100644 --- a/pkg/kv/kvserver/raft_log_queue_test.go +++ b/pkg/kv/kvserver/raft_log_queue_test.go @@ -22,7 +22,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/skip" "github.com/cockroachdb/cockroach/pkg/util/hlc" @@ -857,20 +856,7 @@ func TestTruncateLogRecompute(t *testing.T) { defer log.Scope(t).Close(t) ctx := context.Background() - dir, cleanup := testutils.TempDir(t) - defer cleanup() - - eng, err := storage.Open(ctx, - storage.Filesystem(dir), - storage.CacheSize(1<<20 /* 1 MiB */)) - if err != nil { - t.Fatal(err) - } - defer eng.Close() - - tc := testContext{ - engine: eng, - } + tc := testContext{} cfg := TestStoreConfig(nil) cfg.TestingKnobs.DisableRaftLogQueue = true stopper := stop.NewStopper() From 8be243cb75ebe8ffb4b7e2a5a468bbf6cf2d0108 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 14:59:56 +0100 Subject: [PATCH 202/205] kvserver: simplify testContext It supports pre-populating fields where this is never used, and if it were used who knows how well it would work. Better remove it now. Release note: None --- pkg/kv/kvserver/replica_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index a7e51b895652..a9c5bf63b430 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -191,13 +191,12 @@ func (tc *testContext) StartWithStoreConfigAndVersion( Settings: cfg.Settings, }) grpcServer := rpc.NewServer(rpcContext) // never started - if tc.gossip == nil { - tc.gossip = gossip.NewTest(1, rpcContext, grpcServer, stopper, metric.NewRegistry(), zonepb.DefaultZoneConfigRef()) - } - if tc.transport == nil { - dialer := nodedialer.New(rpcContext, gossip.AddressResolver(tc.gossip)) - tc.transport = NewRaftTransport(cfg.AmbientCtx, cfg.Settings, dialer, grpcServer, stopper) - } + require.Nil(t, tc.gossip) + tc.gossip = gossip.NewTest(1, rpcContext, grpcServer, stopper, metric.NewRegistry(), zonepb.DefaultZoneConfigRef()) + require.Nil(t, tc.transport) + dialer := nodedialer.New(rpcContext, gossip.AddressResolver(tc.gossip)) + tc.transport = NewRaftTransport(cfg.AmbientCtx, cfg.Settings, dialer, grpcServer, stopper) + if tc.engine == nil { disableSeparatedIntents := !cfg.Settings.Version.ActiveVersionOrEmpty(context.Background()).IsActive( From b944cffbc598191aa4ee4f0d038083467fae1c81 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 16:31:04 +0100 Subject: [PATCH 203/205] kvserver: resuscitate and fix up TestRaftSSTableSideloading This test originally verified that SSTs that would be sent as part of the raft log in a raft snapshot would be correctly inlined. But it has been fooling itself for a long time, since snapshots haven't had a log (in this test) for 1-2 years at this point. The test ended up using a for loop that never went anywhere and a resulting index of zero and just happened to never return a failing result. Replace it with a more useful test verifying that we can propose and read back a sideloaded proposal, the main bit of logic I wanted to preserve is a test for an idiosyncracy of `entries()` when no sideloaded storage is provided, in which case it can't add to the raft entry cache for fear of putting a non-inlined entry there. As a little additional upshot, there was no reason for this test to use an on-disk engine (anymore), since the sideloaded storage is backed by the pebble instance as well. Release note: None --- pkg/kv/kvserver/replica_sideload_test.go | 94 ++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/pkg/kv/kvserver/replica_sideload_test.go b/pkg/kv/kvserver/replica_sideload_test.go index 8eaee788fcfe..78a23fcc408c 100644 --- a/pkg/kv/kvserver/replica_sideload_test.go +++ b/pkg/kv/kvserver/replica_sideload_test.go @@ -27,6 +27,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/raftentry" + "github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/storage" @@ -678,6 +679,99 @@ func newOnDiskEngine(t *testing.T) (func(), storage.Engine) { return cleanup, eng } +// This test verifies that sideloaded proposals are +// inlined correctly and can be read back. +func TestRaftSSTableSideloading(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + defer SetMockAddSSTable()() + + ctx := context.Background() + tc := testContext{} + + stopper := stop.NewStopper() + defer stopper.Stop(ctx) + tc.Start(t, stopper) + + // Disable log truncation to make sure our proposal stays in the log. + tc.store.SetRaftLogQueueActive(false) + + var ba roachpb.BatchRequest + ba.RangeID = tc.repl.RangeID + + // Put a sideloaded proposal on the Range. + key, val := "don't", "care" + origSSTData, _ := MakeSSTable(key, val, hlc.Timestamp{}.Add(0, 1)) + { + + var addReq roachpb.AddSSTableRequest + addReq.Data = origSSTData + addReq.Key = roachpb.Key(key) + addReq.EndKey = addReq.Key.Next() + ba.Add(&addReq) + + _, pErr := tc.store.Send(ctx, ba) + if pErr != nil { + t.Fatal(pErr) + } + } + + // Check the `entries()` method which has special handling to accommodate + // `term()`: when an empty sideload storage is passed in, `entries()` should + // not inline, and in turn also not populate the entries cache (since its + // contents must always be fully inlined). + tc.repl.raftMu.Lock() + defer tc.repl.raftMu.Unlock() + tc.repl.mu.Lock() + defer tc.repl.mu.Unlock() + testutils.RunTrueAndFalse(t, "withSS", func(t *testing.T, withSS bool) { + rsl := stateloader.Make(tc.repl.RangeID) + lo := tc.repl.mu.state.TruncatedState.Index + 1 + hi := tc.repl.mu.lastIndex + 1 + + var ss SideloadStorage + if withSS { + ss = tc.repl.raftMu.sideloaded + } + + tc.store.raftEntryCache.Clear(tc.repl.RangeID, hi) + ents, err := entries( + ctx, rsl, tc.store.Engine(), tc.repl.RangeID, tc.store.raftEntryCache, + ss, lo, hi, math.MaxUint64, + ) + require.NoError(t, err) + require.Len(t, ents, int(hi-lo)) + + // Raft entry cache only gets populated when sideloaded storage provided. + _, okLo := tc.store.raftEntryCache.Get(tc.repl.RangeID, lo) + _, okHi := tc.store.raftEntryCache.Get(tc.repl.RangeID, hi-1) + if withSS { + require.True(t, okLo) + require.True(t, okHi) + } else { + require.False(t, okLo) + require.False(t, okHi) + } + + // The rest of the test is the same in both cases. We find the sideloaded entry + // and check the sideloaded storage for the payload. + var idx int + for idx = 0; idx < len(ents); idx++ { + // Get the SST back from the raft log. + if !sniffSideloadedRaftCommand(ents[idx].Data) { + continue + } + ent, err := maybeInlineSideloadedRaftCommand(ctx, tc.repl.RangeID, ents[idx], tc.repl.raftMu.sideloaded, tc.store.raftEntryCache) + require.NoError(t, err) + sst, err := tc.repl.raftMu.sideloaded.Get(ctx, ent.Index, ent.Term) + require.NoError(t, err) + require.Equal(t, origSSTData, sst) + break + } + require.Less(t, idx, len(ents)) // there was an SST + }) +} + func TestRaftSSTableSideloadingTruncation(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) From 4b796ba2ab789368b59150c140adf731ce0b37a4 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 16:37:08 +0100 Subject: [PATCH 204/205] kvserver: prevent pre-populating `testContext.eng` This functionality was no longer used and is now removed. Release note: None --- pkg/kv/kvserver/replica_test.go | 36 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index a9c5bf63b430..6646c56618f5 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -197,24 +197,23 @@ func (tc *testContext) StartWithStoreConfigAndVersion( dialer := nodedialer.New(rpcContext, gossip.AddressResolver(tc.gossip)) tc.transport = NewRaftTransport(cfg.AmbientCtx, cfg.Settings, dialer, grpcServer, stopper) - if tc.engine == nil { - disableSeparatedIntents := - !cfg.Settings.Version.ActiveVersionOrEmpty(context.Background()).IsActive( - clusterversion.PostSeparatedIntentsMigration) - log.Infof(context.Background(), "engine creation is randomly setting disableSeparatedIntents: %t", - disableSeparatedIntents) - var err error - tc.engine, err = storage.Open(context.Background(), - storage.InMemory(), - storage.Attributes(roachpb.Attributes{Attrs: []string{"dc1", "mem"}}), - storage.MaxSize(1<<20), - storage.SetSeparatedIntents(disableSeparatedIntents), - storage.Settings(cfg.Settings)) - if err != nil { - t.Fatal(err) - } - stopper.AddCloser(tc.engine) - } + require.Nil(t, tc.engine) + disableSeparatedIntents := + !cfg.Settings.Version.ActiveVersionOrEmpty(context.Background()).IsActive( + clusterversion.PostSeparatedIntentsMigration) + log.Infof(context.Background(), "engine creation is randomly setting disableSeparatedIntents: %t", + disableSeparatedIntents) + + var err error + tc.engine, err = storage.Open(context.Background(), + storage.InMemory(), + storage.Attributes(roachpb.Attributes{Attrs: []string{"dc1", "mem"}}), + storage.MaxSize(1<<20), + storage.SetSeparatedIntents(disableSeparatedIntents), + storage.Settings(cfg.Settings)) + require.NoError(t, err) + stopper.AddCloser(tc.engine) + require.Nil(t, tc.store) cv := clusterversion.ClusterVersion{Version: bootstrapVersion} cfg.Gossip = tc.gossip @@ -259,7 +258,6 @@ func (tc *testContext) StartWithStoreConfigAndVersion( t.Fatal(err) } tc.store.WaitForInit() - var err error tc.repl, err = tc.store.GetReplica(1) if err != nil { t.Fatal(err) From 1545843aab60b1ac5b634faa6b0996ab262ce8fd Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Thu, 4 Nov 2021 10:43:27 +0100 Subject: [PATCH 205/205] roachtest: add ctx timeout in multitenant-upgrade This would have turned the timeout in #72420 into a faster failure. Release note: None --- pkg/cmd/roachtest/tests/multitenant_upgrade.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/cmd/roachtest/tests/multitenant_upgrade.go b/pkg/cmd/roachtest/tests/multitenant_upgrade.go index 98eb9f2a8bec..b6adfe6e56b6 100644 --- a/pkg/cmd/roachtest/tests/multitenant_upgrade.go +++ b/pkg/cmd/roachtest/tests/multitenant_upgrade.go @@ -126,6 +126,8 @@ func (tn *tenantNode) start(ctx context.Context, t test.Test, c cluster.Cluster, return err } defer db.Close() + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() _, err = db.ExecContext(ctx, `SELECT 1`) return err }); err != nil {
SettingTypeDefaultDescription
admission.kv.enabledbooleanfalsewhen true, work performed by the KV layer is subject to admission control
admission.sql_kv_response.enabledbooleanfalsewhen true, work performed by the SQL layer when receiving a KV response is subject to admission control
admission.sql_sql_response.enabledbooleanfalsewhen true, work performed by the SQL layer when receiving a DistSQL response is subject to admission control
admission.kv.enabledbooleantruewhen true, work performed by the KV layer is subject to admission control
admission.sql_kv_response.enabledbooleantruewhen true, work performed by the SQL layer when receiving a KV response is subject to admission control
admission.sql_sql_response.enabledbooleantruewhen true, work performed by the SQL layer when receiving a DistSQL response is subject to admission control
bulkio.backup.file_sizebyte size128 MiBtarget size for individual data files produced during BACKUP
bulkio.backup.read_timeoutduration5m0samount of time after which a read attempt is considered timed out, which causes the backup to fail
bulkio.backup.read_with_priority_afterduration1m0samount of time since the read-as-of time above which a BACKUP should use priority when retrying reads
crdb_internal.range_stats(key: bytes) → jsonb

This function is used to retrieve range statistics information as a JSON object.

crdb_internal.reset_index_usage_stats() → bool

This function is used to clear the collected index usage statistics.

+
crdb_internal.reset_sql_stats() → bool

This function is used to clear the collected SQL statistics.

crdb_internal.round_decimal_values(val: decimal, scale: int) → decimal

This function is used internally to round decimal values during mutations.

diff --git a/pkg/ccl/serverccl/tenant_status_test.go b/pkg/ccl/serverccl/tenant_status_test.go index 0c1ec856332c..70b9fb566268 100644 --- a/pkg/ccl/serverccl/tenant_status_test.go +++ b/pkg/ccl/serverccl/tenant_status_test.go @@ -300,6 +300,112 @@ func TestResetSQLStatsRPCForTenant(t *testing.T) { } } +func TestResetIndexUsageStatsRPCForTenant(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + skip.UnderStressRace(t, "expensive tests") + + ctx := context.Background() + + statsIngestionCb, statsIngestionNotifier := idxusage.CreateIndexStatsIngestedCallbackForTest() + + knobs := tests.CreateTestingKnobs() + knobs.IndexUsageStatsKnobs = &idxusage.TestingKnobs{ + OnIndexUsageStatsProcessedCallback: statsIngestionCb, + } + + testHelper := newTestTenantHelper(t, 3 /* tenantClusterSize */, knobs) + defer testHelper.cleanup(ctx, t) + + testingCluster := testHelper.testCluster() + controlCluster := testHelper.controlCluster() + + for _, cluster := range []tenantCluster{testingCluster, controlCluster} { + // Create tables and insert data. + cluster.tenantConn(0).Exec(t, ` +CREATE TABLE test ( + k INT PRIMARY KEY, + a INT, + b INT, + INDEX(a) +) +`) + + cluster.tenantConn(0).Exec(t, ` +INSERT INTO test +VALUES (1, 10, 100), (2, 20, 200), (3, 30, 300) +`) + + // Record scan on primary index. + cluster.tenantConn(0).Exec(t, "SELECT * FROM test") + + // Record scan on secondary index. + cluster.tenantConn(1).Exec(t, "SELECT * FROM test@test_a_idx") + testTableIDStr := cluster.tenantConn(2).QueryStr(t, "SELECT 'test'::regclass::oid")[0][0] + testTableID, err := strconv.Atoi(testTableIDStr) + require.NoError(t, err) + + // Wait for the stats to be ingested. + require.NoError(t, + idxusage.WaitForIndexStatsIngestionForTest(statsIngestionNotifier, map[roachpb.IndexUsageKey]struct{}{ + { + TableID: roachpb.TableID(testTableID), + IndexID: 1, + }: {}, + { + TableID: roachpb.TableID(testTableID), + IndexID: 2, + }: {}, + }, 2 /* expectedEventCnt*/, 5*time.Second /* timeout */), + ) + + query := ` +SELECT + table_id, + index_id, + total_reads, + extract_duration('second', now() - last_read) < 5 +FROM + crdb_internal.index_usage_statistics +WHERE + table_id = $1 +` + // Assert index usage data was inserted. + actual := cluster.tenantConn(2).QueryStr(t, query, testTableID) + expected := [][]string{ + {testTableIDStr, "1", "1", "true"}, + {testTableIDStr, "2", "1", "true"}, + } + require.Equal(t, expected, actual) + } + + // Reset index usage stats. + status := testingCluster.tenantStatusSrv(1 /* idx */) + _, err := status.ResetIndexUsageStats(ctx, &serverpb.ResetIndexUsageStatsRequest{}) + require.NoError(t, err) + + resp, err := status.IndexUsageStatistics(ctx, &serverpb.IndexUsageStatisticsRequest{}) + require.NoError(t, err) + + // Require index usage metrics to be reset. + for _, stats := range resp.Statistics { + require.Equal(t, uint64(0), stats.Stats.TotalReadCount) + require.Equal(t, time.Time{}, stats.Stats.LastRead) + } + + // Ensure tenant data isolation. + status = controlCluster.tenantStatusSrv(1 /* idx */) + resp, err = status.IndexUsageStatistics(ctx, &serverpb.IndexUsageStatisticsRequest{}) + require.NoError(t, err) + + // Require index usage metrics to not be reset. + for _, stats := range resp.Statistics { + require.NotEqual(t, uint64(0), stats.Stats.TotalReadCount) + require.NotEqual(t, time.Time{}, stats.Stats.LastRead) + } +} + func ensureExpectedStmtFingerprintExistsInRPCResponse( t *testing.T, expectedStmts []string, resp *serverpb.StatementsResponse, clusterType string, ) { diff --git a/pkg/server/index_usage_stats.go b/pkg/server/index_usage_stats.go index 8e990bc54f91..e3f74c212518 100644 --- a/pkg/server/index_usage_stats.go +++ b/pkg/server/index_usage_stats.go @@ -93,6 +93,9 @@ func (s *statusServer) IndexUsageStatistics( return nil, err } + // Append last reset time. + resp.LastReset = s.sqlServer.pgServer.SQLServer.GetLocalIndexStatistics().GetLastReset() + return resp, nil } @@ -108,5 +111,68 @@ func indexUsageStatsLocal( }); err != nil { return nil, err } + // Append last reset time. + resp.LastReset = idxUsageStats.GetLastReset() + return resp, nil +} + +func (s *statusServer) ResetIndexUsageStats( + ctx context.Context, req *serverpb.ResetIndexUsageStatsRequest, +) (*serverpb.ResetIndexUsageStatsResponse, error) { + ctx = propagateGatewayMetadata(ctx) + ctx = s.AnnotateCtx(ctx) + + if _, err := s.privilegeChecker.requireAdminUser(ctx); err != nil { + return nil, err + } + + localReq := &serverpb.ResetIndexUsageStatsRequest{ + NodeID: "local", + } + resp := &serverpb.ResetIndexUsageStatsResponse{} + + if len(req.NodeID) > 0 { + requestedNodeID, local, err := s.parseNodeID(req.NodeID) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + if local { + s.sqlServer.pgServer.SQLServer.GetLocalIndexStatistics().Reset() + return resp, nil + } + + statusClient, err := s.dialNode(ctx, requestedNodeID) + if err != nil { + return nil, err + } + + return statusClient.ResetIndexUsageStats(ctx, localReq) + } + + dialFn := func(ctx context.Context, nodeID roachpb.NodeID) (interface{}, error) { + client, err := s.dialNode(ctx, nodeID) + return client, err + } + + resetIndexUsageStats := func(ctx context.Context, client interface{}, _ roachpb.NodeID) (interface{}, error) { + statusClient := client.(serverpb.StatusClient) + return statusClient.ResetIndexUsageStats(ctx, localReq) + } + + aggFn := func(_ roachpb.NodeID, nodeResp interface{}) { + // Nothing to do here. + } + + var combinedError error + errFn := func(_ roachpb.NodeID, nodeFnError error) { + combinedError = errors.CombineErrors(combinedError, nodeFnError) + } + + if err := s.iterateNodes(ctx, + fmt.Sprintf("Resetting index usage stats for node %s", req.NodeID), + dialFn, resetIndexUsageStats, aggFn, errFn); err != nil { + return nil, err + } + return resp, nil } diff --git a/pkg/server/serverpb/status.go b/pkg/server/serverpb/status.go index 1a48f6ce120c..6adcbc7c53ec 100644 --- a/pkg/server/serverpb/status.go +++ b/pkg/server/serverpb/status.go @@ -32,6 +32,7 @@ type SQLStatusServer interface { ListLocalDistSQLFlows(context.Context, *ListDistSQLFlowsRequest) (*ListDistSQLFlowsResponse, error) Profile(ctx context.Context, request *ProfileRequest) (*JSONResponse, error) IndexUsageStatistics(context.Context, *IndexUsageStatisticsRequest) (*IndexUsageStatisticsResponse, error) + ResetIndexUsageStats(context.Context, *ResetIndexUsageStatsRequest) (*ResetIndexUsageStatsResponse, error) } // OptionalNodesStatusServer is a StatusServer that is only optionally present diff --git a/pkg/server/serverpb/status.pb.go b/pkg/server/serverpb/status.pb.go index 6c72829f2a88..cb20682aca5f 100644 --- a/pkg/server/serverpb/status.pb.go +++ b/pkg/server/serverpb/status.pb.go @@ -4631,6 +4631,8 @@ var xxx_messageInfo_IndexUsageStatisticsRequest proto.InternalMessageInfo // Response object returned by IndexUsageStatistics. type IndexUsageStatisticsResponse struct { Statistics []roachpb.CollectedIndexUsageStatistics `protobuf:"bytes,1,rep,name=statistics,proto3" json:"statistics"` + // Timestamp of the last index usage stats reset. + LastReset time.Time `protobuf:"bytes,3,opt,name=last_reset,json=lastReset,proto3,stdtime" json:"last_reset"` } func (m *IndexUsageStatisticsResponse) Reset() { *m = IndexUsageStatisticsResponse{} } @@ -4662,6 +4664,73 @@ func (m *IndexUsageStatisticsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_IndexUsageStatisticsResponse proto.InternalMessageInfo +// Request object for issuing a index usage stats reset request. +type ResetIndexUsageStatsRequest struct { + NodeID string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` +} + +func (m *ResetIndexUsageStatsRequest) Reset() { *m = ResetIndexUsageStatsRequest{} } +func (m *ResetIndexUsageStatsRequest) String() string { return proto.CompactTextString(m) } +func (*ResetIndexUsageStatsRequest) ProtoMessage() {} +func (*ResetIndexUsageStatsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_07f09f8b91174efc, []int{102} +} +func (m *ResetIndexUsageStatsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResetIndexUsageStatsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ResetIndexUsageStatsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResetIndexUsageStatsRequest.Merge(m, src) +} +func (m *ResetIndexUsageStatsRequest) XXX_Size() int { + return m.Size() +} +func (m *ResetIndexUsageStatsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ResetIndexUsageStatsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ResetIndexUsageStatsRequest proto.InternalMessageInfo + +// Response object returned by ResetIndexUsageStatsRequest. +type ResetIndexUsageStatsResponse struct { +} + +func (m *ResetIndexUsageStatsResponse) Reset() { *m = ResetIndexUsageStatsResponse{} } +func (m *ResetIndexUsageStatsResponse) String() string { return proto.CompactTextString(m) } +func (*ResetIndexUsageStatsResponse) ProtoMessage() {} +func (*ResetIndexUsageStatsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_07f09f8b91174efc, []int{103} +} +func (m *ResetIndexUsageStatsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResetIndexUsageStatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ResetIndexUsageStatsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResetIndexUsageStatsResponse.Merge(m, src) +} +func (m *ResetIndexUsageStatsResponse) XXX_Size() int { + return m.Size() +} +func (m *ResetIndexUsageStatsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ResetIndexUsageStatsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ResetIndexUsageStatsResponse proto.InternalMessageInfo + func init() { proto.RegisterEnum("cockroach.server.serverpb.StacksType", StacksType_name, StacksType_value) proto.RegisterEnum("cockroach.server.serverpb.FileType", FileType_name, FileType_value) @@ -4798,516 +4867,521 @@ func init() { proto.RegisterType((*ResetSQLStatsResponse)(nil), "cockroach.server.serverpb.ResetSQLStatsResponse") proto.RegisterType((*IndexUsageStatisticsRequest)(nil), "cockroach.server.serverpb.IndexUsageStatisticsRequest") proto.RegisterType((*IndexUsageStatisticsResponse)(nil), "cockroach.server.serverpb.IndexUsageStatisticsResponse") + proto.RegisterType((*ResetIndexUsageStatsRequest)(nil), "cockroach.server.serverpb.ResetIndexUsageStatsRequest") + proto.RegisterType((*ResetIndexUsageStatsResponse)(nil), "cockroach.server.serverpb.ResetIndexUsageStatsResponse") } func init() { proto.RegisterFile("server/serverpb/status.proto", fileDescriptor_07f09f8b91174efc) } var fileDescriptor_07f09f8b91174efc = []byte{ - // 8061 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x7d, 0x6d, 0x6c, 0x1b, 0x49, - 0x96, 0x98, 0x5b, 0x24, 0x25, 0xf2, 0x51, 0x1f, 0x74, 0xe9, 0xc3, 0x34, 0xed, 0x91, 0x3c, 0x3d, - 0x33, 0x1e, 0xdb, 0x33, 0x43, 0xcd, 0xd8, 0xe3, 0xf9, 0xda, 0xdd, 0xb9, 0xd5, 0xd7, 0xd8, 0xb2, - 0x65, 0xd9, 0x6e, 0x49, 0x3b, 0x8b, 0xd9, 0xcb, 0x31, 0x2d, 0x76, 0x89, 0x6e, 0xab, 0xd9, 0x4d, - 0x75, 0x37, 0x35, 0xe2, 0x4e, 0x66, 0x6f, 0xb3, 0xf9, 0xb8, 0xcd, 0xdd, 0x65, 0xef, 0xf6, 0x92, - 0x0d, 0x36, 0x48, 0x90, 0x5c, 0xf6, 0xc7, 0x5d, 0x70, 0x41, 0x0e, 0x77, 0x08, 0x12, 0x24, 0x3f, - 0xf2, 0x81, 0xfc, 0xb8, 0xec, 0x21, 0x40, 0xb0, 0x40, 0xf2, 0x63, 0x91, 0x20, 0xba, 0x44, 0x1b, - 0x24, 0x41, 0xf2, 0x2b, 0x48, 0x7e, 0x2d, 0x70, 0x41, 0x50, 0xaf, 0xaa, 0x9a, 0xd5, 0x24, 0xd5, - 0x24, 0xed, 0xf1, 0xec, 0x01, 0xf9, 0x31, 0x63, 0xf6, 0xab, 0x57, 0xaf, 0x5e, 0xbd, 0x7a, 0xf5, - 0xea, 0x55, 0xbd, 0x57, 0x25, 0xb8, 0x18, 0x50, 0xff, 0x90, 0xfa, 0x8b, 0xfc, 0x9f, 0xc6, 0xee, - 0x62, 0x10, 0x9a, 0x61, 0x33, 0x28, 0x37, 0x7c, 0x2f, 0xf4, 0xc8, 0xf9, 0xaa, 0x57, 0xdd, 0xf7, - 0x3d, 0xb3, 0xfa, 0xa8, 0xcc, 0x11, 0xca, 0x12, 0xaf, 0x54, 0xd8, 0x6d, 0xda, 0x8e, 0xb5, 0x68, - 0xbb, 0x7b, 0x1e, 0x47, 0x2e, 0x4d, 0xd7, 0xbc, 0x20, 0xb0, 0x1b, 0x8b, 0xfc, 0x1f, 0x01, 0x9c, - 0x7b, 0xec, 0xed, 0x06, 0x8b, 0xec, 0x7f, 0x8d, 0x5d, 0xfc, 0x47, 0xc0, 0xcf, 0x21, 0xd5, 0xc6, - 0xee, 0xa2, 0xd9, 0x68, 0x54, 0x58, 0x9b, 0xb2, 0x80, 0xc8, 0x02, 0xcb, 0x0c, 0x4d, 0x01, 0x5b, - 0x90, 0x30, 0xdb, 0xb5, 0xe8, 0x51, 0xa5, 0x19, 0x98, 0x35, 0x1a, 0xab, 0x34, 0x27, 0x11, 0xea, - 0x34, 0x34, 0x95, 0x8a, 0xd7, 0x45, 0xef, 0x2c, 0xdb, 0xac, 0xb9, 0x5e, 0x10, 0xda, 0xd5, 0x40, - 0xfd, 0xcd, 0x5a, 0x69, 0x7f, 0x89, 0x3a, 0x2f, 0x48, 0x89, 0xa0, 0x20, 0xc4, 0x3f, 0x1d, 0x82, - 0x29, 0x3d, 0x1f, 0x1c, 0x38, 0x8b, 0x55, 0xcf, 0x0d, 0xa9, 0x1b, 0xda, 0x9e, 0xdb, 0xd8, 0x55, - 0x3e, 0x04, 0xca, 0x79, 0x86, 0x42, 0x8f, 0x68, 0xd5, 0x76, 0xf7, 0x7c, 0x13, 0x7b, 0x6a, 0x8b, - 0xa2, 0xe7, 0x82, 0xd0, 0xf3, 0xcd, 0x1a, 0x5d, 0xa4, 0x6e, 0xcd, 0x76, 0x69, 0x63, 0x57, 0xfc, - 0x10, 0xc5, 0x17, 0xba, 0x8a, 0xeb, 0x87, 0xd5, 0xaa, 0x28, 0x9c, 0xef, 0x2a, 0xf4, 0xbd, 0xea, - 0x7e, 0x60, 0xed, 0x8a, 0xf2, 0xab, 0xfb, 0x87, 0x8b, 0xfb, 0x87, 0xa2, 0x0f, 0xf2, 0x47, 0x63, - 0x77, 0xd1, 0xa1, 0x66, 0xc0, 0x85, 0x16, 0x75, 0x42, 0x3f, 0x05, 0x95, 0x21, 0x49, 0x5e, 0xca, - 0x2a, 0x8e, 0x63, 0x1f, 0x52, 0x97, 0x06, 0x41, 0xf4, 0x83, 0xd1, 0x15, 0x3f, 0x05, 0x7e, 0xb1, - 0x19, 0xda, 0xce, 0xa2, 0xe3, 0xd5, 0xd8, 0x7f, 0xac, 0xd8, 0xab, 0x89, 0x92, 0x12, 0x96, 0x34, - 0x5d, 0x9f, 0x06, 0x9e, 0x73, 0x48, 0xad, 0x8a, 0x69, 0x59, 0x7e, 0xac, 0xd6, 0x23, 0xa7, 0xba, - 0x18, 0xda, 0x75, 0x1a, 0x84, 0x66, 0x5d, 0xea, 0xcf, 0x3c, 0x0d, 0xab, 0xd6, 0xa2, 0x6f, 0xee, - 0x85, 0x8b, 0x87, 0x37, 0xf0, 0x5f, 0xd6, 0x63, 0x73, 0x2f, 0x14, 0xe5, 0x33, 0x35, 0xaf, 0xe6, - 0xe1, 0xcf, 0x45, 0xf6, 0x4b, 0x40, 0x2f, 0xd6, 0x3c, 0xaf, 0xe6, 0x50, 0x26, 0xf2, 0x45, 0xd3, - 0x75, 0xbd, 0xd0, 0x64, 0x03, 0x23, 0x79, 0x5c, 0x10, 0xa5, 0xf8, 0xb5, 0xdb, 0xdc, 0xeb, 0x6e, - 0xb4, 0x13, 0xc1, 0x6a, 0xfa, 0x66, 0x7b, 0x68, 0xf5, 0x32, 0x4c, 0xaf, 0x50, 0x3f, 0xb4, 0xf7, - 0xec, 0xaa, 0x19, 0xd2, 0xc0, 0xa0, 0x07, 0x4d, 0x1a, 0x84, 0xe4, 0x1c, 0x8c, 0xb9, 0x9e, 0x45, - 0x2b, 0xb6, 0x55, 0xd4, 0x2e, 0x69, 0x57, 0x72, 0xc6, 0x28, 0xfb, 0x5c, 0xb7, 0xf4, 0x3f, 0x4e, - 0x03, 0x51, 0x2a, 0xac, 0xd2, 0xd0, 0xb4, 0x9d, 0x80, 0x3c, 0x84, 0x74, 0xd8, 0x6a, 0x50, 0x44, - 0x9e, 0xbc, 0xfe, 0xa5, 0xf2, 0xa9, 0x93, 0xad, 0xdc, 0x5d, 0x59, 0x05, 0x6d, 0xb7, 0x1a, 0xd4, - 0x40, 0x52, 0xe4, 0x05, 0x98, 0xa0, 0xbe, 0xef, 0xf9, 0x95, 0x3a, 0x0d, 0xd8, 0x2c, 0x29, 0x8e, - 0x20, 0x23, 0xe3, 0x08, 0xbc, 0xc7, 0x61, 0x84, 0x40, 0x9a, 0xcd, 0x91, 0x62, 0xea, 0x92, 0x76, - 0x65, 0xdc, 0xc0, 0xdf, 0xc4, 0x80, 0xd1, 0x3d, 0x9b, 0x3a, 0x56, 0x50, 0x4c, 0x5f, 0x4a, 0x5d, - 0xc9, 0x5f, 0x7f, 0x73, 0x38, 0x6e, 0x3e, 0xc0, 0xba, 0xcb, 0xe9, 0x1f, 0x1e, 0x2f, 0x9c, 0x31, - 0x04, 0xa5, 0xd2, 0x3f, 0x18, 0x81, 0x51, 0x5e, 0x40, 0xe6, 0x60, 0xd4, 0x0e, 0x82, 0x26, 0xf5, - 0xa5, 0x64, 0xf8, 0x17, 0x29, 0xc2, 0x58, 0xd0, 0xdc, 0x7d, 0x4c, 0xab, 0xa1, 0xe0, 0x54, 0x7e, - 0x92, 0xe7, 0x00, 0x0e, 0x4d, 0xc7, 0xb6, 0x2a, 0x7b, 0xbe, 0x57, 0x47, 0x56, 0x53, 0x46, 0x0e, - 0x21, 0x1f, 0xf8, 0x5e, 0x9d, 0x2c, 0x40, 0x9e, 0x17, 0x37, 0xdd, 0xd0, 0x76, 0x8a, 0x69, 0x2c, - 0xe7, 0x35, 0x76, 0x18, 0x84, 0x5c, 0x84, 0x1c, 0x53, 0x30, 0x1a, 0x04, 0x34, 0x28, 0x66, 0x2e, - 0xa5, 0xae, 0xe4, 0x8c, 0x36, 0x80, 0x2c, 0xc2, 0x74, 0x60, 0xd7, 0x5c, 0x33, 0x6c, 0xfa, 0xb4, - 0x62, 0x3a, 0x35, 0xcf, 0xb7, 0xc3, 0x47, 0xf5, 0xe2, 0x28, 0xf2, 0x40, 0xa2, 0xa2, 0x25, 0x59, - 0xc2, 0xd8, 0x69, 0x34, 0x77, 0x1d, 0xbb, 0x5a, 0xd9, 0xa7, 0xad, 0xe2, 0x18, 0xe2, 0xe5, 0x38, - 0xe4, 0x2e, 0x6d, 0x91, 0x0b, 0x90, 0xdb, 0xa7, 0x2d, 0x6e, 0x99, 0x8a, 0x59, 0x6c, 0x2d, 0xbb, - 0x4f, 0x5b, 0x3b, 0x28, 0xef, 0x57, 0x81, 0xd0, 0xa3, 0x90, 0xba, 0x16, 0xb5, 0x2a, 0x6d, 0xac, - 0x1c, 0x62, 0x15, 0x64, 0xc9, 0x5d, 0x81, 0xad, 0x3f, 0x84, 0xa9, 0x8e, 0xb1, 0x25, 0xa3, 0x30, - 0xb2, 0xb2, 0x54, 0x38, 0x43, 0xb2, 0x90, 0xde, 0xbc, 0xbf, 0xba, 0x56, 0xd0, 0xc8, 0x04, 0xe4, - 0x56, 0x36, 0xd6, 0xd7, 0x36, 0xb7, 0x2b, 0x2b, 0x4b, 0x85, 0x11, 0x02, 0x30, 0xca, 0x3f, 0x0b, - 0x29, 0x92, 0x83, 0xcc, 0xce, 0x3a, 0x03, 0xa7, 0x59, 0xbd, 0x9d, 0xf5, 0x42, 0x46, 0xf7, 0x60, - 0x26, 0xae, 0xaf, 0x41, 0xc3, 0x73, 0x03, 0x4a, 0x3e, 0x84, 0xf1, 0xaa, 0x02, 0x2f, 0x6a, 0x38, - 0xf4, 0xaf, 0x0d, 0x35, 0xf4, 0x62, 0xcc, 0x63, 0x84, 0xf4, 0x45, 0x98, 0x14, 0xc5, 0xfd, 0xe6, - 0xc6, 0x9d, 0x74, 0x76, 0xa4, 0x90, 0xd2, 0x37, 0x01, 0xb6, 0x5a, 0x41, 0x48, 0xeb, 0xeb, 0xee, - 0x9e, 0xc7, 0x06, 0x37, 0xc0, 0xaf, 0x0a, 0x5b, 0x5e, 0x44, 0x05, 0x08, 0x62, 0x08, 0xfb, 0xd4, - 0x77, 0xa9, 0xc3, 0x11, 0xb8, 0xea, 0x00, 0x07, 0x31, 0x04, 0xfd, 0x57, 0x53, 0x30, 0x15, 0x71, - 0x20, 0x7a, 0xfb, 0x51, 0x9c, 0x85, 0xcc, 0xf2, 0xd2, 0xc9, 0xf1, 0xc2, 0xe8, 0x26, 0x63, 0x63, - 0xf5, 0xa7, 0xc7, 0x0b, 0x37, 0x6a, 0x76, 0xf8, 0xa8, 0xb9, 0x5b, 0xae, 0x7a, 0xf5, 0xc5, 0x48, - 0x00, 0xd6, 0x6e, 0xfb, 0xf7, 0x62, 0x63, 0xbf, 0xb6, 0x28, 0x16, 0x9a, 0x32, 0xaf, 0x26, 0x7b, - 0x41, 0xde, 0x87, 0x31, 0xa1, 0x5c, 0xc8, 0x4c, 0xfe, 0xfa, 0xbc, 0x22, 0x44, 0x66, 0xdc, 0xca, - 0x3b, 0x91, 0xe1, 0x5b, 0xb2, 0x2c, 0x5f, 0x48, 0x4d, 0x56, 0x22, 0xef, 0x01, 0xe0, 0x7a, 0xca, - 0xfb, 0x93, 0x42, 0x12, 0xb3, 0x0a, 0x09, 0x2c, 0x2c, 0xb3, 0xae, 0x89, 0x9a, 0x39, 0x84, 0xa0, - 0x30, 0x36, 0xe2, 0xd2, 0x4a, 0x63, 0xe5, 0x97, 0x12, 0x06, 0xb1, 0x2d, 0x69, 0x41, 0x4c, 0x15, - 0xed, 0x16, 0xe4, 0x83, 0x03, 0xa7, 0x22, 0x7b, 0x93, 0x19, 0xa8, 0x37, 0x84, 0x91, 0x39, 0x39, - 0x5e, 0x80, 0xad, 0x87, 0x1b, 0x4b, 0xbc, 0xa6, 0x01, 0xc1, 0x81, 0x23, 0x7e, 0xeb, 0x93, 0x30, - 0xce, 0x04, 0x26, 0xb5, 0x41, 0xff, 0xdb, 0x29, 0x98, 0x10, 0x00, 0x31, 0x38, 0xb7, 0x21, 0xc3, - 0x44, 0x29, 0x75, 0xf0, 0xd5, 0x1e, 0xec, 0xf3, 0xa5, 0x4b, 0xae, 0xc7, 0x38, 0x02, 0x5b, 0xf8, - 0x21, 0x7a, 0xc1, 0x09, 0x90, 0x7f, 0xa6, 0xc1, 0xb4, 0x5c, 0x94, 0x2a, 0xbb, 0xad, 0x8a, 0x1c, - 0xf3, 0x11, 0x24, 0xfc, 0x7e, 0x82, 0x5c, 0x62, 0x1c, 0x95, 0x37, 0x04, 0x8d, 0xe5, 0x16, 0x8e, - 0xb5, 0xb5, 0xe6, 0x86, 0x7e, 0x6b, 0xf9, 0xbe, 0xe8, 0x69, 0xa1, 0xa3, 0x78, 0xf5, 0x5b, 0x7f, - 0xf4, 0x64, 0x1a, 0x54, 0x70, 0x3a, 0xda, 0x29, 0x7d, 0x4b, 0x83, 0xd9, 0x9e, 0x8d, 0x93, 0x02, - 0xa4, 0x98, 0xf5, 0x41, 0xed, 0x35, 0xd8, 0x4f, 0xb2, 0x05, 0x99, 0x43, 0xd3, 0x69, 0x72, 0x3b, - 0x1f, 0x5f, 0x43, 0xf6, 0x0f, 0xcb, 0x72, 0xe1, 0x2e, 0x47, 0x8b, 0x74, 0x7b, 0xe1, 0xc6, 0xf6, - 0x65, 0x33, 0x5c, 0x8e, 0x06, 0xa7, 0xf5, 0xde, 0xc8, 0x3b, 0x9a, 0xfe, 0x3b, 0x29, 0x98, 0x8d, - 0xc9, 0x63, 0xed, 0x28, 0xa4, 0xbe, 0x6b, 0x3a, 0x64, 0x25, 0x3e, 0x52, 0x2f, 0xf7, 0x11, 0xa8, - 0xac, 0x1f, 0x1f, 0xa4, 0x3f, 0x48, 0x1c, 0xa4, 0x0f, 0x06, 0x1d, 0x24, 0xc9, 0xd4, 0xff, 0xe7, - 0x83, 0x75, 0x0b, 0xb2, 0x1b, 0x5e, 0xd5, 0x74, 0xec, 0xb0, 0x45, 0xbe, 0x00, 0x99, 0xd0, 0xa6, - 0xbe, 0x1c, 0x9e, 0x85, 0x04, 0x51, 0x6e, 0xdb, 0x54, 0x1a, 0x22, 0x5e, 0x47, 0x2f, 0x43, 0x9a, - 0x01, 0x55, 0xde, 0x73, 0x9c, 0xf7, 0x19, 0x95, 0xf7, 0x9c, 0x68, 0x5c, 0xff, 0x15, 0x0d, 0xc6, - 0xbe, 0x42, 0xfd, 0xc0, 0xf6, 0x5c, 0x72, 0x19, 0x72, 0x75, 0xf3, 0xb1, 0xe7, 0x57, 0x0e, 0x4d, - 0x47, 0x18, 0xd8, 0xdc, 0xc9, 0xf1, 0x42, 0xe6, 0x1e, 0x03, 0x1a, 0x59, 0x2c, 0xfb, 0x8a, 0xe9, - 0x20, 0x9e, 0xed, 0x0a, 0xbc, 0x11, 0x05, 0x8f, 0x01, 0x8d, 0x2c, 0x96, 0x31, 0xbc, 0x19, 0xc8, - 0x34, 0xcc, 0xb0, 0xfa, 0x08, 0xad, 0x61, 0xc6, 0xe0, 0x1f, 0xa4, 0x04, 0x59, 0xdb, 0xe5, 0x83, - 0x8e, 0x96, 0x2e, 0x63, 0x44, 0xdf, 0xfa, 0x2f, 0x65, 0x60, 0x92, 0x09, 0x6a, 0x95, 0x06, 0x55, - 0xdf, 0x6e, 0x84, 0x9e, 0xff, 0x27, 0xda, 0xe6, 0xbf, 0x0b, 0x19, 0x33, 0x0c, 0xfd, 0x40, 0x98, - 0xfb, 0xe7, 0x94, 0xda, 0xb2, 0xc5, 0xa5, 0x30, 0xf4, 0xed, 0xdd, 0x66, 0x48, 0x23, 0x1b, 0x87, - 0x35, 0xc8, 0x1a, 0x64, 0x1d, 0x31, 0xe0, 0xc2, 0xde, 0xbf, 0x90, 0x30, 0xce, 0x52, 0x37, 0x04, - 0x8d, 0xa8, 0x2a, 0xd9, 0x84, 0x89, 0x2d, 0x44, 0x12, 0x63, 0x28, 0xac, 0xbd, 0x9e, 0x40, 0x4b, - 0x60, 0x0a, 0x52, 0xf1, 0xea, 0xcc, 0x0b, 0xe2, 0xab, 0x58, 0x68, 0xd6, 0x84, 0x2f, 0x95, 0x45, - 0xc0, 0xb6, 0x59, 0x63, 0x1e, 0x54, 0x10, 0x9a, 0x7e, 0xc8, 0x3c, 0xff, 0x10, 0x3d, 0xa8, 0x94, - 0x91, 0x13, 0x90, 0xa5, 0x90, 0x6c, 0x41, 0x41, 0xf2, 0x15, 0x2d, 0x3e, 0x59, 0x54, 0x61, 0xbd, - 0x87, 0x60, 0x64, 0x97, 0xc4, 0x02, 0x23, 0xd8, 0x99, 0x72, 0xe2, 0x60, 0xf2, 0x3c, 0x8c, 0x57, - 0x9d, 0x66, 0x10, 0x52, 0xbf, 0xe2, 0x9a, 0x75, 0xe6, 0x73, 0x31, 0x9e, 0xf2, 0x02, 0xb6, 0x69, - 0xd6, 0x69, 0xe7, 0x7a, 0x07, 0x9f, 0xc9, 0x7a, 0xf7, 0x09, 0xe4, 0x1f, 0x50, 0xbf, 0xca, 0xf6, - 0x83, 0x0e, 0x0d, 0xd8, 0x74, 0x6a, 0xbc, 0xf1, 0x3a, 0x6a, 0xa0, 0x66, 0xb0, 0x9f, 0x08, 0xb9, - 0x7e, 0x13, 0xf5, 0x86, 0x41, 0xae, 0xdf, 0x44, 0xc8, 0xcd, 0xd7, 0x51, 0x17, 0x18, 0xe4, 0x26, - 0xc7, 0x79, 0xfb, 0x26, 0x8e, 0x2f, 0x83, 0xbc, 0xcd, 0x71, 0xde, 0x7d, 0x1d, 0x47, 0x89, 0x41, - 0xde, 0x7d, 0x9d, 0xb9, 0xf2, 0x8d, 0x7b, 0xe6, 0x11, 0x0a, 0x5b, 0x33, 0xf0, 0xb7, 0xfe, 0x0f, - 0x47, 0x60, 0x6a, 0x2b, 0xf4, 0x7c, 0x75, 0x1e, 0xfc, 0x02, 0x64, 0xd9, 0xbe, 0x51, 0x99, 0x08, - 0x2b, 0x27, 0xc7, 0x0b, 0x63, 0x88, 0x86, 0x33, 0xe1, 0xcd, 0xa1, 0x66, 0x82, 0xa8, 0x67, 0x8c, - 0x21, 0xd1, 0x75, 0xab, 0xad, 0xcb, 0x23, 0x43, 0xeb, 0xf2, 0x0a, 0xa4, 0xd9, 0x84, 0x12, 0xb3, - 0xe0, 0x6a, 0x1f, 0xd3, 0xdf, 0xee, 0x93, 0xa0, 0x82, 0x95, 0xc9, 0x32, 0x64, 0xab, 0x66, 0xc3, - 0xac, 0xb6, 0x27, 0xc4, 0xa5, 0x1e, 0x2c, 0x20, 0xdb, 0x2b, 0x02, 0x4f, 0xce, 0x06, 0x59, 0x4f, - 0x3f, 0xd6, 0x20, 0x8f, 0x18, 0xdc, 0xc0, 0x92, 0x55, 0x48, 0x5b, 0x34, 0xa8, 0xa2, 0xbc, 0xf2, - 0xd7, 0xaf, 0x25, 0x39, 0x54, 0x71, 0x69, 0x4b, 0xce, 0x58, 0x6d, 0x72, 0x0f, 0xc6, 0xea, 0x34, - 0xf4, 0xed, 0x6a, 0x20, 0x16, 0xb7, 0x1b, 0xfd, 0x08, 0xf1, 0xe6, 0xcb, 0xf7, 0x78, 0x2d, 0x5c, - 0x4c, 0x0c, 0x49, 0xa3, 0xf4, 0x1e, 0x8c, 0xab, 0x05, 0xfd, 0x2c, 0xb5, 0xa6, 0x2e, 0x13, 0xff, - 0x31, 0xcb, 0xdd, 0xb0, 0xc8, 0xe9, 0x5a, 0x89, 0xf5, 0x70, 0x78, 0xd1, 0x63, 0x07, 0xe3, 0xae, - 0xeb, 0xc8, 0x50, 0xae, 0x6b, 0xdc, 0x26, 0xa4, 0x3a, 0x6d, 0xc2, 0x73, 0x00, 0xcd, 0x86, 0x65, - 0x8a, 0x62, 0xbe, 0xc7, 0xcb, 0x09, 0xc8, 0x52, 0x48, 0x36, 0xdb, 0xa2, 0xcd, 0xf4, 0xdd, 0xb4, - 0xaa, 0x1d, 0xef, 0x2d, 0x5b, 0xb2, 0x05, 0x93, 0x7c, 0x92, 0x70, 0x1f, 0x93, 0x06, 0xc5, 0x51, - 0x24, 0x7b, 0x79, 0xb0, 0x11, 0x93, 0x36, 0x31, 0x68, 0x83, 0x68, 0xc0, 0x66, 0xa8, 0xe9, 0xd7, - 0x82, 0xe2, 0x18, 0x6e, 0xf7, 0xf0, 0x37, 0x1b, 0x34, 0xea, 0x1e, 0x8a, 0x7d, 0x22, 0xfb, 0x49, - 0xbe, 0xab, 0x41, 0xce, 0x31, 0x43, 0xea, 0x56, 0x6d, 0x1a, 0xe0, 0xd6, 0x30, 0x7f, 0xfd, 0xad, - 0x41, 0x7b, 0xb3, 0x21, 0x2b, 0x72, 0xaf, 0xe7, 0x0b, 0x8c, 0x8d, 0x27, 0xf5, 0x70, 0xda, 0x5c, - 0x90, 0xef, 0x68, 0x90, 0x35, 0xab, 0xa1, 0x7d, 0xc8, 0x26, 0x15, 0x20, 0x4b, 0x37, 0x07, 0x65, - 0x69, 0x49, 0xd4, 0xfb, 0x0c, 0x38, 0x8a, 0x78, 0x20, 0x65, 0x98, 0x0e, 0xbd, 0xd0, 0x74, 0x2a, - 0x62, 0xbb, 0x53, 0xa7, 0x75, 0xcf, 0x6f, 0x15, 0xf3, 0xa8, 0x17, 0x67, 0xb1, 0x88, 0x6f, 0x6d, - 0xee, 0x61, 0x01, 0x39, 0x0f, 0x59, 0xb7, 0x59, 0xaf, 0x54, 0x1b, 0xcd, 0xa0, 0x38, 0x8e, 0xbe, - 0xc2, 0x98, 0xdb, 0xac, 0xaf, 0x34, 0x9a, 0x4f, 0x35, 0x8d, 0x4a, 0x5f, 0x84, 0xc9, 0xb8, 0xc4, - 0x7b, 0xb8, 0x7a, 0xb1, 0xda, 0x29, 0xb5, 0x76, 0x15, 0xa6, 0x36, 0x69, 0xf8, 0xb1, 0xe7, 0xef, - 0x4b, 0x19, 0x71, 0x9f, 0xa6, 0xea, 0xd5, 0x6d, 0xb7, 0x86, 0x34, 0x52, 0x46, 0xf4, 0xcd, 0xca, - 0xbc, 0x66, 0x58, 0xf3, 0x58, 0x19, 0xa7, 0x15, 0x7d, 0x93, 0x22, 0x8c, 0xf1, 0xd1, 0x6a, 0x89, - 0xa9, 0x23, 0x3f, 0x4b, 0x21, 0x4c, 0xc4, 0x46, 0xa0, 0x07, 0x87, 0xf7, 0x54, 0x0e, 0xf3, 0xd7, - 0xdf, 0x1e, 0x74, 0x64, 0x3b, 0x98, 0x57, 0xed, 0x4b, 0x01, 0x26, 0x0d, 0x5a, 0xb3, 0x3d, 0x37, - 0xda, 0xe7, 0xfd, 0x6f, 0x0d, 0xa6, 0x22, 0x90, 0x30, 0x3a, 0x0f, 0x61, 0xcc, 0xe7, 0x20, 0xe1, - 0xa2, 0x26, 0x35, 0xdd, 0x51, 0x59, 0x7e, 0x8b, 0x89, 0x2b, 0xe8, 0x94, 0xe6, 0x61, 0x94, 0x17, - 0x30, 0xb9, 0x7f, 0xdd, 0x73, 0xc5, 0xe6, 0x24, 0x67, 0xf0, 0x8f, 0x52, 0x1d, 0xc6, 0xd5, 0x8a, - 0x3d, 0x46, 0xfb, 0x56, 0x5c, 0x1a, 0x6f, 0x0c, 0xcd, 0x92, 0x2a, 0x87, 0xcb, 0x90, 0xe7, 0x22, - 0xeb, 0x73, 0x2c, 0xf8, 0x7b, 0x69, 0xc8, 0x19, 0xe6, 0x5e, 0xc8, 0x6c, 0x05, 0x25, 0xaf, 0x02, - 0xf8, 0xb4, 0xe1, 0xd8, 0x55, 0x53, 0x62, 0xa6, 0x97, 0x27, 0x4e, 0x8e, 0x17, 0x72, 0x06, 0x87, - 0xb2, 0xc9, 0x29, 0x10, 0xd6, 0x2d, 0xf2, 0x16, 0xc0, 0x23, 0xd3, 0xb7, 0xd0, 0x54, 0x49, 0xae, - 0xcf, 0x96, 0xf9, 0xf9, 0x68, 0xf9, 0xb6, 0xe9, 0x5b, 0x48, 0x54, 0x5a, 0xdc, 0x47, 0x12, 0xc0, - 0xcc, 0x91, 0x43, 0x4d, 0x0b, 0x15, 0x26, 0x6d, 0xe0, 0x6f, 0x26, 0x34, 0x4e, 0x26, 0xcd, 0x7d, - 0x7b, 0xfc, 0x60, 0xda, 0x65, 0x36, 0x1a, 0x8e, 0x4d, 0x2d, 0x74, 0x38, 0xd2, 0x86, 0xfc, 0x24, - 0xdb, 0x90, 0x6d, 0xf8, 0x5e, 0x0d, 0xfd, 0x25, 0x6e, 0x21, 0xaf, 0x27, 0xc9, 0x4b, 0xf6, 0xb0, - 0xfc, 0x40, 0x54, 0xe2, 0x46, 0x41, 0x2c, 0xbf, 0x92, 0x12, 0x79, 0x19, 0xa6, 0x18, 0x37, 0x95, - 0xd0, 0x37, 0xdd, 0x60, 0x8f, 0xfa, 0x94, 0xa2, 0x93, 0x98, 0x36, 0x26, 0x19, 0x78, 0x3b, 0x82, - 0x96, 0xfe, 0xb2, 0x06, 0x59, 0x49, 0x8a, 0xf1, 0x5e, 0xc7, 0x5d, 0x02, 0x0a, 0xcc, 0xe0, 0x1f, - 0xac, 0x97, 0x2e, 0x3d, 0xe2, 0x67, 0x8a, 0x69, 0x03, 0x7f, 0xb7, 0x7b, 0x99, 0x52, 0x7b, 0x39, - 0x07, 0xa3, 0x0d, 0xb3, 0x19, 0x50, 0x0b, 0x3b, 0x9f, 0x35, 0xc4, 0x17, 0xb9, 0x0a, 0x85, 0x06, - 0x75, 0x2d, 0xdb, 0xad, 0x55, 0x02, 0xd7, 0x6c, 0x04, 0x8f, 0xbc, 0x50, 0x88, 0x61, 0x4a, 0xc0, - 0xb7, 0x04, 0xb8, 0xf4, 0x18, 0x26, 0x62, 0x3d, 0x53, 0xd5, 0x2b, 0xcd, 0xd5, 0x6b, 0x25, 0xae, - 0x5e, 0xaf, 0x0d, 0x25, 0x2e, 0x55, 0xb5, 0x4e, 0x46, 0x60, 0xc2, 0x30, 0xdd, 0x1a, 0x7d, 0xe0, - 0x7b, 0xbb, 0x0e, 0xad, 0x07, 0xe4, 0x12, 0xe4, 0x9b, 0xae, 0x79, 0x68, 0xda, 0x8e, 0xb9, 0xeb, - 0xf0, 0xb3, 0xe4, 0xac, 0xa1, 0x82, 0xc8, 0x4d, 0x38, 0xc7, 0x24, 0xc8, 0x7c, 0x60, 0x2f, 0xac, - 0xf0, 0x38, 0xc0, 0x23, 0xcf, 0xb1, 0xa8, 0x8f, 0xec, 0x64, 0x8d, 0x19, 0x5e, 0xbc, 0xe9, 0x85, - 0x1b, 0xac, 0xf0, 0x36, 0x96, 0x91, 0x17, 0x61, 0xd2, 0xf5, 0x2a, 0x4c, 0xa3, 0x2a, 0xbc, 0x1c, - 0x05, 0x97, 0x35, 0xc6, 0x5d, 0x8f, 0xf1, 0xb8, 0x81, 0x30, 0x72, 0x05, 0xa6, 0x9a, 0xae, 0x45, - 0x7d, 0xa1, 0x99, 0x61, 0x24, 0xc8, 0x4e, 0x30, 0xb9, 0x0c, 0x93, 0xde, 0x61, 0x0c, 0x31, 0x8b, - 0x88, 0x1d, 0x50, 0xb4, 0xda, 0x1e, 0x67, 0x13, 0x25, 0x9e, 0x35, 0xc6, 0x5c, 0x0f, 0x19, 0x23, - 0xef, 0x40, 0xf1, 0xa0, 0x69, 0xd3, 0x80, 0x39, 0xd6, 0x15, 0x7a, 0xd0, 0x34, 0x9d, 0xa0, 0x12, - 0xda, 0xd5, 0x7d, 0x66, 0x1c, 0x47, 0x11, 0x75, 0x2e, 0x2a, 0x5f, 0xc3, 0xe2, 0x6d, 0x5e, 0x4a, - 0x5e, 0x01, 0xc2, 0x7b, 0xe2, 0xd5, 0x2a, 0xa1, 0xe7, 0x55, 0x1c, 0xd3, 0xaf, 0x71, 0xfd, 0xca, - 0x1a, 0x53, 0xac, 0x64, 0xc3, 0xab, 0x6d, 0x7b, 0xde, 0x06, 0x03, 0xeb, 0xfb, 0x30, 0x85, 0x32, - 0x66, 0xc3, 0x60, 0x63, 0x68, 0x88, 0xbc, 0x0a, 0xe4, 0xa0, 0x49, 0x7d, 0x9b, 0x06, 0x95, 0x06, - 0xf5, 0x2b, 0x01, 0xad, 0x7a, 0xae, 0x25, 0x1c, 0xfa, 0x82, 0x28, 0x79, 0x40, 0xfd, 0x2d, 0x84, - 0x93, 0x6b, 0x70, 0xf6, 0x63, 0xdf, 0x0e, 0xe3, 0xc8, 0x7c, 0x1d, 0x99, 0xe2, 0x05, 0x11, 0xae, - 0x7e, 0x1b, 0xe0, 0x81, 0x4f, 0xc3, 0xb0, 0xb5, 0xd5, 0x30, 0x71, 0x07, 0x85, 0xee, 0x4f, 0xa5, - 0x6d, 0x9f, 0xb2, 0x08, 0xb8, 0x4b, 0x5b, 0xcc, 0x90, 0x50, 0x17, 0x8f, 0x90, 0xc5, 0x2e, 0x7c, - 0x94, 0xba, 0xd6, 0x5d, 0xda, 0x7a, 0x2f, 0xfd, 0xdf, 0x7f, 0x73, 0x41, 0xd3, 0xbf, 0x97, 0x67, - 0xe6, 0xc4, 0xad, 0x51, 0x74, 0xad, 0x7e, 0x0e, 0xd2, 0x41, 0xc3, 0x74, 0x85, 0x6f, 0x97, 0x74, - 0x1c, 0xd8, 0x6e, 0x5e, 0xfa, 0x75, 0xac, 0x22, 0x59, 0x07, 0x40, 0x91, 0xa9, 0x16, 0xe6, 0xc5, - 0x41, 0x14, 0x57, 0x1a, 0x1d, 0x3f, 0x32, 0x6d, 0x1f, 0xa8, 0x06, 0x26, 0xee, 0x4a, 0xab, 0x07, - 0x1f, 0x22, 0xb2, 0x85, 0xb4, 0x44, 0x37, 0xe4, 0x56, 0x81, 0x4f, 0xd6, 0x3a, 0x4c, 0x06, 0x5e, - 0xd3, 0xaf, 0xd2, 0xe8, 0xbc, 0x28, 0x83, 0x7b, 0x99, 0x5b, 0x27, 0xc7, 0x0b, 0xe3, 0x5b, 0x58, - 0xf2, 0x74, 0x5b, 0xfb, 0xf1, 0xa0, 0x4d, 0xc4, 0x22, 0x07, 0x30, 0x25, 0x9a, 0x8b, 0xf6, 0x4e, - 0xa3, 0xd8, 0xde, 0xfa, 0xc9, 0xf1, 0xc2, 0x04, 0x6f, 0xef, 0x69, 0x77, 0x50, 0x13, 0x81, 0x42, - 0xc6, 0xea, 0x8e, 0xdf, 0x8c, 0xf5, 0x88, 0xdf, 0xac, 0xc0, 0x84, 0x98, 0xc5, 0x36, 0x63, 0xac, - 0x25, 0xf6, 0xc9, 0xc5, 0x5e, 0xfb, 0x64, 0x86, 0x27, 0x8f, 0xe8, 0xb1, 0xd2, 0x6d, 0x5e, 0x87, - 0xdc, 0x41, 0x23, 0x8e, 0x36, 0x04, 0xb7, 0xc5, 0xf9, 0xeb, 0x57, 0x12, 0x07, 0x57, 0xb1, 0x39, - 0x8a, 0xe9, 0xe6, 0x36, 0x48, 0x8c, 0xaf, 0xdc, 0x3d, 0x5f, 0xeb, 0x47, 0xa8, 0x3d, 0xb1, 0xd4, - 0xf1, 0x0d, 0xc8, 0x0e, 0x8c, 0xab, 0x61, 0xca, 0xe2, 0x04, 0x92, 0x7b, 0xb5, 0xaf, 0xba, 0x60, - 0x2f, 0x63, 0x4e, 0x78, 0xde, 0x69, 0x83, 0xc8, 0x45, 0xc8, 0x45, 0x66, 0xa1, 0x38, 0x89, 0x73, - 0xbe, 0x0d, 0x60, 0xeb, 0x9c, 0xb4, 0x21, 0x53, 0xdc, 0xdc, 0x88, 0x4f, 0xf2, 0x3c, 0x8c, 0xfb, - 0x6c, 0x45, 0x72, 0xd8, 0x9a, 0x42, 0x83, 0x62, 0x01, 0x9d, 0xac, 0x3c, 0x83, 0x6d, 0x70, 0x10, - 0x1b, 0x2f, 0x9c, 0xd0, 0x11, 0xce, 0x59, 0xc4, 0x19, 0x47, 0xa0, 0x44, 0x9a, 0x81, 0x8c, 0xe3, - 0x55, 0xf7, 0x83, 0x22, 0xe1, 0xce, 0x20, 0x7e, 0x90, 0x1b, 0x30, 0x87, 0x3f, 0x2a, 0x1f, 0xdb, - 0xe1, 0xa3, 0xca, 0xc7, 0xa6, 0x1d, 0x56, 0x0e, 0x9a, 0xb4, 0x49, 0x83, 0xe2, 0x34, 0xa2, 0x4d, - 0x63, 0xe9, 0x87, 0x76, 0xf8, 0xe8, 0x43, 0xd3, 0x0e, 0x1f, 0x62, 0x11, 0xda, 0x72, 0xaf, 0xba, - 0xaf, 0xa0, 0xe3, 0x4f, 0xea, 0x07, 0xc5, 0x19, 0xac, 0x35, 0xc3, 0x8a, 0xa3, 0x0a, 0x1f, 0xf2, - 0x32, 0xf2, 0x09, 0x3c, 0x1f, 0x7a, 0x8d, 0xca, 0x7e, 0x85, 0xb7, 0xb8, 0xdb, 0xea, 0x45, 0x60, - 0xb6, 0x6f, 0xf4, 0x27, 0x9a, 0x96, 0xe5, 0x0d, 0xaf, 0xba, 0xaf, 0xcc, 0xcf, 0x0b, 0xa1, 0xd7, - 0xb8, 0xcb, 0x60, 0xc1, 0x72, 0xab, 0xb3, 0xf1, 0xd2, 0xff, 0xd2, 0xf0, 0x78, 0x72, 0x5f, 0xee, - 0xf8, 0x1a, 0x68, 0x6f, 0x14, 0x0b, 0x97, 0xe3, 0x10, 0x66, 0xe2, 0x3e, 0xe0, 0x4b, 0x27, 0xb3, - 0x36, 0xe3, 0xcb, 0x6f, 0xfe, 0xf4, 0x78, 0xe1, 0xf5, 0xa1, 0x66, 0xd5, 0x5d, 0xda, 0xe2, 0x0b, - 0x2e, 0x81, 0xf4, 0x23, 0xea, 0x58, 0x62, 0xc9, 0xc2, 0xdf, 0x6c, 0xa0, 0x65, 0x57, 0xf9, 0x56, - 0x52, 0x7e, 0x32, 0xd7, 0x83, 0xfd, 0x64, 0x8b, 0xbd, 0x8f, 0xcb, 0x1a, 0x8f, 0x7b, 0xa4, 0x8c, - 0x49, 0x01, 0x36, 0x38, 0x54, 0x45, 0xc4, 0x11, 0xf6, 0x03, 0xb4, 0x08, 0x6d, 0xc4, 0x0f, 0x39, - 0xf4, 0x4e, 0x3a, 0x9b, 0x2f, 0x8c, 0xdf, 0x49, 0x67, 0xc7, 0x0b, 0x13, 0xfa, 0xaf, 0x68, 0x62, - 0xcd, 0xee, 0x1b, 0x0c, 0x23, 0x26, 0xe4, 0x7c, 0x86, 0x59, 0xb1, 0x2d, 0x7e, 0x5c, 0x90, 0x5a, - 0x5e, 0x3d, 0x39, 0x5e, 0xc8, 0x72, 0xc1, 0xaf, 0x06, 0x43, 0x9b, 0x19, 0x51, 0xd1, 0xc8, 0x22, - 0xd9, 0x75, 0x2b, 0xd0, 0xb7, 0x61, 0x52, 0x32, 0x23, 0x1c, 0xf2, 0x65, 0x18, 0xc5, 0x52, 0xe9, - 0x8f, 0xbf, 0x38, 0x88, 0x06, 0xc8, 0x50, 0x2f, 0xaf, 0xa9, 0x5f, 0x81, 0x89, 0x5b, 0x98, 0xf6, - 0xd1, 0xd7, 0xe9, 0xfd, 0xc1, 0x08, 0x4c, 0xad, 0x61, 0xea, 0x02, 0x9b, 0x9d, 0x01, 0x2a, 0xc5, - 0xb3, 0x3e, 0x9d, 0xda, 0x83, 0x39, 0x36, 0xa7, 0xa9, 0x1f, 0x54, 0x4c, 0xd7, 0xe2, 0x66, 0xb3, - 0xe6, 0x9b, 0x75, 0x79, 0x5c, 0xf5, 0xba, 0xda, 0x63, 0x6e, 0x55, 0xca, 0x32, 0xbd, 0xa2, 0xbc, - 0xcd, 0x6b, 0x2e, 0xb9, 0xd6, 0xed, 0xa8, 0x9e, 0x31, 0x13, 0xf6, 0x80, 0x92, 0x5b, 0x90, 0xe7, - 0xd5, 0x2a, 0x18, 0xd7, 0x4f, 0xe1, 0x31, 0xff, 0xe5, 0x24, 0xe2, 0x5c, 0x12, 0x18, 0xc0, 0x07, - 0x1a, 0xfd, 0xd6, 0x5f, 0x03, 0xa2, 0xc8, 0xa8, 0xaf, 0x4c, 0xff, 0x14, 0x4c, 0xc7, 0xd0, 0xc5, - 0xc0, 0x46, 0x66, 0x99, 0x8f, 0x6b, 0x92, 0x59, 0xee, 0x18, 0x91, 0x98, 0x59, 0xd6, 0xff, 0x34, - 0xc0, 0xb6, 0x6f, 0x56, 0xe9, 0xda, 0x21, 0xb3, 0x97, 0xef, 0x40, 0x3a, 0xb4, 0xeb, 0x54, 0x38, - 0x16, 0xa5, 0x32, 0xcf, 0x95, 0x28, 0xcb, 0x5c, 0x89, 0xf2, 0xb6, 0x4c, 0xa6, 0x58, 0xce, 0x32, - 0x22, 0xbf, 0xfe, 0x47, 0x0b, 0x9a, 0x81, 0x35, 0xd8, 0x04, 0x8c, 0xa7, 0x25, 0xc8, 0x4f, 0xfd, - 0xf7, 0x34, 0x98, 0x5a, 0x72, 0x1c, 0xaf, 0x6a, 0x86, 0x9e, 0xbf, 0xea, 0xb7, 0x8c, 0xa6, 0xcb, - 0x94, 0x42, 0xce, 0x05, 0xbe, 0x2b, 0xe6, 0x4a, 0x21, 0x34, 0xfa, 0x89, 0x67, 0xc2, 0x98, 0x98, - 0x09, 0xe4, 0x4b, 0x30, 0x4a, 0x59, 0x87, 0xe4, 0xb9, 0x5c, 0x92, 0x8b, 0xd4, 0xee, 0xbe, 0x21, - 0x2a, 0xe9, 0xd7, 0x61, 0x36, 0xe2, 0x18, 0x69, 0xcb, 0x51, 0x3a, 0xdf, 0xc9, 0x77, 0xd4, 0xa4, - 0xfe, 0x4f, 0x34, 0x98, 0xeb, 0xac, 0xd4, 0x3b, 0x38, 0x9d, 0xfa, 0x2c, 0x03, 0x15, 0x2b, 0x30, - 0x66, 0xf9, 0xad, 0x8a, 0xdf, 0x74, 0x85, 0xbe, 0x27, 0x69, 0x42, 0xc7, 0x30, 0x18, 0xa3, 0x16, - 0xfe, 0xab, 0x7f, 0x47, 0x83, 0x42, 0x9b, 0xf7, 0x3f, 0x01, 0x86, 0xec, 0x23, 0x38, 0xab, 0xf0, - 0x23, 0xc4, 0xb8, 0x06, 0x59, 0xd1, 0xd5, 0x41, 0xb4, 0xbe, 0xb3, 0xaf, 0x63, 0xbc, 0xaf, 0x81, - 0xae, 0xc3, 0xf8, 0x9d, 0xad, 0xfb, 0x9b, 0x11, 0x59, 0x99, 0x31, 0xa3, 0xb5, 0x33, 0x66, 0xf4, - 0x3a, 0x4c, 0x44, 0x61, 0x48, 0xe6, 0x9d, 0xb1, 0x65, 0x1e, 0xdd, 0x34, 0x21, 0x0a, 0xfe, 0xc1, - 0xaa, 0x56, 0x3d, 0x8b, 0x6b, 0x7c, 0xc6, 0xc0, 0xdf, 0xea, 0x44, 0x48, 0xc5, 0x26, 0x02, 0x2b, - 0xb1, 0x78, 0xda, 0x02, 0xe6, 0xe1, 0xe4, 0x0c, 0xf9, 0xa9, 0xff, 0x0b, 0x0d, 0xf2, 0x1b, 0x5e, - 0xad, 0xff, 0x1a, 0xc2, 0xbc, 0x0d, 0x7a, 0x48, 0x1d, 0x19, 0xa9, 0xc3, 0x8f, 0xe8, 0xa4, 0xb5, - 0x82, 0x73, 0x97, 0xb7, 0xca, 0xb7, 0x1a, 0x6c, 0xbe, 0x32, 0xa5, 0x65, 0x5b, 0x0b, 0x2c, 0xe4, - 0xa7, 0x00, 0x6c, 0xab, 0x81, 0x45, 0x05, 0x48, 0xd5, 0xcd, 0x23, 0x5c, 0x10, 0x73, 0x06, 0xfb, - 0xc9, 0x98, 0x6c, 0x98, 0x61, 0x48, 0x7d, 0x57, 0x04, 0x79, 0xe4, 0x27, 0xdb, 0x4d, 0xfb, 0xd4, - 0x32, 0xab, 0xa1, 0xd8, 0x5a, 0x89, 0xaf, 0x3b, 0xe9, 0x6c, 0xb6, 0x90, 0xd3, 0xef, 0x03, 0xd9, - 0xf0, 0x6a, 0x6c, 0x8f, 0x6c, 0x2b, 0xcb, 0xcf, 0xbb, 0x6c, 0x57, 0x83, 0x20, 0x31, 0x62, 0xe7, - 0x3b, 0x83, 0x2f, 0x8e, 0x57, 0x2b, 0xab, 0x67, 0x06, 0x12, 0x5f, 0x2f, 0xc3, 0xf4, 0x86, 0x57, - 0xfb, 0xc0, 0x76, 0x68, 0xb0, 0x61, 0x07, 0x61, 0x5f, 0x3b, 0xf9, 0x00, 0x66, 0xe2, 0xf8, 0x82, - 0x85, 0x77, 0x20, 0xb3, 0xc7, 0x80, 0x82, 0x81, 0x8b, 0xbd, 0x18, 0x60, 0xb5, 0x54, 0xd3, 0x88, - 0x15, 0xf4, 0xaf, 0xc1, 0xa4, 0xa0, 0xd8, 0x77, 0x5c, 0x08, 0xa4, 0x59, 0x1d, 0x31, 0x2c, 0xf8, - 0x5b, 0x91, 0x57, 0xaa, 0x43, 0x5e, 0xe9, 0x42, 0x46, 0xaf, 0xc2, 0xc4, 0x56, 0x68, 0x56, 0xf7, - 0xfb, 0x8f, 0xf9, 0xbb, 0x22, 0x93, 0x8c, 0x07, 0x96, 0x13, 0x73, 0x3f, 0x90, 0x60, 0x3b, 0x63, - 0x4c, 0xdf, 0x82, 0x34, 0x63, 0x1f, 0x8f, 0x4c, 0x4c, 0x61, 0xd6, 0x73, 0x06, 0xfe, 0x66, 0xbb, - 0x51, 0xc6, 0x66, 0x25, 0xb0, 0xbf, 0x2e, 0x4f, 0x32, 0xb3, 0x0c, 0xb0, 0x65, 0x7f, 0x9d, 0x92, - 0x12, 0x64, 0x45, 0xce, 0x63, 0x20, 0x32, 0xc9, 0xa2, 0x6f, 0xfd, 0x6f, 0x6a, 0x30, 0x75, 0x8b, - 0x86, 0x28, 0xe9, 0xbe, 0xcc, 0x5f, 0x80, 0x9c, 0x63, 0x07, 0x61, 0xc5, 0x73, 0x9d, 0x96, 0x38, - 0x91, 0xc8, 0x32, 0xc0, 0x7d, 0xd7, 0x69, 0x91, 0xb7, 0x45, 0xcf, 0x32, 0xd8, 0xb3, 0xa4, 0x28, - 0x27, 0x6b, 0x4c, 0xc9, 0x84, 0x2b, 0x41, 0x56, 0x68, 0x25, 0x3f, 0xa4, 0xca, 0x19, 0xd1, 0xb7, - 0xbe, 0x0e, 0x85, 0x36, 0x77, 0x42, 0x07, 0x6e, 0xc6, 0x75, 0x60, 0xa1, 0x4f, 0x4b, 0x52, 0x01, - 0xfe, 0x58, 0x83, 0xc9, 0x07, 0xbe, 0xb7, 0x37, 0x88, 0x06, 0x2c, 0xc7, 0xfa, 0x52, 0x4e, 0xdc, - 0x92, 0xab, 0x14, 0xcb, 0x4a, 0xb7, 0x8a, 0x30, 0xc6, 0xcf, 0x13, 0xb8, 0xe7, 0x99, 0x31, 0xe4, - 0x27, 0xd3, 0x25, 0xc7, 0xdc, 0xa5, 0x4e, 0x20, 0xe7, 0x1e, 0xff, 0xd2, 0x7f, 0x1e, 0xd2, 0x98, - 0x44, 0x96, 0x85, 0xf4, 0xed, 0xb5, 0xa5, 0x07, 0x85, 0x33, 0x64, 0x0c, 0x52, 0x2b, 0x0f, 0x76, - 0x78, 0x16, 0xd9, 0xad, 0xfb, 0xc6, 0xfd, 0x9d, 0xed, 0xf5, 0xcd, 0xb5, 0xc2, 0x08, 0x29, 0xc0, - 0xf8, 0xf6, 0x6d, 0x63, 0x6d, 0x69, 0x75, 0xc5, 0x58, 0x5b, 0xda, 0x5e, 0x2b, 0xa4, 0x08, 0xc0, - 0xe8, 0xd2, 0xc6, 0xc6, 0xfd, 0x95, 0xad, 0x42, 0x9a, 0xe4, 0x20, 0xb3, 0xbc, 0x71, 0x7f, 0xe5, - 0x6e, 0x21, 0xc3, 0x7e, 0xde, 0xdb, 0xd9, 0x5e, 0xfb, 0x6a, 0x61, 0x54, 0xbf, 0x0a, 0x93, 0xe2, - 0x20, 0xbd, 0xef, 0xec, 0xfb, 0xfb, 0xe8, 0x07, 0xef, 0x85, 0x68, 0xca, 0xd9, 0x12, 0xf5, 0x4c, - 0xa3, 0xf3, 0x5f, 0x86, 0x0c, 0x2e, 0x15, 0x03, 0x9d, 0x5c, 0x74, 0x9c, 0x36, 0x60, 0x45, 0xfd, - 0x1a, 0xf3, 0x94, 0x05, 0xbb, 0xdc, 0xc2, 0x2b, 0x76, 0x5b, 0x8b, 0x3b, 0x30, 0xdf, 0x1c, 0x81, - 0xa9, 0x08, 0x59, 0x6c, 0x3b, 0x9f, 0xb5, 0x03, 0x73, 0x0b, 0x46, 0x71, 0x89, 0x91, 0x0e, 0xcc, - 0xd5, 0x3e, 0x87, 0x33, 0xed, 0x8e, 0x48, 0xe7, 0x9d, 0x57, 0x27, 0xab, 0x32, 0xa3, 0x27, 0x85, - 0x74, 0xae, 0x0c, 0x42, 0x87, 0x49, 0x3b, 0x96, 0xd2, 0xa3, 0x37, 0xa1, 0xc0, 0x4a, 0x57, 0xe9, - 0x6e, 0xb3, 0x26, 0x75, 0x21, 0xe6, 0x06, 0x68, 0xcf, 0xc4, 0x0d, 0xf8, 0x77, 0x23, 0x70, 0x56, - 0x69, 0x57, 0xcc, 0xe6, 0xef, 0x68, 0x1d, 0x9b, 0x9a, 0x77, 0xfa, 0x74, 0x2a, 0x56, 0x9d, 0x37, - 0x23, 0xce, 0xa9, 0xbf, 0x28, 0x82, 0x57, 0x4f, 0xc6, 0xa8, 0xe0, 0xe2, 0x33, 0x1b, 0xac, 0x12, - 0x85, 0xbc, 0xc2, 0x9d, 0x7a, 0xd6, 0x9c, 0xe2, 0x5b, 0xdf, 0x2f, 0xc7, 0xcf, 0x9a, 0xaf, 0x0d, - 0xd2, 0x50, 0x77, 0x4a, 0xd1, 0x1f, 0xa6, 0x61, 0x6c, 0xfb, 0xc8, 0xc5, 0xed, 0xd9, 0x43, 0x18, - 0x11, 0x2a, 0x3c, 0xbe, 0xbc, 0xc4, 0x98, 0xf9, 0xf7, 0x83, 0xce, 0x4d, 0x9e, 0xf2, 0xdd, 0xb4, - 0xad, 0xf2, 0xce, 0xce, 0x3a, 0x1b, 0xf9, 0x91, 0xf5, 0x55, 0x63, 0xc4, 0xb6, 0xc8, 0x7b, 0xb8, - 0x35, 0xf1, 0x43, 0xc1, 0xe4, 0x60, 0xbb, 0x08, 0x5e, 0x85, 0x6d, 0xc2, 0xc3, 0x23, 0xb7, 0x62, - 0x89, 0x70, 0xb4, 0xed, 0xb9, 0xc2, 0x9f, 0x99, 0x0c, 0x8f, 0xdc, 0xd5, 0x36, 0x94, 0xbc, 0x05, - 0xe7, 0xdc, 0x66, 0x9d, 0x1f, 0x60, 0xd6, 0xd9, 0xba, 0x54, 0xa1, 0x47, 0xb4, 0xda, 0x94, 0x67, - 0xd4, 0x19, 0x63, 0xd6, 0x6d, 0xd6, 0xb7, 0xa2, 0xd2, 0x35, 0x51, 0x48, 0x16, 0x20, 0xcf, 0xea, - 0xf9, 0x94, 0x7b, 0x25, 0x78, 0xc6, 0x68, 0x80, 0xdb, 0xac, 0x1b, 0x1c, 0x42, 0xae, 0x40, 0x81, - 0x21, 0x98, 0xcd, 0xd0, 0x8b, 0xb0, 0xb8, 0x35, 0x9e, 0x74, 0x9b, 0xf5, 0xa5, 0x66, 0xe8, 0x49, - 0xcc, 0x2f, 0x43, 0xd6, 0xa2, 0xa6, 0xe5, 0xd8, 0x2e, 0x3f, 0xca, 0x1b, 0xb4, 0xab, 0x51, 0x2d, - 0x0c, 0x0e, 0xd6, 0x1b, 0x8e, 0x5d, 0xb5, 0x43, 0x71, 0x60, 0x1e, 0x7d, 0x33, 0x46, 0x4d, 0xe6, - 0xc2, 0x56, 0x76, 0x5b, 0x21, 0xe5, 0xc7, 0x78, 0x29, 0x03, 0x10, 0xb4, 0xcc, 0x20, 0xe4, 0x32, - 0x4c, 0xd5, 0xcd, 0xa3, 0x8a, 0x8a, 0x04, 0x88, 0x34, 0x51, 0x37, 0x8f, 0x96, 0xda, 0x78, 0x17, - 0x20, 0x87, 0x27, 0x5d, 0xb8, 0x04, 0xe7, 0x79, 0x2b, 0x0c, 0x80, 0x4b, 0xf0, 0x0b, 0x30, 0x61, - 0x07, 0xe2, 0xac, 0xd1, 0xae, 0x9a, 0x0e, 0xc6, 0x52, 0xb3, 0xc6, 0xb8, 0x1d, 0xdc, 0x8e, 0x60, - 0xb8, 0xdc, 0xfa, 0xb6, 0xe7, 0xdb, 0x61, 0x0b, 0x8f, 0xed, 0xd8, 0x72, 0x2b, 0xbe, 0xf5, 0x3f, - 0x4c, 0x41, 0x1e, 0xe3, 0x85, 0xf4, 0x61, 0x93, 0xfa, 0x2d, 0x32, 0x17, 0xe9, 0x53, 0x6e, 0x79, - 0x54, 0x51, 0x8a, 0xaf, 0xc1, 0x28, 0x1b, 0x58, 0xdb, 0x42, 0x51, 0x8d, 0x2f, 0xaf, 0x3e, 0x9d, - 0xae, 0x65, 0x98, 0xfe, 0xae, 0x1a, 0x99, 0xf0, 0xc8, 0x5d, 0xb7, 0xd8, 0x44, 0x09, 0x0e, 0xa4, - 0x53, 0xcc, 0x7e, 0xb6, 0x75, 0x30, 0x35, 0xbc, 0x0e, 0xbe, 0x04, 0x93, 0x76, 0x50, 0xb1, 0xec, - 0x40, 0xe4, 0xb4, 0xc8, 0xa8, 0xc7, 0x84, 0x1d, 0xac, 0xb6, 0x81, 0x64, 0x19, 0x32, 0x8d, 0x47, - 0x32, 0x90, 0x31, 0xd9, 0x33, 0xab, 0x35, 0xda, 0x8b, 0xb4, 0x05, 0x54, 0x7e, 0xc0, 0xea, 0x18, - 0xbc, 0x2a, 0x97, 0x6c, 0x14, 0x6d, 0xd3, 0xae, 0x8c, 0x28, 0x31, 0xb3, 0x2b, 0x50, 0x08, 0x0e, - 0x9c, 0x8a, 0xeb, 0x55, 0xaa, 0x9e, 0x1b, 0x84, 0x26, 0xf3, 0xc5, 0xb2, 0x7c, 0x2e, 0x04, 0x07, - 0xce, 0xa6, 0xb7, 0x22, 0xa1, 0x98, 0x52, 0x7d, 0xe0, 0x54, 0x82, 0x66, 0xbd, 0x6e, 0xfa, 0x2d, - 0x91, 0x08, 0x05, 0xc1, 0x81, 0xb3, 0xc5, 0x21, 0xfa, 0x4b, 0x90, 0xc1, 0x66, 0x99, 0x53, 0xf0, - 0xc0, 0x58, 0x7b, 0xb0, 0x64, 0xac, 0x6f, 0xde, 0x2a, 0x9c, 0x61, 0x9f, 0x6b, 0x5f, 0x5d, 0x5b, - 0x61, 0x3e, 0xc2, 0xad, 0x82, 0xa6, 0xbf, 0x01, 0xd3, 0xcc, 0x75, 0xde, 0xa2, 0x41, 0xa0, 0x04, - 0x7a, 0x19, 0x93, 0xcd, 0x80, 0xfa, 0x8a, 0x07, 0x19, 0x7d, 0xeb, 0xff, 0x37, 0x0d, 0x63, 0x02, - 0xff, 0x99, 0xae, 0xf8, 0x2a, 0x0f, 0x23, 0x71, 0x1e, 0xd8, 0x78, 0x55, 0x1d, 0x9b, 0xba, 0x61, - 0x94, 0xe8, 0xc5, 0x4d, 0xc6, 0x04, 0x87, 0xca, 0x7c, 0xb1, 0xab, 0x50, 0xc0, 0x20, 0x67, 0x15, - 0x6f, 0x7b, 0xf0, 0x9c, 0x31, 0xbe, 0x1d, 0x9a, 0x52, 0xe0, 0x22, 0x6f, 0x6c, 0x12, 0x13, 0x13, - 0x68, 0x45, 0x84, 0x7f, 0x44, 0x0e, 0xca, 0xe5, 0xc1, 0xc6, 0x58, 0x26, 0x8b, 0x98, 0x11, 0x88, - 0x99, 0x8b, 0x48, 0x25, 0x47, 0x87, 0x57, 0xc9, 0x6b, 0x70, 0xd6, 0x31, 0x83, 0xb0, 0xa2, 0x70, - 0xd5, 0x12, 0xca, 0x30, 0xc5, 0x0a, 0xba, 0x67, 0x60, 0x0e, 0x67, 0x99, 0x3a, 0x03, 0x3b, 0x0c, - 0x0a, 0x0c, 0x62, 0x50, 0xf2, 0xbd, 0x0c, 0xca, 0x12, 0x80, 0xe0, 0x23, 0x3c, 0x72, 0xd1, 0x60, - 0x24, 0xa7, 0x15, 0x8a, 0xa5, 0xc6, 0xc8, 0xf1, 0x5a, 0xdb, 0x47, 0x2e, 0x59, 0x86, 0xf9, 0xae, - 0xfe, 0xc4, 0x35, 0x9d, 0xdb, 0x99, 0x52, 0x47, 0xe7, 0x14, 0xad, 0xbf, 0x93, 0xce, 0x8e, 0x15, - 0xb2, 0xfa, 0x5f, 0xd2, 0xe0, 0xac, 0xaa, 0xb4, 0xdc, 0x99, 0x7b, 0x96, 0xaa, 0x78, 0xfa, 0x49, - 0xd7, 0xdf, 0xd5, 0x60, 0x26, 0x3e, 0x81, 0x84, 0xc7, 0xb2, 0x0a, 0xd9, 0x40, 0xc0, 0x84, 0xcb, - 0x92, 0x24, 0x2f, 0x51, 0x5d, 0x46, 0x62, 0x64, 0x4d, 0x72, 0xa7, 0xc3, 0xcd, 0x48, 0xb2, 0x38, - 0x5d, 0x22, 0x89, 0x7b, 0x1a, 0xfa, 0x01, 0x90, 0x15, 0xd3, 0xad, 0x52, 0x07, 0xc5, 0xda, 0x77, - 0x77, 0x73, 0x19, 0xb2, 0x7c, 0x8c, 0x6c, 0x1e, 0xeb, 0xcc, 0x2d, 0xe7, 0x99, 0xbb, 0x8b, 0x95, - 0x99, 0xdb, 0x8a, 0x85, 0x1d, 0xd3, 0x34, 0xd5, 0x61, 0x2a, 0x6e, 0xc1, 0x74, 0xac, 0x49, 0x21, - 0x1b, 0xb6, 0xd5, 0x44, 0x30, 0xb5, 0x44, 0x80, 0x3b, 0xfa, 0x6e, 0x9f, 0xba, 0x8c, 0x28, 0xa7, - 0x2e, 0x7a, 0x0b, 0x66, 0x38, 0x21, 0xd1, 0xc1, 0xbe, 0xdc, 0xbf, 0x0a, 0x20, 0x84, 0x28, 0xf9, - 0x1f, 0xe7, 0xd9, 0x17, 0x82, 0xc0, 0xfa, 0xaa, 0x91, 0x13, 0x08, 0x7d, 0xfa, 0xb0, 0x0e, 0xb3, - 0x1d, 0x4d, 0x3f, 0x71, 0x2f, 0x9e, 0x83, 0x0b, 0x6c, 0x90, 0x56, 0xa2, 0xab, 0x85, 0x78, 0x04, - 0x19, 0x65, 0xd7, 0x48, 0xbd, 0x8e, 0x52, 0x7d, 0x7e, 0x86, 0x7a, 0xfd, 0x8f, 0x35, 0xb8, 0xd8, - 0x9b, 0x57, 0xd1, 0xfb, 0x8d, 0xe8, 0xb8, 0x95, 0x1f, 0x1c, 0xc7, 0xb6, 0xbf, 0x07, 0x4e, 0x59, - 0xbd, 0x4c, 0x59, 0xde, 0xa2, 0xbe, 0x6d, 0x3a, 0xf6, 0xd7, 0xa9, 0x65, 0xd0, 0x1a, 0x5b, 0x4f, - 0x5b, 0x91, 0x6e, 0x22, 0x8d, 0xa1, 0xf5, 0x3c, 0x26, 0xa2, 0x0e, 0x3d, 0x3f, 0x0f, 0xe7, 0x18, - 0x0a, 0x5b, 0xb7, 0xb7, 0x1e, 0x6e, 0x7c, 0xe0, 0x78, 0x1f, 0x47, 0x12, 0xfe, 0xaf, 0x29, 0x20, - 0x02, 0x6e, 0xd0, 0xba, 0x17, 0x52, 0x2c, 0x25, 0x7b, 0x30, 0xb6, 0xe7, 0x78, 0x1f, 0x57, 0x22, - 0xaf, 0xf8, 0x9e, 0xf0, 0x54, 0xbe, 0x30, 0x90, 0x70, 0x3b, 0x2e, 0x86, 0x96, 0x19, 0x51, 0xf4, - 0x58, 0x46, 0xf9, 0x2f, 0x63, 0x94, 0x51, 0x5f, 0xb7, 0xc8, 0x26, 0x64, 0x6c, 0x77, 0xcf, 0x93, - 0x9d, 0x4c, 0xca, 0xb2, 0xe9, 0xe6, 0x52, 0x4d, 0xb9, 0xe4, 0x64, 0x4a, 0xff, 0x47, 0x83, 0x34, - 0x7a, 0xf4, 0xcf, 0x52, 0x47, 0x96, 0x21, 0x17, 0xdd, 0xa7, 0x1c, 0xca, 0xbd, 0x6f, 0x57, 0x63, - 0xca, 0x22, 0x42, 0xc0, 0x3c, 0x86, 0xf2, 0xe6, 0x70, 0x3d, 0x17, 0xdb, 0x19, 0x41, 0x43, 0x7f, - 0x1e, 0x46, 0xc5, 0x96, 0x3c, 0x0f, 0x63, 0xc6, 0xce, 0xe6, 0x26, 0xf7, 0x6c, 0x00, 0x46, 0x1f, - 0xee, 0xac, 0xed, 0xac, 0xad, 0x16, 0x34, 0xfd, 0xf7, 0x35, 0x28, 0x76, 0x2b, 0x81, 0x50, 0xdd, - 0x75, 0xc8, 0xb0, 0x01, 0x19, 0xe4, 0x7e, 0x5c, 0x37, 0x33, 0xd1, 0x79, 0x21, 0x6a, 0xce, 0x67, - 0xa9, 0xb7, 0xff, 0x41, 0x83, 0xc2, 0x56, 0xc3, 0x74, 0x63, 0x31, 0xa2, 0x17, 0x3a, 0x0c, 0xdc, - 0x32, 0xb4, 0x47, 0x36, 0x1a, 0x22, 0x43, 0xcd, 0x32, 0xe1, 0xb6, 0xee, 0xe6, 0x4f, 0x8f, 0x17, - 0xde, 0x18, 0x6e, 0x87, 0x7b, 0x97, 0xb6, 0x94, 0xe4, 0x94, 0xcd, 0x76, 0x72, 0x4a, 0xea, 0x69, - 0x28, 0x8a, 0x9c, 0x16, 0x66, 0x50, 0xce, 0x2a, 0xbd, 0x13, 0x43, 0xb1, 0x00, 0x79, 0x7e, 0xa0, - 0x50, 0xf5, 0x9a, 0x6e, 0x28, 0x0e, 0xd5, 0x01, 0x41, 0x2b, 0x0c, 0x42, 0xde, 0x84, 0x39, 0xb3, - 0xd1, 0xf0, 0xbd, 0x23, 0xbb, 0x6e, 0x86, 0x94, 0x79, 0xe8, 0xfb, 0xc2, 0x4f, 0xe1, 0x19, 0x6f, - 0x33, 0x4a, 0xe9, 0xaa, 0x1d, 0xec, 0x73, 0x77, 0x65, 0x03, 0xf2, 0x22, 0xb3, 0x54, 0xc4, 0xcb, - 0xba, 0x72, 0x66, 0x3a, 0x03, 0x77, 0xf7, 0xbe, 0xb2, 0xb2, 0x82, 0xac, 0xc9, 0x2b, 0x74, 0x3c, - 0xfd, 0x14, 0xe3, 0x65, 0x5f, 0x80, 0x19, 0x91, 0x2a, 0x11, 0x0f, 0xfb, 0x0e, 0x32, 0x36, 0xfa, - 0xf7, 0x27, 0x60, 0xb6, 0xa3, 0x76, 0x77, 0x88, 0x28, 0xfb, 0x59, 0x4f, 0xda, 0x3f, 0xd0, 0x60, - 0x5a, 0xa6, 0x73, 0xa8, 0xf7, 0xb1, 0x72, 0x7d, 0xef, 0x63, 0xf5, 0xe4, 0xb5, 0x1c, 0xa5, 0x8a, - 0xf4, 0xbe, 0x8f, 0xd5, 0x51, 0xfc, 0xe4, 0xf7, 0xb1, 0x1a, 0x1d, 0xed, 0x94, 0xfe, 0x4d, 0x8e, - 0xe7, 0xb8, 0x47, 0xf9, 0x71, 0x5d, 0x19, 0x35, 0x5a, 0x8f, 0x8c, 0x9a, 0x3f, 0xa7, 0xc1, 0xac, - 0x92, 0x32, 0x57, 0xe9, 0x8c, 0x5d, 0xdd, 0x3f, 0x39, 0x5e, 0x98, 0xde, 0x69, 0x23, 0x3c, 0xf5, - 0xf9, 0xd5, 0x74, 0xb3, 0x93, 0x98, 0x15, 0x90, 0xdf, 0xd5, 0xe0, 0xb2, 0x92, 0x6f, 0xd7, 0x95, - 0xae, 0xa7, 0xb0, 0x95, 0x42, 0xb6, 0x7e, 0xfe, 0xe4, 0x78, 0xe1, 0x52, 0x3b, 0x19, 0x2f, 0x9e, - 0xc0, 0xf7, 0xd4, 0x3c, 0x5e, 0xf2, 0x13, 0x29, 0x5b, 0x01, 0xf9, 0xb6, 0x06, 0xc5, 0x78, 0x8e, - 0xa0, 0xc2, 0x62, 0x1a, 0x59, 0x7c, 0x70, 0x72, 0xbc, 0x30, 0xb3, 0xa9, 0x64, 0x0c, 0x3e, 0x35, - 0x5b, 0x33, 0x6e, 0x17, 0x35, 0x2b, 0x20, 0x47, 0x40, 0x64, 0xd6, 0xa0, 0xc2, 0x43, 0x06, 0x79, - 0xb8, 0x7b, 0x72, 0xbc, 0x30, 0xb5, 0xc9, 0x73, 0x08, 0x9f, 0xba, 0xf9, 0x29, 0x57, 0x25, 0x64, - 0x05, 0xe4, 0xd7, 0x34, 0x38, 0xdf, 0x91, 0xeb, 0xa8, 0x70, 0x30, 0x8a, 0x1c, 0x6c, 0x9d, 0x1c, - 0x2f, 0x9c, 0xdb, 0x89, 0x23, 0x3d, 0x35, 0x27, 0xe7, 0x9a, 0xbd, 0x08, 0x5a, 0x01, 0xf9, 0x55, - 0x0d, 0x8a, 0xf1, 0xa4, 0x4a, 0x85, 0xa1, 0x1c, 0x32, 0x64, 0x9c, 0x1c, 0x2f, 0xcc, 0xdd, 0x3f, - 0xfc, 0x4c, 0xf9, 0x99, 0xf3, 0x0e, 0x7b, 0xb2, 0xf3, 0x5b, 0x1a, 0xe8, 0xa7, 0xa5, 0x6d, 0x2a, - 0x8c, 0x8d, 0x21, 0x63, 0x1f, 0x9d, 0x1c, 0x2f, 0xcc, 0x3f, 0xec, 0x99, 0xc4, 0xf9, 0xd4, 0x0c, - 0xce, 0x1f, 0x24, 0xd0, 0xb5, 0x02, 0xf2, 0x1b, 0x1a, 0x5c, 0xec, 0xce, 0x12, 0x55, 0x58, 0xcc, - 0xb6, 0x07, 0xd3, 0x88, 0xe7, 0x8c, 0x3e, 0xfd, 0x60, 0xfa, 0xbd, 0x08, 0x5a, 0x01, 0x5e, 0x30, - 0xed, 0x69, 0x4d, 0xfb, 0x5d, 0x30, 0xcd, 0x27, 0xbe, 0x28, 0xd1, 0xdb, 0x6c, 0xab, 0x96, 0x53, - 0x39, 0x0d, 0xbe, 0x93, 0xce, 0x6a, 0x85, 0xac, 0xfe, 0x36, 0x14, 0x6e, 0x7b, 0xe1, 0x13, 0xac, - 0x69, 0xbf, 0x3c, 0x06, 0x67, 0x95, 0x9a, 0x9f, 0xc3, 0x7d, 0xfc, 0x7f, 0xa5, 0xc1, 0xec, 0x23, - 0x2f, 0xe4, 0x63, 0xd7, 0xe3, 0x86, 0xf1, 0x4a, 0x82, 0x68, 0xba, 0x38, 0x6d, 0x43, 0xe2, 0xcb, - 0xd9, 0x03, 0xb1, 0x9c, 0x9d, 0xed, 0x2c, 0x7f, 0xe2, 0xf5, 0xec, 0xec, 0xa3, 0xce, 0x96, 0x4a, - 0x87, 0x90, 0x95, 0xe4, 0xc9, 0x17, 0x63, 0xf7, 0xb5, 0x7a, 0xdd, 0x8b, 0x44, 0xbc, 0x53, 0x2e, - 0x6a, 0xf5, 0xce, 0x61, 0x1e, 0xe9, 0x9d, 0xc3, 0x5c, 0xfa, 0xd7, 0x1a, 0x4c, 0x60, 0x22, 0x55, - 0x34, 0x5e, 0xcf, 0x3a, 0x4b, 0xeb, 0x23, 0x80, 0xf6, 0x90, 0x89, 0x71, 0xba, 0xf9, 0x44, 0xe3, - 0x14, 0x5d, 0x7b, 0x90, 0x18, 0xa5, 0x5f, 0xd2, 0x3a, 0xae, 0xbe, 0x0d, 0xe4, 0x16, 0x18, 0x6c, - 0x1b, 0xe2, 0xf9, 0x11, 0x37, 0xef, 0x0d, 0xc5, 0x4d, 0x4c, 0x7a, 0x86, 0xa0, 0x54, 0xfa, 0x45, - 0x98, 0xeb, 0xad, 0x4e, 0x3d, 0xe6, 0xf3, 0xfd, 0xf8, 0x7c, 0x7e, 0x77, 0xa8, 0xe6, 0xd5, 0xee, - 0xaa, 0x91, 0x9d, 0xab, 0x30, 0x3e, 0x68, 0xbe, 0xd2, 0x6f, 0x67, 0x44, 0xe6, 0xe2, 0xe7, 0x32, - 0x67, 0xd5, 0x78, 0xe9, 0xc8, 0x33, 0x88, 0x97, 0xfe, 0x4b, 0x0d, 0x66, 0x7c, 0xd1, 0x91, 0x98, - 0x49, 0xe0, 0x61, 0xcf, 0x9f, 0xeb, 0x17, 0x21, 0x56, 0x6e, 0xfc, 0x08, 0x22, 0xa7, 0x98, 0x83, - 0xce, 0xf2, 0x27, 0x37, 0x07, 0x7e, 0x67, 0x4b, 0xa5, 0xef, 0x76, 0x2a, 0x72, 0x09, 0xb2, 0x12, - 0x4b, 0x9e, 0x2a, 0xf9, 0xa7, 0x2a, 0x79, 0xaf, 0xd7, 0x80, 0xbe, 0x2c, 0x0f, 0x19, 0x52, 0x43, - 0x67, 0x7f, 0x8a, 0x63, 0x85, 0x4f, 0x60, 0xae, 0xb7, 0x48, 0x7a, 0xa8, 0xf4, 0xdd, 0xb8, 0x4a, - 0xdf, 0x1c, 0x58, 0xe8, 0xa7, 0xa8, 0xb3, 0x48, 0x95, 0x79, 0x0d, 0xc8, 0x6a, 0xfb, 0x25, 0xaf, - 0xbe, 0xa9, 0x08, 0x57, 0x84, 0x6d, 0xeb, 0x8f, 0xf9, 0xfb, 0x23, 0x30, 0x2e, 0xae, 0xf7, 0xf2, - 0x47, 0x9b, 0x9e, 0xb5, 0x15, 0x7c, 0x05, 0xce, 0x52, 0xb7, 0xea, 0xb7, 0x30, 0x84, 0x29, 0x13, - 0xe1, 0x71, 0x8f, 0x6e, 0x14, 0xda, 0x05, 0xe2, 0x3c, 0x63, 0x41, 0xee, 0x5b, 0x79, 0xea, 0x0a, - 0xdf, 0xe2, 0xf2, 0xad, 0x28, 0x66, 0xb7, 0xb4, 0x11, 0xf8, 0x1e, 0x38, 0xad, 0x20, 0xf0, 0x9d, - 0xef, 0x15, 0x28, 0x88, 0x03, 0xf6, 0x7d, 0xda, 0x12, 0x64, 0xf8, 0x3d, 0x27, 0x11, 0xde, 0xb8, - 0x4b, 0x5b, 0x9c, 0x54, 0x1c, 0x93, 0xd3, 0x1b, 0xed, 0xc0, 0x44, 0x9a, 0xfa, 0x87, 0x30, 0x29, - 0xa5, 0x1b, 0xa5, 0xe5, 0x49, 0x43, 0xda, 0xff, 0xd1, 0x10, 0x55, 0xda, 0xf2, 0xc4, 0x83, 0x57, - 0xd6, 0xbf, 0xad, 0xc1, 0xd9, 0x76, 0xbc, 0x76, 0xa8, 0x23, 0x0f, 0xcc, 0x56, 0xaa, 0xef, 0xda, - 0x2e, 0xb5, 0x64, 0x8e, 0x91, 0xfc, 0x26, 0x25, 0x35, 0x10, 0x98, 0xc2, 0x36, 0xa3, 0xa8, 0xca, - 0x1c, 0xa4, 0xa8, 0xcb, 0xa3, 0x7b, 0xb2, 0x84, 0x01, 0xf4, 0xff, 0x91, 0x03, 0xa2, 0xb2, 0x22, - 0x3a, 0xda, 0xc0, 0x34, 0x3b, 0x01, 0x15, 0x9d, 0xbd, 0x93, 0x9c, 0x8e, 0xd5, 0x41, 0xa2, 0xbc, - 0xe2, 0x39, 0x0e, 0xad, 0x86, 0xd4, 0x8a, 0xca, 0xba, 0xae, 0x4b, 0x28, 0x6d, 0x90, 0x15, 0x00, - 0x0c, 0x93, 0xf8, 0x34, 0xa0, 0xc3, 0x85, 0x32, 0x73, 0xac, 0x9e, 0xc1, 0xaa, 0x91, 0xb7, 0xa1, - 0x28, 0x5f, 0xd1, 0xa8, 0x98, 0x8d, 0x06, 0x06, 0xbe, 0x2a, 0x0d, 0x9f, 0xee, 0xd9, 0x47, 0x22, - 0xfe, 0x35, 0x2b, 0xcb, 0x97, 0x1a, 0x8d, 0x4d, 0xb3, 0x4e, 0x1f, 0x60, 0x21, 0xf9, 0x06, 0x8c, - 0xe3, 0x7d, 0x3d, 0xa6, 0x01, 0x9e, 0x2b, 0x63, 0x60, 0xdb, 0xc3, 0xf5, 0x78, 0x4d, 0x3c, 0x81, - 0x15, 0xf5, 0x7c, 0xbb, 0x4d, 0xb2, 0xab, 0xef, 0xb1, 0xf6, 0x4a, 0xff, 0x6d, 0x04, 0xe6, 0x65, - 0xf5, 0x1e, 0xf2, 0xe2, 0x57, 0x0a, 0xb2, 0x4c, 0x61, 0xa3, 0xfc, 0xcd, 0x8e, 0x83, 0x9d, 0x03, - 0xa7, 0xdc, 0xbb, 0xa2, 0x4c, 0x36, 0xdc, 0xa7, 0xad, 0x55, 0x33, 0x34, 0xd5, 0xa5, 0x6f, 0xe4, - 0xb3, 0x5e, 0xfa, 0xd6, 0x61, 0xc2, 0xac, 0xd5, 0x7c, 0x5a, 0xc3, 0xcd, 0x5a, 0x18, 0x0c, 0x35, - 0x8e, 0xe3, 0xed, 0xaa, 0xdb, 0x01, 0xf9, 0x0a, 0xcc, 0xc8, 0x6f, 0x0c, 0x66, 0xb0, 0x61, 0x3b, - 0x14, 0x8f, 0xa5, 0xe4, 0xaf, 0x9f, 0xef, 0xa2, 0xb8, 0x2a, 0x9e, 0xb6, 0xe3, 0x04, 0xbf, 0xcf, - 0x08, 0x4e, 0x2b, 0x04, 0xd6, 0x45, 0xfd, 0xd2, 0xdf, 0x1a, 0x81, 0x8b, 0x49, 0xaa, 0x49, 0xac, - 0xb6, 0xad, 0xcf, 0x5f, 0xdf, 0x78, 0x32, 0x0d, 0x48, 0x1c, 0x09, 0x5c, 0x3f, 0xbe, 0x8a, 0x91, - 0x4b, 0x34, 0x74, 0xcb, 0xb7, 0x79, 0xe4, 0xf2, 0xa7, 0xc7, 0x0b, 0xef, 0x0f, 0x69, 0x73, 0xeb, - 0xe1, 0x07, 0xb6, 0x5b, 0xa3, 0x7e, 0xc3, 0xb7, 0xdd, 0x50, 0xc4, 0x3e, 0xdf, 0x97, 0xd9, 0xf2, - 0x23, 0xdd, 0xd1, 0xca, 0xde, 0x4a, 0x12, 0xcb, 0x92, 0x2f, 0xfd, 0x58, 0x83, 0xcb, 0x83, 0x69, - 0x32, 0x31, 0xb8, 0x95, 0x08, 0x54, 0xa5, 0x7c, 0xad, 0xa3, 0xbd, 0x81, 0x26, 0x43, 0x0e, 0xc9, - 0x3c, 0x6b, 0xf5, 0xd4, 0xb7, 0x61, 0x7e, 0x45, 0x18, 0xcb, 0xf6, 0xe0, 0xc5, 0x8e, 0x9d, 0x23, - 0x13, 0xaa, 0x9d, 0x6a, 0x42, 0x47, 0x3a, 0x4d, 0xe8, 0xb7, 0x47, 0xa0, 0x14, 0x91, 0x8b, 0xad, - 0xde, 0x0d, 0xcf, 0x0f, 0xc9, 0x64, 0x94, 0x25, 0x92, 0xc2, 0xf1, 0xb9, 0x08, 0xb9, 0xaa, 0x57, - 0x6f, 0x38, 0x34, 0x8c, 0x4c, 0x78, 0x1b, 0x40, 0x6e, 0xc0, 0x6c, 0x64, 0x14, 0x2b, 0x7b, 0xed, - 0xc1, 0x15, 0xe1, 0xb9, 0x99, 0xa8, 0x50, 0x19, 0x78, 0xf2, 0x3e, 0x14, 0xdb, 0x95, 0x94, 0x97, - 0x40, 0x99, 0x10, 0x55, 0x8b, 0x3f, 0x17, 0xf4, 0x60, 0x13, 0x33, 0xf0, 0xc6, 0x7d, 0x2e, 0x00, - 0xfe, 0x42, 0x45, 0x66, 0x88, 0x59, 0x9b, 0x8f, 0x6a, 0x2e, 0x85, 0xfa, 0x2f, 0xc0, 0xcb, 0x2b, - 0x3e, 0x35, 0x43, 0x7a, 0xba, 0x3c, 0xa4, 0xa4, 0x4f, 0xed, 0xa8, 0x76, 0x7a, 0x47, 0xf5, 0x16, - 0x5c, 0xe9, 0x4f, 0x5f, 0x2c, 0x61, 0xf7, 0x60, 0xd4, 0x47, 0x88, 0x50, 0xcc, 0x9b, 0x83, 0x4c, - 0xe5, 0x6e, 0x72, 0x82, 0x88, 0xfe, 0x22, 0xe8, 0xa7, 0x63, 0x45, 0x81, 0xb6, 0x3f, 0x03, 0x2f, - 0x24, 0x62, 0x09, 0xde, 0x76, 0x60, 0x8c, 0x93, 0x95, 0x6b, 0xeb, 0x93, 0x31, 0x27, 0x4d, 0xbb, - 0xa0, 0xa5, 0xff, 0x8e, 0x06, 0x33, 0xbd, 0xb0, 0xbb, 0x74, 0xf0, 0x54, 0xe1, 0x8f, 0x24, 0x68, - 0xd9, 0x2d, 0x18, 0xaf, 0xca, 0xc9, 0x2c, 0x9f, 0x39, 0x19, 0x58, 0x4b, 0xa2, 0x9a, 0x4b, 0x32, - 0x2b, 0xfc, 0x43, 0xb8, 0xd0, 0xbb, 0x67, 0x5c, 0x3f, 0xde, 0x49, 0xd0, 0x69, 0xde, 0x91, 0x53, - 0xb4, 0x59, 0x3f, 0x80, 0x8b, 0xbd, 0x09, 0x47, 0x0f, 0x37, 0xe4, 0x15, 0x7a, 0xc2, 0x4c, 0x2e, - 0x0e, 0x3b, 0x00, 0x2a, 0x0d, 0xfd, 0x06, 0x14, 0xef, 0x78, 0xbb, 0x32, 0xc6, 0x2b, 0xc2, 0x76, - 0xfd, 0x5c, 0xf2, 0xff, 0xa9, 0xc1, 0xf9, 0x1e, 0xb5, 0x3e, 0x87, 0x1d, 0xea, 0xd7, 0x60, 0xdc, - 0x6f, 0xba, 0xae, 0xed, 0xd6, 0x2a, 0x8f, 0xbd, 0x5d, 0x79, 0x2a, 0x90, 0x94, 0x5a, 0x7a, 0x2a, - 0x9f, 0x58, 0x92, 0x17, 0xd4, 0xee, 0x78, 0xbb, 0x41, 0x69, 0x16, 0x52, 0x77, 0xbc, 0xdd, 0x4e, - 0x95, 0xd3, 0xaf, 0x42, 0xe1, 0x8e, 0xb7, 0x1b, 0x17, 0xcd, 0x2c, 0x8c, 0x3e, 0xf6, 0x76, 0xdb, - 0x23, 0x9a, 0x79, 0xec, 0xed, 0xae, 0x5b, 0xfa, 0x1a, 0x9c, 0x55, 0x50, 0x85, 0x3c, 0x5e, 0x87, - 0xd4, 0x63, 0x6f, 0x57, 0xcc, 0xe5, 0xf9, 0x8e, 0x45, 0x06, 0x1f, 0x64, 0xe6, 0x8f, 0x33, 0x23, - 0x43, 0x0c, 0x55, 0xf7, 0x60, 0x06, 0xbd, 0xc2, 0xad, 0x87, 0x1b, 0xc3, 0x87, 0x16, 0xaf, 0xc3, - 0x2c, 0x7a, 0xa2, 0x95, 0x06, 0xf5, 0x03, 0x1b, 0x0d, 0x63, 0x7b, 0x55, 0xcd, 0x1a, 0xd3, 0x58, - 0xf8, 0x40, 0x96, 0xf1, 0x78, 0xd9, 0x39, 0x98, 0xed, 0x68, 0x90, 0xf3, 0xae, 0x2f, 0xc3, 0x85, - 0x75, 0xd7, 0xa2, 0x47, 0xf8, 0x30, 0x6a, 0x7b, 0xf1, 0x1b, 0xea, 0xec, 0xd1, 0x87, 0x8b, 0xbd, - 0x69, 0x08, 0xf9, 0x88, 0xb5, 0x98, 0x43, 0x7b, 0xbd, 0x3e, 0xa9, 0xae, 0xc5, 0xbd, 0x28, 0xa9, - 0x3e, 0x39, 0x87, 0x5c, 0xbb, 0x02, 0xd0, 0xbe, 0x67, 0x41, 0x66, 0xa0, 0x10, 0x65, 0xd9, 0x57, - 0xb6, 0xb6, 0x97, 0x56, 0xee, 0x6e, 0x15, 0xce, 0xe8, 0xe9, 0xac, 0x56, 0xd0, 0xae, 0xbd, 0x08, - 0x59, 0x79, 0x6f, 0x41, 0x49, 0xd0, 0x9f, 0x04, 0x88, 0x6a, 0x6c, 0x15, 0xb4, 0xeb, 0x7f, 0xed, - 0xcd, 0x28, 0x82, 0xfd, 0x7d, 0x0d, 0xc6, 0xd5, 0xb7, 0x5c, 0x49, 0x79, 0xb0, 0xd7, 0x5a, 0xa5, - 0xd0, 0x4a, 0x8b, 0x03, 0xe3, 0x8b, 0x41, 0x78, 0xf9, 0x5b, 0xff, 0xf6, 0xbf, 0xfc, 0x95, 0x91, - 0xe7, 0xc9, 0xc2, 0xa2, 0xd8, 0x8d, 0x2e, 0xaa, 0x4f, 0xbd, 0x2e, 0x7e, 0x22, 0x86, 0xe0, 0x53, - 0xf2, 0xe7, 0x35, 0x18, 0x93, 0xbb, 0xe4, 0xa4, 0xf4, 0xe6, 0xf8, 0xcb, 0xb0, 0xa5, 0x6b, 0x83, - 0xa0, 0x0a, 0x5e, 0x74, 0xe4, 0xe5, 0x22, 0x29, 0x45, 0xbc, 0x88, 0xeb, 0x51, 0x0a, 0x1b, 0xbb, - 0x30, 0x26, 0x9e, 0x68, 0x49, 0xe4, 0x22, 0xfe, 0x52, 0x4d, 0x22, 0x17, 0x1d, 0x2f, 0xbe, 0xe8, - 0x67, 0x88, 0x0f, 0x19, 0x7c, 0x87, 0x92, 0xbc, 0xdc, 0xff, 0xa5, 0x4a, 0x4e, 0xff, 0xca, 0xa0, - 0x4f, 0x5a, 0xea, 0x73, 0xd8, 0xc7, 0x02, 0x99, 0x8c, 0xfa, 0xc8, 0x9f, 0xcc, 0xfc, 0x06, 0xa4, - 0xf1, 0xd2, 0xc4, 0xe5, 0xbe, 0x2f, 0xf5, 0xf0, 0x16, 0x87, 0x7a, 0x42, 0x55, 0xbf, 0x84, 0xad, - 0x96, 0x48, 0x31, 0xde, 0xaa, 0x22, 0xd7, 0x5f, 0x84, 0x31, 0x64, 0x74, 0x67, 0x7d, 0xf0, 0x5e, - 0xbf, 0x3e, 0xec, 0x43, 0x9e, 0xfa, 0x79, 0xe4, 0x63, 0x9a, 0x9c, 0x8d, 0xf3, 0x51, 0x69, 0xda, - 0xe4, 0x1b, 0x80, 0x73, 0x7b, 0x67, 0x7d, 0x60, 0x11, 0x0c, 0xfa, 0x36, 0xa9, 0xfe, 0x02, 0xb6, - 0xfa, 0x1c, 0xb9, 0xd0, 0xd5, 0xaa, 0x22, 0x80, 0x4f, 0xf9, 0x6b, 0x3d, 0x78, 0x53, 0x80, 0xbc, - 0x32, 0xd8, 0x7d, 0x82, 0xd3, 0x87, 0xe2, 0xd4, 0xcb, 0x07, 0xfa, 0x2c, 0x32, 0x33, 0x45, 0x26, - 0x22, 0x66, 0x7c, 0x73, 0x2f, 0x24, 0xdf, 0xd4, 0x60, 0x94, 0x1f, 0xf1, 0x92, 0xbe, 0x2f, 0x35, - 0x44, 0x03, 0x70, 0x75, 0x00, 0x4c, 0xd1, 0xec, 0xf3, 0xd8, 0xec, 0x05, 0x72, 0x5e, 0x69, 0x96, - 0x21, 0x28, 0x12, 0x08, 0x60, 0x94, 0xdf, 0xf2, 0x4e, 0xe4, 0x20, 0x76, 0x11, 0xbc, 0xa4, 0x5e, - 0xa6, 0x13, 0x7f, 0x19, 0x60, 0xdd, 0xdd, 0xf3, 0x84, 0xda, 0x75, 0x37, 0x2a, 0xfe, 0x88, 0x40, - 0xbb, 0xd1, 0xbf, 0xaa, 0x41, 0x5e, 0xb9, 0x9e, 0x4c, 0x5e, 0x1b, 0xec, 0x1a, 0xb3, 0x6c, 0xbf, - 0x3c, 0x28, 0xba, 0x10, 0xc3, 0x65, 0xe4, 0xe8, 0x12, 0x99, 0x8f, 0x38, 0xe2, 0x39, 0x1f, 0xb8, - 0x8a, 0x29, 0x6c, 0x7d, 0x57, 0x83, 0x5c, 0x74, 0x7f, 0x34, 0x51, 0x1d, 0x3a, 0x6f, 0xcd, 0x26, - 0xaa, 0x43, 0xd7, 0x95, 0x56, 0xfd, 0x2a, 0x32, 0xf4, 0x02, 0x79, 0x3e, 0x62, 0xc8, 0x94, 0x38, - 0xa8, 0xa5, 0x0a, 0x4f, 0x3f, 0xd0, 0x60, 0x32, 0x7e, 0xbf, 0x98, 0xbc, 0x3e, 0x50, 0x5b, 0x4a, - 0x3c, 0xa0, 0xf4, 0xc6, 0x10, 0x35, 0x04, 0x8b, 0xaf, 0x20, 0x8b, 0x2f, 0x91, 0x17, 0x7a, 0xb0, - 0x88, 0x4a, 0xb4, 0xf8, 0x89, 0x3c, 0xd9, 0xff, 0x94, 0xfc, 0xb2, 0x06, 0xe3, 0x6a, 0xea, 0x69, - 0xe2, 0x0a, 0xd6, 0x23, 0xd7, 0x3c, 0x71, 0x05, 0xeb, 0x95, 0x5a, 0xdb, 0xc3, 0xa6, 0x44, 0xf9, - 0xb2, 0xdf, 0x13, 0x29, 0x94, 0xf8, 0x9e, 0xe8, 0xe7, 0xc7, 0xd1, 0x02, 0x72, 0x74, 0x9e, 0x9c, - 0x8b, 0x38, 0xc2, 0x97, 0x4b, 0x2b, 0x11, 0x5f, 0xdf, 0xd7, 0x20, 0xaf, 0x64, 0xc2, 0x26, 0x2a, - 0x7d, 0x77, 0x92, 0x6e, 0xa2, 0xd2, 0xf7, 0x48, 0xb0, 0xd5, 0xaf, 0x21, 0x3f, 0x2f, 0xea, 0xca, - 0x1a, 0x8f, 0x58, 0x3c, 0xeb, 0xba, 0xad, 0x61, 0xef, 0x69, 0xd7, 0xc8, 0x5f, 0xd7, 0xa0, 0xc0, - 0x69, 0xa0, 0xd0, 0x3e, 0x17, 0xfe, 0xc4, 0xa4, 0xd4, 0x2f, 0x74, 0xf2, 0xc7, 0xc5, 0x86, 0x5c, - 0x32, 0xde, 0xfe, 0x9e, 0xc8, 0xae, 0xee, 0xcc, 0x42, 0x25, 0x6f, 0xf5, 0x19, 0xa1, 0x53, 0x52, - 0x6c, 0x4b, 0x6f, 0x0f, 0x5d, 0xef, 0x54, 0x4f, 0xa5, 0x9d, 0xe0, 0x5a, 0x11, 0x49, 0xac, 0xff, - 0x48, 0x83, 0xf3, 0x91, 0xf2, 0xfd, 0xec, 0x59, 0xbe, 0x82, 0x2c, 0xeb, 0xe4, 0x52, 0x87, 0x52, - 0x76, 0x33, 0xfe, 0x37, 0x34, 0x28, 0x74, 0x66, 0x4b, 0x92, 0xeb, 0x7d, 0xda, 0xed, 0x91, 0x5f, - 0x5b, 0xba, 0x31, 0x54, 0x1d, 0xc1, 0xe7, 0x3c, 0xf2, 0x59, 0x24, 0x73, 0x6d, 0x27, 0xd0, 0x0e, - 0xc2, 0xe0, 0xc0, 0xa9, 0xf0, 0x1c, 0xcb, 0xdf, 0xc2, 0x27, 0xb9, 0x85, 0x58, 0x7f, 0x36, 0x2c, - 0xbe, 0x88, 0x2c, 0xce, 0x93, 0x8b, 0x1d, 0xa2, 0x8c, 0x33, 0xfa, 0x9b, 0x1a, 0x4c, 0xc4, 0x52, - 0xc5, 0xc9, 0x62, 0xdf, 0x79, 0x11, 0xcf, 0x67, 0x4f, 0x74, 0xb0, 0x7a, 0x66, 0xa1, 0xeb, 0xaf, - 0x22, 0x6b, 0x97, 0xf5, 0xe7, 0x3b, 0xa7, 0x92, 0xb0, 0x3d, 0xf1, 0xc9, 0xfe, 0x77, 0x34, 0x79, - 0x09, 0x40, 0xb5, 0x90, 0x9f, 0x07, 0x9f, 0x42, 0x1b, 0xf5, 0xe7, 0x7a, 0x4f, 0x79, 0xc1, 0x2d, - 0xe3, 0xf1, 0x9b, 0x1a, 0xe4, 0xa2, 0x4c, 0xd1, 0xc4, 0x95, 0xb8, 0x33, 0x5b, 0x36, 0x71, 0x25, - 0xee, 0x4a, 0x3e, 0xd5, 0x8b, 0xc8, 0x12, 0xd1, 0xdb, 0x8e, 0x59, 0xd0, 0x30, 0x91, 0x85, 0x6f, - 0xe0, 0xfe, 0xac, 0xba, 0x9f, 0xec, 0x9a, 0xc5, 0x2e, 0xf3, 0x27, 0x3a, 0xa7, 0xea, 0xe3, 0x13, - 0x3d, 0x7c, 0xa4, 0x00, 0x09, 0x29, 0x0b, 0xff, 0x9f, 0xd5, 0x60, 0x4c, 0xdc, 0x19, 0x4f, 0xdc, - 0xf4, 0xc4, 0xef, 0x95, 0x0f, 0xce, 0x42, 0xb7, 0x35, 0x6b, 0x70, 0x4a, 0x1d, 0x3c, 0x88, 0xab, - 0xe0, 0x89, 0x3c, 0xc4, 0xaf, 0x8b, 0x3f, 0x0d, 0x0f, 0xe2, 0xdd, 0x5e, 0x85, 0x87, 0xbf, 0xa0, - 0x41, 0x56, 0xde, 0xec, 0x27, 0x49, 0x5b, 0xba, 0x8e, 0xc7, 0x09, 0x4a, 0xaf, 0x0c, 0x84, 0x2b, - 0x38, 0xe9, 0xde, 0x2b, 0x61, 0xdc, 0x34, 0xee, 0xb3, 0x8e, 0xab, 0x2f, 0x4d, 0x24, 0x7b, 0x14, - 0xdd, 0x4f, 0x58, 0x24, 0x7b, 0x14, 0x3d, 0x9e, 0xb0, 0xe8, 0xb1, 0x83, 0x71, 0xbc, 0x5a, 0x27, - 0x5b, 0xbf, 0xa6, 0xc1, 0x98, 0xa8, 0x9d, 0x38, 0x44, 0xf1, 0x27, 0x2d, 0x4a, 0xaf, 0x25, 0xa3, - 0x76, 0x3c, 0xe8, 0x21, 0x9d, 0x09, 0xa2, 0x27, 0xb0, 0xb2, 0xf8, 0x09, 0x03, 0x7c, 0xca, 0x36, - 0xb5, 0x1b, 0x5e, 0x2d, 0x48, 0xdc, 0xd1, 0x29, 0xaf, 0x9e, 0x0c, 0xcb, 0x4a, 0x2f, 0x3f, 0xab, - 0xa6, 0x4a, 0xe4, 0x37, 0x34, 0x7c, 0xbc, 0xb3, 0x9d, 0x0a, 0x97, 0x68, 0xda, 0x7a, 0x65, 0x75, - 0x27, 0x9a, 0xb6, 0x9e, 0x59, 0x76, 0x3d, 0x16, 0x30, 0x91, 0xc6, 0x2c, 0xee, 0x8a, 0x7f, 0x4b, - 0x83, 0x5c, 0x94, 0xcf, 0x93, 0x68, 0xd0, 0x3a, 0xd3, 0xf1, 0x12, 0x0d, 0x5a, 0x57, 0x8a, 0x90, - 0x5e, 0x42, 0x46, 0x66, 0x08, 0x89, 0x18, 0x79, 0xe4, 0x85, 0x82, 0x89, 0x4f, 0x21, 0xc3, 0x77, - 0x10, 0x2f, 0xf7, 0x4f, 0xd1, 0xe8, 0x7f, 0xc4, 0x11, 0xdf, 0x2f, 0x9c, 0xb2, 0xd5, 0x54, 0x77, - 0x09, 0x3f, 0xd0, 0x20, 0xaf, 0x9e, 0xc4, 0x27, 0x5f, 0xba, 0xe8, 0x3c, 0x05, 0x2f, 0x7d, 0xb1, - 0x1b, 0x5d, 0xfd, 0x4b, 0x6f, 0xb1, 0xbf, 0x01, 0xa7, 0xd4, 0xe7, 0xe1, 0x81, 0x1e, 0x7b, 0x40, - 0xf5, 0x0f, 0xc8, 0xb5, 0xb5, 0x87, 0x6d, 0xc9, 0x79, 0xa6, 0x43, 0x1f, 0xbb, 0xaf, 0xa4, 0x9a, - 0x24, 0x6e, 0xc9, 0xe3, 0x69, 0x13, 0x3d, 0x2d, 0x3f, 0x43, 0x50, 0x58, 0xf8, 0x8b, 0x1a, 0x9e, - 0x35, 0xca, 0x6c, 0x80, 0x57, 0x07, 0x8c, 0xbb, 0xf6, 0x9f, 0x4d, 0xdd, 0x51, 0x5a, 0xfd, 0x02, - 0xb2, 0x33, 0x4b, 0xa6, 0xd5, 0x85, 0x48, 0xb6, 0xfc, 0x03, 0x0d, 0xe6, 0xba, 0x82, 0x84, 0x7c, - 0x49, 0x4e, 0xca, 0x5b, 0x4b, 0x8e, 0x2b, 0x0e, 0xcb, 0x61, 0xf7, 0xcc, 0x92, 0x49, 0x1e, 0x41, - 0x58, 0x0f, 0x03, 0xf2, 0x23, 0x0d, 0x2e, 0xf5, 0x0b, 0x84, 0x91, 0xe5, 0x24, 0x76, 0x07, 0x8b, - 0xd2, 0x95, 0x56, 0x9e, 0x8a, 0x46, 0xdc, 0xa6, 0xeb, 0x45, 0x45, 0xde, 0xf5, 0x90, 0xa9, 0xa2, - 0x08, 0x5c, 0x31, 0xd7, 0xe3, 0x9f, 0x6b, 0xa7, 0x85, 0x6d, 0x90, 0x93, 0x80, 0x7c, 0xe9, 0x89, - 0x42, 0x64, 0xd1, 0x08, 0xbc, 0xff, 0xa4, 0xd5, 0x4f, 0x5d, 0x2c, 0x3b, 0x3a, 0x41, 0xfe, 0xe9, - 0x69, 0xd1, 0xb7, 0xb7, 0x86, 0x6e, 0xba, 0xff, 0x0e, 0x28, 0x29, 0xc2, 0xa5, 0xbf, 0x89, 0xbc, - 0x96, 0xc9, 0xab, 0x5d, 0xbc, 0x2e, 0x7e, 0x72, 0x5a, 0x50, 0xed, 0x53, 0xf2, 0xdb, 0x1a, 0xc6, - 0x5d, 0xe2, 0x71, 0x1e, 0x72, 0x63, 0xb8, 0xa8, 0x10, 0xe7, 0xfc, 0xcd, 0x27, 0x09, 0x25, 0xf5, - 0x38, 0xa1, 0x7f, 0xec, 0xed, 0x56, 0x7c, 0x81, 0x1c, 0x77, 0x8f, 0x72, 0x51, 0x84, 0x28, 0x71, - 0x61, 0xe9, 0x0c, 0x39, 0x25, 0x2e, 0x2c, 0x5d, 0x41, 0x27, 0xfd, 0x39, 0xe4, 0xe8, 0x1c, 0x99, - 0x55, 0x39, 0x5a, 0xfc, 0x84, 0x07, 0xad, 0x3e, 0x25, 0xdf, 0xd3, 0xf0, 0xed, 0xb4, 0x76, 0xc4, - 0x27, 0x71, 0xd5, 0xed, 0x15, 0x8c, 0x4a, 0x5c, 0x75, 0x7b, 0x07, 0x93, 0x84, 0x31, 0xd5, 0xdb, - 0xb6, 0x01, 0x63, 0x51, 0xc1, 0x81, 0x83, 0x47, 0x7b, 0x6c, 0x2e, 0xfd, 0xae, 0x06, 0x33, 0xbd, - 0x42, 0x3c, 0x89, 0x9a, 0x98, 0x10, 0xa1, 0x4a, 0xd4, 0xc4, 0xa4, 0xa8, 0x94, 0xfe, 0x12, 0x32, - 0xbb, 0x40, 0xda, 0xbb, 0x1f, 0xfc, 0xd3, 0xa7, 0xf8, 0x97, 0x03, 0xdb, 0x81, 0xa6, 0xe5, 0x6b, - 0x3f, 0xfc, 0xcf, 0xf3, 0x67, 0x7e, 0x78, 0x32, 0xaf, 0xfd, 0xe8, 0x64, 0x5e, 0xfb, 0xf1, 0xc9, - 0xbc, 0xf6, 0x9f, 0x4e, 0xe6, 0xb5, 0x5f, 0xff, 0xc9, 0xfc, 0x99, 0x1f, 0xfd, 0x64, 0xfe, 0xcc, - 0x8f, 0x7f, 0x32, 0x7f, 0xe6, 0xa3, 0xac, 0x6c, 0x6f, 0x77, 0x14, 0x03, 0xcd, 0x37, 0xfe, 0x5f, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x3e, 0xf2, 0xb9, 0x50, 0xf2, 0x75, 0x00, 0x00, + // 8107 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x7d, 0x6b, 0x6c, 0x5b, 0x49, + 0x96, 0x9e, 0xaf, 0x48, 0x4a, 0xe4, 0xa1, 0x1e, 0x74, 0xe9, 0x61, 0x9a, 0x76, 0x4b, 0xf6, 0x75, + 0xb7, 0xdb, 0x76, 0xbb, 0x29, 0xb7, 0xdc, 0xee, 0xd7, 0xcc, 0xf4, 0x8e, 0x5e, 0x6d, 0xcb, 0x96, + 0x65, 0xfb, 0x4a, 0x9a, 0x1e, 0xf4, 0x6c, 0x96, 0xb9, 0x22, 0x4b, 0xf4, 0xb5, 0x2e, 0xef, 0xa5, + 0xee, 0xbd, 0x54, 0x8b, 0xd3, 0xe9, 0xd9, 0xc9, 0xe4, 0xb1, 0x93, 0xdd, 0xcd, 0xec, 0xce, 0x26, + 0x03, 0x4c, 0x90, 0x20, 0xd9, 0xcc, 0x8f, 0xdd, 0x60, 0x83, 0x2c, 0x76, 0x10, 0xe4, 0xf5, 0x23, + 0x0f, 0xe4, 0xc7, 0x66, 0x36, 0x01, 0x82, 0x01, 0x92, 0x1f, 0x83, 0x04, 0xd1, 0x26, 0x9a, 0x20, + 0x09, 0x92, 0x5f, 0x41, 0xf2, 0xab, 0x81, 0x0d, 0x82, 0x3a, 0x55, 0x75, 0x59, 0x97, 0xa4, 0x2e, + 0x49, 0x3f, 0x7a, 0x16, 0xd8, 0x1f, 0xdd, 0xe6, 0xad, 0xc7, 0xa9, 0xaf, 0x4e, 0x9d, 0x3a, 0x75, + 0xaa, 0xce, 0xa9, 0x12, 0x9c, 0xf7, 0xa9, 0x77, 0x40, 0xbd, 0x79, 0xfe, 0x4f, 0x7d, 0x67, 0xde, + 0x0f, 0xcc, 0xa0, 0xe1, 0x17, 0xeb, 0x9e, 0x1b, 0xb8, 0xe4, 0x6c, 0xd9, 0x2d, 0xef, 0x79, 0xae, + 0x59, 0x7e, 0x5c, 0xe4, 0x05, 0x8a, 0xb2, 0x5c, 0x21, 0xb7, 0xd3, 0xb0, 0xec, 0xca, 0xbc, 0xe5, + 0xec, 0xba, 0xbc, 0x70, 0x61, 0xb2, 0xea, 0xfa, 0xbe, 0x55, 0x9f, 0xe7, 0xff, 0x88, 0xc4, 0x99, + 0x27, 0xee, 0x8e, 0x3f, 0xcf, 0xfe, 0x57, 0xdf, 0xc1, 0x7f, 0x44, 0xfa, 0x19, 0xa4, 0x5a, 0xdf, + 0x99, 0x37, 0xeb, 0xf5, 0x12, 0x6b, 0x53, 0x66, 0x10, 0x99, 0x51, 0x31, 0x03, 0x53, 0xa4, 0xcd, + 0xc9, 0x34, 0xcb, 0xa9, 0xd0, 0xc3, 0x52, 0xc3, 0x37, 0xab, 0x34, 0x52, 0x69, 0x46, 0x16, 0xa8, + 0xd1, 0xc0, 0x54, 0x2a, 0x2e, 0x88, 0xde, 0x55, 0x2c, 0xb3, 0xea, 0xb8, 0x7e, 0x60, 0x95, 0x7d, + 0xf5, 0x37, 0x6b, 0xa5, 0xf5, 0x25, 0xea, 0x5c, 0x92, 0x1c, 0x41, 0x46, 0x88, 0x7f, 0xda, 0x18, + 0x53, 0xb8, 0xe8, 0xef, 0xdb, 0xf3, 0x65, 0xd7, 0x09, 0xa8, 0x13, 0x58, 0xae, 0x53, 0xdf, 0x51, + 0x3e, 0x44, 0x91, 0xb3, 0xac, 0x08, 0x3d, 0xa4, 0x65, 0xcb, 0xd9, 0xf5, 0x4c, 0xec, 0xa9, 0x25, + 0xb2, 0x5e, 0xf2, 0x03, 0xd7, 0x33, 0xab, 0x74, 0x9e, 0x3a, 0x55, 0xcb, 0xa1, 0xf5, 0x1d, 0xf1, + 0x43, 0x64, 0x9f, 0xeb, 0xc8, 0xae, 0x1d, 0x94, 0xcb, 0x22, 0x73, 0xb6, 0x23, 0xd3, 0x73, 0xcb, + 0x7b, 0x7e, 0x65, 0x47, 0xe4, 0x5f, 0xdd, 0x3b, 0x98, 0xdf, 0x3b, 0x10, 0x7d, 0x90, 0x3f, 0xea, + 0x3b, 0xf3, 0x36, 0x35, 0x7d, 0xce, 0xb4, 0xb0, 0x13, 0xfa, 0x09, 0x45, 0x59, 0x21, 0x89, 0xa5, + 0xa8, 0x96, 0xb1, 0xad, 0x03, 0xea, 0x50, 0xdf, 0x0f, 0x7f, 0x30, 0xba, 0xe2, 0xa7, 0x28, 0x9f, + 0x6f, 0x04, 0x96, 0x3d, 0x6f, 0xbb, 0x55, 0xf6, 0x1f, 0xcb, 0x76, 0xab, 0x22, 0xa7, 0x80, 0x39, + 0x0d, 0xc7, 0xa3, 0xbe, 0x6b, 0x1f, 0xd0, 0x4a, 0xc9, 0xac, 0x54, 0xbc, 0x48, 0xad, 0xc7, 0x76, + 0x79, 0x3e, 0xb0, 0x6a, 0xd4, 0x0f, 0xcc, 0x9a, 0x94, 0x9f, 0x59, 0x1a, 0x94, 0x2b, 0xf3, 0x9e, + 0xb9, 0x1b, 0xcc, 0x1f, 0xdc, 0xc4, 0x7f, 0x59, 0x8f, 0xcd, 0xdd, 0x40, 0xe4, 0x4f, 0x55, 0xdd, + 0xaa, 0x8b, 0x3f, 0xe7, 0xd9, 0x2f, 0x91, 0x7a, 0xbe, 0xea, 0xba, 0x55, 0x9b, 0x32, 0x96, 0xcf, + 0x9b, 0x8e, 0xe3, 0x06, 0x26, 0x1b, 0x18, 0x89, 0x71, 0x4e, 0xe4, 0xe2, 0xd7, 0x4e, 0x63, 0xb7, + 0xb3, 0xd1, 0xf6, 0x02, 0x95, 0x86, 0x67, 0xb6, 0x86, 0x56, 0x2f, 0xc2, 0xe4, 0x32, 0xf5, 0x02, + 0x6b, 0xd7, 0x2a, 0x9b, 0x01, 0xf5, 0x0d, 0xba, 0xdf, 0xa0, 0x7e, 0x40, 0xce, 0xc0, 0x88, 0xe3, + 0x56, 0x68, 0xc9, 0xaa, 0xe4, 0xb5, 0x0b, 0xda, 0x95, 0x8c, 0x31, 0xcc, 0x3e, 0xd7, 0x2a, 0xfa, + 0x1f, 0x25, 0x81, 0x28, 0x15, 0x56, 0x68, 0x60, 0x5a, 0xb6, 0x4f, 0x1e, 0x41, 0x32, 0x68, 0xd6, + 0x29, 0x16, 0x1e, 0x5f, 0xf8, 0x52, 0xf1, 0xc4, 0xc9, 0x56, 0xec, 0xac, 0xac, 0x26, 0x6d, 0x35, + 0xeb, 0xd4, 0x40, 0x52, 0xe4, 0x12, 0x8c, 0x51, 0xcf, 0x73, 0xbd, 0x52, 0x8d, 0xfa, 0x6c, 0x96, + 0xe4, 0x87, 0x10, 0xc8, 0x28, 0x26, 0xde, 0xe7, 0x69, 0x84, 0x40, 0x92, 0xcd, 0x91, 0x7c, 0xe2, + 0x82, 0x76, 0x65, 0xd4, 0xc0, 0xdf, 0xc4, 0x80, 0xe1, 0x5d, 0x8b, 0xda, 0x15, 0x3f, 0x9f, 0xbc, + 0x90, 0xb8, 0x92, 0x5d, 0x78, 0x73, 0x30, 0x34, 0x1f, 0x60, 0xdd, 0xa5, 0xe4, 0x8f, 0x8e, 0xe6, + 0x4e, 0x19, 0x82, 0x52, 0xe1, 0xef, 0x0f, 0xc1, 0x30, 0xcf, 0x20, 0x33, 0x30, 0x6c, 0xf9, 0x7e, + 0x83, 0x7a, 0x92, 0x33, 0xfc, 0x8b, 0xe4, 0x61, 0xc4, 0x6f, 0xec, 0x3c, 0xa1, 0xe5, 0x40, 0x20, + 0x95, 0x9f, 0xe4, 0x25, 0x80, 0x03, 0xd3, 0xb6, 0x2a, 0xa5, 0x5d, 0xcf, 0xad, 0x21, 0xd4, 0x84, + 0x91, 0xc1, 0x94, 0x0f, 0x3c, 0xb7, 0x46, 0xe6, 0x20, 0xcb, 0xb3, 0x1b, 0x4e, 0x60, 0xd9, 0xf9, + 0x24, 0xe6, 0xf3, 0x1a, 0xdb, 0x2c, 0x85, 0x9c, 0x87, 0x0c, 0x13, 0x30, 0xea, 0xfb, 0xd4, 0xcf, + 0xa7, 0x2e, 0x24, 0xae, 0x64, 0x8c, 0x56, 0x02, 0x99, 0x87, 0x49, 0xdf, 0xaa, 0x3a, 0x66, 0xd0, + 0xf0, 0x68, 0xc9, 0xb4, 0xab, 0xae, 0x67, 0x05, 0x8f, 0x6b, 0xf9, 0x61, 0xc4, 0x40, 0xc2, 0xac, + 0x45, 0x99, 0xc3, 0xe0, 0xd4, 0x1b, 0x3b, 0xb6, 0x55, 0x2e, 0xed, 0xd1, 0x66, 0x7e, 0x04, 0xcb, + 0x65, 0x78, 0xca, 0x3d, 0xda, 0x24, 0xe7, 0x20, 0xb3, 0x47, 0x9b, 0x5c, 0x33, 0xe5, 0xd3, 0xd8, + 0x5a, 0x7a, 0x8f, 0x36, 0xb7, 0x91, 0xdf, 0xd7, 0x81, 0xd0, 0xc3, 0x80, 0x3a, 0x15, 0x5a, 0x29, + 0xb5, 0x4a, 0x65, 0xb0, 0x54, 0x4e, 0xe6, 0xdc, 0x13, 0xa5, 0xf5, 0x47, 0x30, 0xd1, 0x36, 0xb6, + 0x64, 0x18, 0x86, 0x96, 0x17, 0x73, 0xa7, 0x48, 0x1a, 0x92, 0x1b, 0x0f, 0x56, 0x56, 0x73, 0x1a, + 0x19, 0x83, 0xcc, 0xf2, 0xfa, 0xda, 0xea, 0xc6, 0x56, 0x69, 0x79, 0x31, 0x37, 0x44, 0x00, 0x86, + 0xf9, 0x67, 0x2e, 0x41, 0x32, 0x90, 0xda, 0x5e, 0x63, 0xc9, 0x49, 0x56, 0x6f, 0x7b, 0x2d, 0x97, + 0xd2, 0x5d, 0x98, 0x8a, 0xca, 0xab, 0x5f, 0x77, 0x1d, 0x9f, 0x92, 0x0f, 0x61, 0xb4, 0xac, 0xa4, + 0xe7, 0x35, 0x1c, 0xfa, 0xd7, 0x07, 0x1a, 0x7a, 0x31, 0xe6, 0x11, 0x42, 0xfa, 0x3c, 0x8c, 0x8b, + 0xec, 0x5e, 0x73, 0xe3, 0x6e, 0x32, 0x3d, 0x94, 0x4b, 0xe8, 0x1b, 0x00, 0x9b, 0x4d, 0x3f, 0xa0, + 0xb5, 0x35, 0x67, 0xd7, 0x65, 0x83, 0xeb, 0xe3, 0x57, 0x89, 0x2d, 0x2f, 0xa2, 0x02, 0xf8, 0x91, + 0x02, 0x7b, 0xd4, 0x73, 0xa8, 0xcd, 0x0b, 0x70, 0xd1, 0x01, 0x9e, 0xc4, 0x0a, 0xe8, 0xbf, 0x9a, + 0x80, 0x89, 0x10, 0x81, 0xe8, 0xed, 0x47, 0x51, 0x08, 0xa9, 0xa5, 0xc5, 0xe3, 0xa3, 0xb9, 0xe1, + 0x0d, 0x06, 0x63, 0xe5, 0xb3, 0xa3, 0xb9, 0x9b, 0x55, 0x2b, 0x78, 0xdc, 0xd8, 0x29, 0x96, 0xdd, + 0xda, 0x7c, 0xc8, 0x80, 0xca, 0x4e, 0xeb, 0xf7, 0x7c, 0x7d, 0xaf, 0x3a, 0x2f, 0x16, 0x9a, 0x22, + 0xaf, 0x26, 0x7b, 0x41, 0xde, 0x87, 0x11, 0x21, 0x5c, 0x08, 0x26, 0xbb, 0x30, 0xab, 0x30, 0x91, + 0x29, 0xb7, 0xe2, 0x76, 0xa8, 0xf8, 0x16, 0x2b, 0x15, 0x4f, 0x70, 0x4d, 0x56, 0x22, 0xef, 0x01, + 0xe0, 0x7a, 0xca, 0xfb, 0x93, 0x40, 0x12, 0xd3, 0x0a, 0x09, 0xcc, 0x2c, 0xb2, 0xae, 0x89, 0x9a, + 0x19, 0x4c, 0x41, 0x66, 0xac, 0x47, 0xb9, 0x95, 0xc4, 0xca, 0xaf, 0xc4, 0x0c, 0x62, 0x8b, 0xd3, + 0x82, 0x98, 0xca, 0xda, 0x4d, 0xc8, 0xfa, 0xfb, 0x76, 0x49, 0xf6, 0x26, 0xd5, 0x57, 0x6f, 0x08, + 0x23, 0x73, 0x7c, 0x34, 0x07, 0x9b, 0x8f, 0xd6, 0x17, 0x79, 0x4d, 0x03, 0xfc, 0x7d, 0x5b, 0xfc, + 0xd6, 0xc7, 0x61, 0x94, 0x31, 0x4c, 0x4a, 0x83, 0xfe, 0xb7, 0x12, 0x30, 0x26, 0x12, 0xc4, 0xe0, + 0xdc, 0x81, 0x14, 0x63, 0xa5, 0x94, 0xc1, 0xeb, 0x5d, 0xe0, 0xf3, 0xa5, 0x4b, 0xae, 0xc7, 0x38, + 0x02, 0x9b, 0xf8, 0x21, 0x7a, 0xc1, 0x09, 0x90, 0x7f, 0xa6, 0xc1, 0xa4, 0x5c, 0x94, 0x4a, 0x3b, + 0xcd, 0x92, 0x1c, 0xf3, 0x21, 0x24, 0xfc, 0x7e, 0x0c, 0x5f, 0x22, 0x88, 0x8a, 0xeb, 0x82, 0xc6, + 0x52, 0x13, 0xc7, 0xba, 0xb2, 0xea, 0x04, 0x5e, 0x73, 0xe9, 0x81, 0xe8, 0x69, 0xae, 0x2d, 0x7b, + 0xe5, 0x5b, 0x7f, 0xf8, 0x74, 0x12, 0x94, 0xb3, 0xdb, 0xda, 0x29, 0x7c, 0x4b, 0x83, 0xe9, 0xae, + 0x8d, 0x93, 0x1c, 0x24, 0x98, 0xf6, 0x41, 0xe9, 0x35, 0xd8, 0x4f, 0xb2, 0x09, 0xa9, 0x03, 0xd3, + 0x6e, 0x70, 0x3d, 0x1f, 0x5d, 0x43, 0xf6, 0x0e, 0x8a, 0x72, 0xe1, 0x2e, 0x86, 0x8b, 0x74, 0x6b, + 0xe1, 0xc6, 0xf6, 0x65, 0x33, 0x9c, 0x8f, 0x06, 0xa7, 0xf5, 0xde, 0xd0, 0x3b, 0x9a, 0xfe, 0x3b, + 0x09, 0x98, 0x8e, 0xf0, 0x63, 0xf5, 0x30, 0xa0, 0x9e, 0x63, 0xda, 0x64, 0x39, 0x3a, 0x52, 0xaf, + 0xf6, 0x60, 0xa8, 0xac, 0x1f, 0x1d, 0xa4, 0xdf, 0x8f, 0x1d, 0xa4, 0x0f, 0xfa, 0x1d, 0x24, 0x09, + 0xea, 0x4f, 0xf8, 0x60, 0xdd, 0x86, 0xf4, 0xba, 0x5b, 0x36, 0x6d, 0x2b, 0x68, 0x92, 0x2f, 0x40, + 0x2a, 0xb0, 0xa8, 0x27, 0x87, 0x67, 0x2e, 0x86, 0x95, 0x5b, 0x16, 0x95, 0x8a, 0x88, 0xd7, 0xd1, + 0x8b, 0x90, 0x64, 0x89, 0x2a, 0xf6, 0x0c, 0xc7, 0x3e, 0xa5, 0x62, 0xcf, 0x88, 0xc6, 0xf5, 0x5f, + 0xd1, 0x60, 0xe4, 0x2b, 0xd4, 0xf3, 0x2d, 0xd7, 0x21, 0x97, 0x21, 0x53, 0x33, 0x9f, 0xb8, 0x5e, + 0xe9, 0xc0, 0xb4, 0x85, 0x82, 0xcd, 0x1c, 0x1f, 0xcd, 0xa5, 0xee, 0xb3, 0x44, 0x23, 0x8d, 0x79, + 0x5f, 0x31, 0x6d, 0x2c, 0x67, 0x39, 0xa2, 0xdc, 0x90, 0x52, 0x8e, 0x25, 0x1a, 0x69, 0xcc, 0x63, + 0xe5, 0xa6, 0x20, 0x55, 0x37, 0x83, 0xf2, 0x63, 0xd4, 0x86, 0x29, 0x83, 0x7f, 0x90, 0x02, 0xa4, + 0x2d, 0x87, 0x0f, 0x3a, 0x6a, 0xba, 0x94, 0x11, 0x7e, 0xeb, 0xbf, 0x94, 0x82, 0x71, 0xc6, 0xa8, + 0x15, 0xea, 0x97, 0x3d, 0xab, 0x1e, 0xb8, 0xde, 0x1f, 0x6b, 0x9d, 0xff, 0x2e, 0xa4, 0xcc, 0x20, + 0xf0, 0x7c, 0xa1, 0xee, 0x5f, 0x52, 0x6a, 0xcb, 0x16, 0x17, 0x83, 0xc0, 0xb3, 0x76, 0x1a, 0x01, + 0x0d, 0x75, 0x1c, 0xd6, 0x20, 0xab, 0x90, 0xb6, 0xc5, 0x80, 0x0b, 0x7d, 0x7f, 0x29, 0x66, 0x9c, + 0xa5, 0x6c, 0x08, 0x1a, 0x61, 0x55, 0xb2, 0x01, 0x63, 0x9b, 0x58, 0x48, 0x8c, 0xa1, 0xd0, 0xf6, + 0x7a, 0x0c, 0x2d, 0x51, 0x52, 0x90, 0x8a, 0x56, 0x67, 0x56, 0x10, 0x5f, 0xc5, 0x02, 0xb3, 0x2a, + 0x6c, 0xa9, 0x34, 0x26, 0x6c, 0x99, 0x55, 0x66, 0x41, 0xf9, 0x81, 0xe9, 0x05, 0xcc, 0xf2, 0x0f, + 0xd0, 0x82, 0x4a, 0x18, 0x19, 0x91, 0xb2, 0x18, 0x90, 0x4d, 0xc8, 0x49, 0x5c, 0xe1, 0xe2, 0x93, + 0x46, 0x11, 0xd6, 0xbb, 0x30, 0x46, 0x76, 0x49, 0x2c, 0x30, 0x02, 0xce, 0x84, 0x1d, 0x4d, 0x26, + 0x17, 0x61, 0xb4, 0x6c, 0x37, 0xfc, 0x80, 0x7a, 0x25, 0xc7, 0xac, 0x31, 0x9b, 0x8b, 0x61, 0xca, + 0x8a, 0xb4, 0x0d, 0xb3, 0x46, 0xdb, 0xd7, 0x3b, 0x78, 0x2e, 0xeb, 0xdd, 0x27, 0x90, 0x7d, 0x48, + 0xbd, 0x32, 0xdb, 0x0f, 0xda, 0xd4, 0x67, 0xd3, 0xa9, 0xfe, 0xc6, 0x0d, 0x94, 0x40, 0xcd, 0x60, + 0x3f, 0x31, 0x65, 0xe1, 0x16, 0xca, 0x0d, 0x4b, 0x59, 0xb8, 0x85, 0x29, 0xb7, 0x6e, 0xa0, 0x2c, + 0xb0, 0x94, 0x5b, 0xbc, 0xcc, 0xdb, 0xb7, 0x70, 0x7c, 0x59, 0xca, 0xdb, 0xbc, 0xcc, 0xbb, 0x37, + 0x70, 0x94, 0x58, 0xca, 0xbb, 0x37, 0x98, 0x29, 0x5f, 0xbf, 0x6f, 0x1e, 0x22, 0xb3, 0x35, 0x03, + 0x7f, 0xeb, 0xff, 0x60, 0x08, 0x26, 0x36, 0x03, 0xd7, 0x53, 0xe7, 0xc1, 0x2f, 0x40, 0x9a, 0xed, + 0x1b, 0x95, 0x89, 0xb0, 0x7c, 0x7c, 0x34, 0x37, 0x82, 0xc5, 0x70, 0x26, 0xbc, 0x39, 0xd0, 0x4c, + 0x10, 0xf5, 0x8c, 0x11, 0x24, 0xba, 0x56, 0x69, 0xc9, 0xf2, 0xd0, 0xc0, 0xb2, 0xbc, 0x0c, 0x49, + 0x36, 0xa1, 0xc4, 0x2c, 0xb8, 0xda, 0x43, 0xf5, 0xb7, 0xfa, 0x24, 0xa8, 0x60, 0x65, 0xb2, 0x04, + 0xe9, 0xb2, 0x59, 0x37, 0xcb, 0xad, 0x09, 0x71, 0xa1, 0x0b, 0x04, 0x84, 0xbd, 0x2c, 0xca, 0xc9, + 0xd9, 0x20, 0xeb, 0xe9, 0x47, 0x1a, 0x64, 0xb1, 0x04, 0x57, 0xb0, 0x64, 0x05, 0x92, 0x15, 0xea, + 0x97, 0x91, 0x5f, 0xd9, 0x85, 0x6b, 0x71, 0x06, 0x55, 0x94, 0xdb, 0x12, 0x19, 0xab, 0x4d, 0xee, + 0xc3, 0x48, 0x8d, 0x06, 0x9e, 0x55, 0xf6, 0xc5, 0xe2, 0x76, 0xb3, 0x17, 0x21, 0xde, 0x7c, 0xf1, + 0x3e, 0xaf, 0x85, 0x8b, 0x89, 0x21, 0x69, 0x14, 0xde, 0x83, 0x51, 0x35, 0xa3, 0x97, 0xa6, 0xd6, + 0xd4, 0x65, 0xe2, 0x3f, 0xa5, 0xb9, 0x19, 0x16, 0x1a, 0x5d, 0xcb, 0x91, 0x1e, 0x0e, 0xce, 0x7a, + 0xec, 0x60, 0xd4, 0x74, 0x1d, 0x1a, 0xc8, 0x74, 0x8d, 0xea, 0x84, 0x44, 0xbb, 0x4e, 0x78, 0x09, + 0xa0, 0x51, 0xaf, 0x98, 0x22, 0x9b, 0xef, 0xf1, 0x32, 0x22, 0x65, 0x31, 0x20, 0x1b, 0x2d, 0xd6, + 0xa6, 0x7a, 0x6e, 0x5a, 0xd5, 0x8e, 0x77, 0xe7, 0x2d, 0xd9, 0x84, 0x71, 0x3e, 0x49, 0xb8, 0x8d, + 0x49, 0xfd, 0xfc, 0x30, 0x92, 0xbd, 0xdc, 0xdf, 0x88, 0x49, 0x9d, 0xe8, 0xb7, 0x92, 0xa8, 0xcf, + 0x66, 0xa8, 0xe9, 0x55, 0xfd, 0xfc, 0x08, 0x6e, 0xf7, 0xf0, 0x37, 0x1b, 0x34, 0xea, 0x1c, 0x88, + 0x7d, 0x22, 0xfb, 0x49, 0xbe, 0xab, 0x41, 0xc6, 0x36, 0x03, 0xea, 0x94, 0x2d, 0xea, 0xe3, 0xd6, + 0x30, 0xbb, 0xf0, 0x56, 0xbf, 0xbd, 0x59, 0x97, 0x15, 0xb9, 0xd5, 0xf3, 0x05, 0x06, 0xe3, 0x69, + 0x2d, 0x9c, 0x16, 0x0a, 0xf2, 0x1d, 0x0d, 0xd2, 0x66, 0x39, 0xb0, 0x0e, 0xd8, 0xa4, 0x02, 0x84, + 0x74, 0xab, 0x5f, 0x48, 0x8b, 0xa2, 0xde, 0x73, 0x40, 0x14, 0x62, 0x20, 0x45, 0x98, 0x0c, 0xdc, + 0xc0, 0xb4, 0x4b, 0x62, 0xbb, 0x53, 0xa3, 0x35, 0xd7, 0x6b, 0xe6, 0xb3, 0x28, 0x17, 0xa7, 0x31, + 0x8b, 0x6f, 0x6d, 0xee, 0x63, 0x06, 0x39, 0x0b, 0x69, 0xa7, 0x51, 0x2b, 0x95, 0xeb, 0x0d, 0x3f, + 0x3f, 0x8a, 0xb6, 0xc2, 0x88, 0xd3, 0xa8, 0x2d, 0xd7, 0x1b, 0xcf, 0x34, 0x8d, 0x0a, 0x5f, 0x84, + 0xf1, 0x28, 0xc7, 0xbb, 0x98, 0x7a, 0x91, 0xda, 0x09, 0xb5, 0x76, 0x19, 0x26, 0x36, 0x68, 0xf0, + 0xb1, 0xeb, 0xed, 0x49, 0x1e, 0x71, 0x9b, 0xa6, 0xec, 0xd6, 0x2c, 0xa7, 0x8a, 0x34, 0x12, 0x46, + 0xf8, 0xcd, 0xf2, 0xdc, 0x46, 0x50, 0x75, 0x59, 0x1e, 0xa7, 0x15, 0x7e, 0x93, 0x3c, 0x8c, 0xf0, + 0xd1, 0x6a, 0x8a, 0xa9, 0x23, 0x3f, 0x0b, 0x01, 0x8c, 0x45, 0x46, 0xa0, 0x0b, 0xc2, 0xfb, 0x2a, + 0xc2, 0xec, 0xc2, 0xdb, 0xfd, 0x8e, 0x6c, 0x1b, 0x78, 0x55, 0xbf, 0xe4, 0x60, 0xdc, 0xa0, 0x55, + 0xcb, 0x75, 0xc2, 0x7d, 0xde, 0xff, 0xd1, 0x60, 0x22, 0x4c, 0x12, 0x4a, 0xe7, 0x11, 0x8c, 0x78, + 0x3c, 0x49, 0x98, 0xa8, 0x71, 0x4d, 0xb7, 0x55, 0x96, 0xdf, 0x62, 0xe2, 0x0a, 0x3a, 0x85, 0x59, + 0x18, 0xe6, 0x19, 0x8c, 0xef, 0x5f, 0x77, 0x1d, 0xb1, 0x39, 0xc9, 0x18, 0xfc, 0xa3, 0x50, 0x83, + 0x51, 0xb5, 0x62, 0x97, 0xd1, 0xbe, 0x1d, 0xe5, 0xc6, 0x1b, 0x03, 0x43, 0x52, 0xf9, 0x70, 0x19, + 0xb2, 0x9c, 0x65, 0x3d, 0x8e, 0x05, 0x7f, 0x2f, 0x09, 0x19, 0xc3, 0xdc, 0x0d, 0x98, 0xae, 0xa0, + 0xe4, 0x3a, 0x80, 0x47, 0xeb, 0xb6, 0x55, 0x36, 0x65, 0xc9, 0xe4, 0xd2, 0xd8, 0xf1, 0xd1, 0x5c, + 0xc6, 0xe0, 0xa9, 0x6c, 0x72, 0x8a, 0x02, 0x6b, 0x15, 0xf2, 0x16, 0xc0, 0x63, 0xd3, 0xab, 0xa0, + 0xaa, 0x92, 0xa8, 0x4f, 0x17, 0xf9, 0xf9, 0x68, 0xf1, 0x8e, 0xe9, 0x55, 0x90, 0xa8, 0xd4, 0xb8, + 0x8f, 0x65, 0x02, 0x53, 0x47, 0x36, 0x35, 0x2b, 0x28, 0x30, 0x49, 0x03, 0x7f, 0x33, 0xa6, 0x71, + 0x32, 0x49, 0x6e, 0xdb, 0xe3, 0x07, 0x93, 0x2e, 0xb3, 0x5e, 0xb7, 0x2d, 0x5a, 0x41, 0x83, 0x23, + 0x69, 0xc8, 0x4f, 0xb2, 0x05, 0xe9, 0xba, 0xe7, 0x56, 0xd1, 0x5e, 0xe2, 0x1a, 0x72, 0x21, 0x8e, + 0x5f, 0xb2, 0x87, 0xc5, 0x87, 0xa2, 0x12, 0x57, 0x0a, 0x62, 0xf9, 0x95, 0x94, 0xc8, 0xab, 0x30, + 0xc1, 0xd0, 0x94, 0x02, 0xcf, 0x74, 0xfc, 0x5d, 0xea, 0x51, 0x8a, 0x46, 0x62, 0xd2, 0x18, 0x67, + 0xc9, 0x5b, 0x61, 0x6a, 0xe1, 0x2f, 0x6b, 0x90, 0x96, 0xa4, 0x18, 0xf6, 0x1a, 0xee, 0x12, 0x90, + 0x61, 0x06, 0xff, 0x60, 0xbd, 0x74, 0xe8, 0x21, 0x3f, 0x53, 0x4c, 0x1a, 0xf8, 0xbb, 0xd5, 0xcb, + 0x84, 0xda, 0xcb, 0x19, 0x18, 0xae, 0x9b, 0x0d, 0x9f, 0x56, 0xb0, 0xf3, 0x69, 0x43, 0x7c, 0x91, + 0xab, 0x90, 0xab, 0x53, 0xa7, 0x62, 0x39, 0xd5, 0x92, 0xef, 0x98, 0x75, 0xff, 0xb1, 0x1b, 0x08, + 0x36, 0x4c, 0x88, 0xf4, 0x4d, 0x91, 0x5c, 0x78, 0x02, 0x63, 0x91, 0x9e, 0xa9, 0xe2, 0x95, 0xe4, + 0xe2, 0xb5, 0x1c, 0x15, 0xaf, 0xd7, 0x07, 0x62, 0x97, 0x2a, 0x5a, 0xc7, 0x43, 0x30, 0x66, 0x98, + 0x4e, 0x95, 0x3e, 0xf4, 0xdc, 0x1d, 0x9b, 0xd6, 0x7c, 0x72, 0x01, 0xb2, 0x0d, 0xc7, 0x3c, 0x30, + 0x2d, 0xdb, 0xdc, 0xb1, 0xf9, 0x59, 0x72, 0xda, 0x50, 0x93, 0xc8, 0x2d, 0x38, 0xc3, 0x38, 0xc8, + 0x6c, 0x60, 0x37, 0x28, 0x71, 0x3f, 0xc0, 0x63, 0xd7, 0xae, 0x50, 0x0f, 0xe1, 0xa4, 0x8d, 0x29, + 0x9e, 0xbd, 0xe1, 0x06, 0xeb, 0x2c, 0xf3, 0x0e, 0xe6, 0x91, 0x97, 0x61, 0xdc, 0x71, 0x4b, 0x4c, + 0xa2, 0x4a, 0x3c, 0x1f, 0x19, 0x97, 0x36, 0x46, 0x1d, 0x97, 0x61, 0x5c, 0xc7, 0x34, 0x72, 0x05, + 0x26, 0x1a, 0x4e, 0x85, 0x7a, 0x42, 0x32, 0x83, 0x90, 0x91, 0xed, 0xc9, 0xe4, 0x32, 0x8c, 0xbb, + 0x07, 0x91, 0x82, 0x69, 0x2c, 0xd8, 0x96, 0x8a, 0x5a, 0xdb, 0xe5, 0x30, 0x91, 0xe3, 0x69, 0x63, + 0xc4, 0x71, 0x11, 0x18, 0x79, 0x07, 0xf2, 0xfb, 0x0d, 0x8b, 0xfa, 0xcc, 0xb0, 0x2e, 0xd1, 0xfd, + 0x86, 0x69, 0xfb, 0xa5, 0xc0, 0x2a, 0xef, 0x31, 0xe5, 0x38, 0x8c, 0x45, 0x67, 0xc2, 0xfc, 0x55, + 0xcc, 0xde, 0xe2, 0xb9, 0xe4, 0x35, 0x20, 0xbc, 0x27, 0x6e, 0xb5, 0x14, 0xb8, 0x6e, 0xc9, 0x36, + 0xbd, 0x2a, 0x97, 0xaf, 0xb4, 0x31, 0xc1, 0x72, 0xd6, 0xdd, 0xea, 0x96, 0xeb, 0xae, 0xb3, 0x64, + 0x7d, 0x0f, 0x26, 0x90, 0xc7, 0x6c, 0x18, 0x2c, 0x74, 0x0d, 0x91, 0xeb, 0x40, 0xf6, 0x1b, 0xd4, + 0xb3, 0xa8, 0x5f, 0xaa, 0x53, 0xaf, 0xe4, 0xd3, 0xb2, 0xeb, 0x54, 0x84, 0x41, 0x9f, 0x13, 0x39, + 0x0f, 0xa9, 0xb7, 0x89, 0xe9, 0xe4, 0x1a, 0x9c, 0xfe, 0xd8, 0xb3, 0x82, 0x68, 0x61, 0xbe, 0x8e, + 0x4c, 0xf0, 0x8c, 0xb0, 0xac, 0x7e, 0x07, 0xe0, 0xa1, 0x47, 0x83, 0xa0, 0xb9, 0x59, 0x37, 0x71, + 0x07, 0x85, 0xe6, 0x4f, 0xa9, 0xa5, 0x9f, 0xd2, 0x98, 0x70, 0x8f, 0x36, 0x99, 0x22, 0xa1, 0x0e, + 0x1e, 0x21, 0x8b, 0x5d, 0xf8, 0x30, 0x75, 0x2a, 0xf7, 0x68, 0xf3, 0xbd, 0xe4, 0xff, 0xf8, 0xcd, + 0x39, 0x4d, 0xff, 0x5e, 0x96, 0xa9, 0x13, 0xa7, 0x4a, 0xd1, 0xb4, 0xfa, 0x39, 0x48, 0xfa, 0x75, + 0xd3, 0x11, 0xb6, 0x5d, 0xdc, 0x71, 0x60, 0xab, 0x79, 0x69, 0xd7, 0xb1, 0x8a, 0x64, 0x0d, 0x00, + 0x59, 0xa6, 0x6a, 0x98, 0x97, 0xfb, 0x11, 0x5c, 0xa9, 0x74, 0xbc, 0x50, 0xb5, 0x7d, 0xa0, 0x2a, + 0x98, 0xa8, 0x29, 0xad, 0x1e, 0x7c, 0x08, 0xcf, 0x16, 0xd2, 0x12, 0xdd, 0x90, 0x5b, 0x05, 0x3e, + 0x59, 0x6b, 0x30, 0xee, 0xbb, 0x0d, 0xaf, 0x4c, 0xc3, 0xf3, 0xa2, 0x14, 0xee, 0x65, 0x6e, 0x1f, + 0x1f, 0xcd, 0x8d, 0x6e, 0x62, 0xce, 0xb3, 0x6d, 0xed, 0x47, 0xfd, 0x16, 0x91, 0x0a, 0xd9, 0x87, + 0x09, 0xd1, 0x5c, 0xb8, 0x77, 0x1a, 0xc6, 0xf6, 0xd6, 0x8e, 0x8f, 0xe6, 0xc6, 0x78, 0x7b, 0xcf, + 0xba, 0x83, 0x1a, 0xf3, 0x15, 0x32, 0x95, 0x4e, 0xff, 0xcd, 0x48, 0x17, 0xff, 0xcd, 0x32, 0x8c, + 0x89, 0x59, 0x6c, 0x31, 0x60, 0x4d, 0xb1, 0x4f, 0xce, 0x77, 0xdb, 0x27, 0xb3, 0x72, 0xf2, 0x88, + 0x1e, 0x2b, 0xdd, 0xe1, 0x75, 0xc8, 0x5d, 0x54, 0xe2, 0xa8, 0x43, 0x70, 0x5b, 0x9c, 0x5d, 0xb8, + 0x12, 0x3b, 0xb8, 0x8a, 0xce, 0x51, 0x54, 0x37, 0xd7, 0x41, 0x62, 0x7c, 0xe5, 0xee, 0xf9, 0x5a, + 0x2f, 0x42, 0xad, 0x89, 0xa5, 0x8e, 0xaf, 0x4f, 0xb6, 0x61, 0x54, 0x75, 0x53, 0xe6, 0xc7, 0x90, + 0xdc, 0xf5, 0x9e, 0xe2, 0x82, 0xbd, 0x8c, 0x18, 0xe1, 0x59, 0xbb, 0x95, 0x44, 0xce, 0x43, 0x26, + 0x54, 0x0b, 0xf9, 0x71, 0x9c, 0xf3, 0xad, 0x04, 0xb6, 0xce, 0x49, 0x1d, 0x32, 0xc1, 0xd5, 0x8d, + 0xf8, 0x24, 0x17, 0x61, 0xd4, 0x63, 0x2b, 0x92, 0xcd, 0xd6, 0x14, 0xea, 0xe7, 0x73, 0x68, 0x64, + 0x65, 0x59, 0xda, 0x3a, 0x4f, 0x62, 0xe3, 0x85, 0x13, 0x3a, 0x2c, 0x73, 0x1a, 0xcb, 0x8c, 0x62, + 0xa2, 0x2c, 0x34, 0x05, 0x29, 0xdb, 0x2d, 0xef, 0xf9, 0x79, 0xc2, 0x8d, 0x41, 0xfc, 0x20, 0x37, + 0x61, 0x06, 0x7f, 0x94, 0x3e, 0xb6, 0x82, 0xc7, 0xa5, 0x8f, 0x4d, 0x2b, 0x28, 0xed, 0x37, 0x68, + 0x83, 0xfa, 0xf9, 0x49, 0x2c, 0x36, 0x89, 0xb9, 0x1f, 0x5a, 0xc1, 0xe3, 0x0f, 0x4d, 0x2b, 0x78, + 0x84, 0x59, 0xa8, 0xcb, 0xdd, 0xf2, 0x9e, 0x52, 0x1c, 0x7f, 0x52, 0xcf, 0xcf, 0x4f, 0x61, 0xad, + 0x29, 0x96, 0x1d, 0x56, 0xf8, 0x90, 0xe7, 0x91, 0x4f, 0xe0, 0x62, 0xe0, 0xd6, 0x4b, 0x7b, 0x25, + 0xde, 0xe2, 0x4e, 0xb3, 0x1b, 0x81, 0xe9, 0x9e, 0xde, 0x9f, 0x70, 0x5a, 0x16, 0xd7, 0xdd, 0xf2, + 0x9e, 0x32, 0x3f, 0xcf, 0x05, 0x6e, 0xfd, 0x1e, 0x4b, 0xf3, 0x97, 0x9a, 0xed, 0x8d, 0x17, 0xfe, + 0xb7, 0x86, 0xc7, 0x93, 0x7b, 0x72, 0xc7, 0x57, 0x47, 0x7d, 0xa3, 0x68, 0xb8, 0x0c, 0x4f, 0x61, + 0x2a, 0xee, 0x03, 0xbe, 0x74, 0x32, 0x6d, 0x33, 0xba, 0xf4, 0xe6, 0x67, 0x47, 0x73, 0x37, 0x06, + 0x9a, 0x55, 0xf7, 0x68, 0x93, 0x2f, 0xb8, 0x04, 0x92, 0x8f, 0xa9, 0x5d, 0x11, 0x4b, 0x16, 0xfe, + 0x66, 0x03, 0x2d, 0xbb, 0xca, 0xb7, 0x92, 0xf2, 0x93, 0x99, 0x1e, 0xec, 0x27, 0x5b, 0xec, 0x3d, + 0x5c, 0xd6, 0xb8, 0xdf, 0x23, 0x61, 0x8c, 0x8b, 0x64, 0x83, 0xa7, 0xaa, 0x05, 0x71, 0x84, 0x3d, + 0x1f, 0x35, 0x42, 0xab, 0xe0, 0x87, 0x3c, 0xf5, 0x6e, 0x32, 0x9d, 0xcd, 0x8d, 0xde, 0x4d, 0xa6, + 0x47, 0x73, 0x63, 0xfa, 0xaf, 0x68, 0x62, 0xcd, 0xee, 0xe9, 0x0c, 0x23, 0x26, 0x64, 0x3c, 0x56, + 0xb2, 0x64, 0x55, 0xf8, 0x71, 0x41, 0x62, 0x69, 0xe5, 0xf8, 0x68, 0x2e, 0xcd, 0x19, 0xbf, 0xe2, + 0x0f, 0xac, 0x66, 0x44, 0x45, 0x23, 0x8d, 0x64, 0xd7, 0x2a, 0xbe, 0xbe, 0x05, 0xe3, 0x12, 0x8c, + 0x30, 0xc8, 0x97, 0x60, 0x18, 0x73, 0xa5, 0x3d, 0xfe, 0x72, 0x3f, 0x12, 0x20, 0x5d, 0xbd, 0xbc, + 0xa6, 0x7e, 0x05, 0xc6, 0x6e, 0x63, 0xd8, 0x47, 0x4f, 0xa3, 0xf7, 0x07, 0x43, 0x30, 0xb1, 0x8a, + 0xa1, 0x0b, 0x6c, 0x76, 0xfa, 0x28, 0x14, 0x2f, 0xfa, 0x74, 0x6a, 0x17, 0x66, 0xd8, 0x9c, 0xa6, + 0x9e, 0x5f, 0x32, 0x9d, 0x0a, 0x57, 0x9b, 0x55, 0xcf, 0xac, 0xc9, 0xe3, 0xaa, 0x1b, 0x6a, 0x8f, + 0xb9, 0x56, 0x29, 0xca, 0xf0, 0x8a, 0xe2, 0x16, 0xaf, 0xb9, 0xe8, 0x54, 0xee, 0x84, 0xf5, 0x8c, + 0xa9, 0xa0, 0x4b, 0x2a, 0xb9, 0x0d, 0x59, 0x5e, 0xad, 0x84, 0x7e, 0xfd, 0x04, 0x1e, 0xf3, 0x5f, + 0x8e, 0x23, 0xce, 0x39, 0x81, 0x0e, 0x7c, 0xa0, 0xe1, 0x6f, 0xfd, 0x75, 0x20, 0x0a, 0x8f, 0x7a, + 0xf2, 0xf4, 0x4f, 0xc1, 0x64, 0xa4, 0xb8, 0x18, 0xd8, 0x50, 0x2d, 0xf3, 0x71, 0x8d, 0x53, 0xcb, + 0x6d, 0x23, 0x12, 0x51, 0xcb, 0xfa, 0x9f, 0x06, 0xd8, 0xf2, 0xcc, 0x32, 0x5d, 0x3d, 0x60, 0xfa, + 0xf2, 0x1d, 0x48, 0x06, 0x56, 0x8d, 0x0a, 0xc3, 0xa2, 0x50, 0xe4, 0xb1, 0x12, 0x45, 0x19, 0x2b, + 0x51, 0xdc, 0x92, 0xc1, 0x14, 0x4b, 0x69, 0x46, 0xe4, 0xd7, 0xff, 0x70, 0x4e, 0x33, 0xb0, 0x06, + 0x9b, 0x80, 0xd1, 0xb0, 0x04, 0xf9, 0xa9, 0xff, 0x9e, 0x06, 0x13, 0x8b, 0xb6, 0xed, 0x96, 0xcd, + 0xc0, 0xf5, 0x56, 0xbc, 0xa6, 0xd1, 0x70, 0x98, 0x50, 0xc8, 0xb9, 0xc0, 0x77, 0xc5, 0x5c, 0x28, + 0x84, 0x44, 0x3f, 0xf5, 0x4c, 0x18, 0x11, 0x33, 0x81, 0x7c, 0x09, 0x86, 0x29, 0xeb, 0x90, 0x3c, + 0x97, 0x8b, 0x33, 0x91, 0x5a, 0xdd, 0x37, 0x44, 0x25, 0x7d, 0x01, 0xa6, 0x43, 0xc4, 0x48, 0x5b, + 0x8e, 0xd2, 0xd9, 0x76, 0xdc, 0x61, 0x93, 0xfa, 0x3f, 0xd1, 0x60, 0xa6, 0xbd, 0x52, 0x77, 0xe7, + 0x74, 0xe2, 0x79, 0x3a, 0x2a, 0x96, 0x61, 0xa4, 0xe2, 0x35, 0x4b, 0x5e, 0xc3, 0x11, 0xf2, 0x1e, + 0x27, 0x09, 0x6d, 0xc3, 0x60, 0x0c, 0x57, 0xf0, 0x5f, 0xfd, 0x3b, 0x1a, 0xe4, 0x5a, 0xd8, 0xff, + 0x18, 0x28, 0xb2, 0x8f, 0xe0, 0xb4, 0x82, 0x47, 0xb0, 0x71, 0x15, 0xd2, 0xa2, 0xab, 0xfd, 0x48, + 0x7d, 0x7b, 0x5f, 0x47, 0x78, 0x5f, 0x7d, 0x5d, 0x87, 0xd1, 0xbb, 0x9b, 0x0f, 0x36, 0x42, 0xb2, + 0x32, 0x62, 0x46, 0x6b, 0x45, 0xcc, 0xe8, 0x35, 0x18, 0x0b, 0xdd, 0x90, 0xcc, 0x3a, 0x63, 0xcb, + 0x3c, 0x9a, 0x69, 0x82, 0x15, 0xfc, 0x83, 0x55, 0x2d, 0xbb, 0x15, 0x2e, 0xf1, 0x29, 0x03, 0x7f, + 0xab, 0x13, 0x21, 0x11, 0x99, 0x08, 0x2c, 0xa7, 0xc2, 0xc3, 0x16, 0x30, 0x0e, 0x27, 0x63, 0xc8, + 0x4f, 0xfd, 0x5f, 0x68, 0x90, 0x5d, 0x77, 0xab, 0xbd, 0xd7, 0x10, 0x66, 0x6d, 0xd0, 0x03, 0x6a, + 0x4b, 0x4f, 0x1d, 0x7e, 0x84, 0x27, 0xad, 0x25, 0x9c, 0xbb, 0xbc, 0x55, 0xbe, 0xd5, 0x60, 0xf3, + 0x95, 0x09, 0x2d, 0xdb, 0x5a, 0x60, 0x26, 0x3f, 0x05, 0x60, 0x5b, 0x0d, 0xcc, 0xca, 0x41, 0xa2, + 0x66, 0x1e, 0xe2, 0x82, 0x98, 0x31, 0xd8, 0x4f, 0x06, 0xb2, 0x6e, 0x06, 0x01, 0xf5, 0x1c, 0xe1, + 0xe4, 0x91, 0x9f, 0x6c, 0x37, 0xed, 0xd1, 0x8a, 0x59, 0x0e, 0xc4, 0xd6, 0x4a, 0x7c, 0xdd, 0x4d, + 0xa6, 0xd3, 0xb9, 0x8c, 0xfe, 0x00, 0xc8, 0xba, 0x5b, 0x65, 0x7b, 0x64, 0x4b, 0x59, 0x7e, 0xde, + 0x65, 0xbb, 0x1a, 0x4c, 0x12, 0x23, 0x76, 0xb6, 0xdd, 0xf9, 0x62, 0xbb, 0xd5, 0xa2, 0x7a, 0x66, + 0x20, 0xcb, 0xeb, 0x45, 0x98, 0x5c, 0x77, 0xab, 0x1f, 0x58, 0x36, 0xf5, 0xd7, 0x2d, 0x3f, 0xe8, + 0xa9, 0x27, 0x1f, 0xc2, 0x54, 0xb4, 0xbc, 0x80, 0xf0, 0x0e, 0xa4, 0x76, 0x59, 0xa2, 0x00, 0x70, + 0xbe, 0x1b, 0x00, 0x56, 0x4b, 0x55, 0x8d, 0x58, 0x41, 0xff, 0x1a, 0x8c, 0x0b, 0x8a, 0x3d, 0xc7, + 0x85, 0x40, 0x92, 0xd5, 0x11, 0xc3, 0x82, 0xbf, 0x15, 0x7e, 0x25, 0xda, 0xf8, 0x95, 0xcc, 0xa5, + 0xf4, 0x32, 0x8c, 0x6d, 0x06, 0x66, 0x79, 0xaf, 0xf7, 0x98, 0xbf, 0x2b, 0x22, 0xc9, 0xb8, 0x63, + 0x39, 0x36, 0xf6, 0x03, 0x09, 0xb6, 0x22, 0xc6, 0xf4, 0x4d, 0x48, 0x32, 0xf8, 0x78, 0x64, 0x62, + 0x0a, 0xb5, 0x9e, 0x31, 0xf0, 0x37, 0xdb, 0x8d, 0x32, 0x98, 0x25, 0xdf, 0xfa, 0xba, 0x3c, 0xc9, + 0x4c, 0xb3, 0x84, 0x4d, 0xeb, 0xeb, 0x94, 0x14, 0x20, 0x2d, 0x62, 0x1e, 0x7d, 0x11, 0x49, 0x16, + 0x7e, 0xeb, 0x7f, 0x43, 0x83, 0x89, 0xdb, 0x34, 0x40, 0x4e, 0xf7, 0x04, 0x7f, 0x0e, 0x32, 0xb6, + 0xe5, 0x07, 0x25, 0xd7, 0xb1, 0x9b, 0xe2, 0x44, 0x22, 0xcd, 0x12, 0x1e, 0x38, 0x76, 0x93, 0xbc, + 0x2d, 0x7a, 0x96, 0xc2, 0x9e, 0xc5, 0x79, 0x39, 0x59, 0x63, 0x4a, 0x24, 0x5c, 0x01, 0xd2, 0x42, + 0x2a, 0xf9, 0x21, 0x55, 0xc6, 0x08, 0xbf, 0xf5, 0x35, 0xc8, 0xb5, 0xd0, 0x09, 0x19, 0xb8, 0x15, + 0x95, 0x81, 0xb9, 0x1e, 0x2d, 0x49, 0x01, 0xf8, 0x23, 0x0d, 0xc6, 0x1f, 0x7a, 0xee, 0x6e, 0x3f, + 0x12, 0xb0, 0x14, 0xe9, 0x4b, 0x31, 0x76, 0x4b, 0xae, 0x52, 0x2c, 0x2a, 0xdd, 0xca, 0xc3, 0x08, + 0x3f, 0x4f, 0xe0, 0x96, 0x67, 0xca, 0x90, 0x9f, 0x4c, 0x96, 0x6c, 0x73, 0x87, 0xda, 0xbe, 0x9c, + 0x7b, 0xfc, 0x4b, 0xff, 0x79, 0x48, 0x62, 0x10, 0x59, 0x1a, 0x92, 0x77, 0x56, 0x17, 0x1f, 0xe6, + 0x4e, 0x91, 0x11, 0x48, 0x2c, 0x3f, 0xdc, 0xe6, 0x51, 0x64, 0xb7, 0x1f, 0x18, 0x0f, 0xb6, 0xb7, + 0xd6, 0x36, 0x56, 0x73, 0x43, 0x24, 0x07, 0xa3, 0x5b, 0x77, 0x8c, 0xd5, 0xc5, 0x95, 0x65, 0x63, + 0x75, 0x71, 0x6b, 0x35, 0x97, 0x20, 0x00, 0xc3, 0x8b, 0xeb, 0xeb, 0x0f, 0x96, 0x37, 0x73, 0x49, + 0x92, 0x81, 0xd4, 0xd2, 0xfa, 0x83, 0xe5, 0x7b, 0xb9, 0x14, 0xfb, 0x79, 0x7f, 0x7b, 0x6b, 0xf5, + 0xab, 0xb9, 0x61, 0xfd, 0x2a, 0x8c, 0x8b, 0x83, 0xf4, 0x9e, 0xb3, 0xef, 0xef, 0xa1, 0x1d, 0xbc, + 0x1b, 0xa0, 0x2a, 0x67, 0x4b, 0xd4, 0x0b, 0xf5, 0xce, 0x7f, 0x19, 0x52, 0xb8, 0x54, 0xf4, 0x75, + 0x72, 0xd1, 0x76, 0xda, 0x80, 0x15, 0xf5, 0x6b, 0xcc, 0x52, 0x16, 0x70, 0xb9, 0x86, 0x57, 0xf4, + 0xb6, 0x16, 0x35, 0x60, 0xbe, 0x39, 0x04, 0x13, 0x61, 0x61, 0xb1, 0xed, 0x7c, 0xd1, 0x06, 0xcc, + 0x6d, 0x18, 0xc6, 0x25, 0x46, 0x1a, 0x30, 0x57, 0x7b, 0x1c, 0xce, 0xb4, 0x3a, 0x22, 0x8d, 0x77, + 0x5e, 0x9d, 0xac, 0xc8, 0x88, 0x9e, 0x04, 0xd2, 0xb9, 0xd2, 0x0f, 0x1d, 0xc6, 0xed, 0x48, 0x48, + 0x8f, 0xde, 0x80, 0x1c, 0xcb, 0x5d, 0xa1, 0x3b, 0x8d, 0xaa, 0x94, 0x85, 0x88, 0x19, 0xa0, 0xbd, + 0x10, 0x33, 0xe0, 0xdf, 0x0f, 0xc1, 0x69, 0xa5, 0x5d, 0x31, 0x9b, 0xbf, 0xa3, 0xb5, 0x6d, 0x6a, + 0xde, 0xe9, 0xd1, 0xa9, 0x48, 0x75, 0xde, 0x8c, 0x38, 0xa7, 0xfe, 0xa2, 0x70, 0x5e, 0x3d, 0x1d, + 0x50, 0x81, 0xe2, 0xb9, 0x0d, 0x56, 0x81, 0x42, 0x56, 0x41, 0xa7, 0x9e, 0x35, 0x27, 0xf8, 0xd6, + 0xf7, 0xcb, 0xd1, 0xb3, 0xe6, 0x6b, 0xfd, 0x34, 0xd4, 0x19, 0x52, 0xf4, 0x07, 0x49, 0x18, 0xd9, + 0x3a, 0x74, 0x70, 0x7b, 0xf6, 0x08, 0x86, 0x84, 0x08, 0x8f, 0x2e, 0x2d, 0x32, 0x30, 0xff, 0xa1, + 0xdf, 0xb9, 0xc9, 0x43, 0xbe, 0x1b, 0x56, 0xa5, 0xb8, 0xbd, 0xbd, 0xc6, 0x46, 0x7e, 0x68, 0x6d, + 0xc5, 0x18, 0xb2, 0x2a, 0xe4, 0x3d, 0xdc, 0x9a, 0x78, 0x81, 0x00, 0xd9, 0xdf, 0x2e, 0x82, 0x57, + 0x61, 0x9b, 0xf0, 0xe0, 0xd0, 0x29, 0x55, 0x84, 0x3b, 0xda, 0x72, 0x1d, 0x61, 0xcf, 0x8c, 0x07, + 0x87, 0xce, 0x4a, 0x2b, 0x95, 0xbc, 0x05, 0x67, 0x9c, 0x46, 0x8d, 0x1f, 0x60, 0xd6, 0xd8, 0xba, + 0x54, 0xa2, 0x87, 0xb4, 0xdc, 0x90, 0x67, 0xd4, 0x29, 0x63, 0xda, 0x69, 0xd4, 0x36, 0xc3, 0xdc, + 0x55, 0x91, 0x49, 0xe6, 0x20, 0xcb, 0xea, 0x79, 0x94, 0x5b, 0x25, 0x78, 0xc6, 0x68, 0x80, 0xd3, + 0xa8, 0x19, 0x3c, 0x85, 0x5c, 0x81, 0x1c, 0x2b, 0x60, 0x36, 0x02, 0x37, 0x2c, 0xc5, 0xb5, 0xf1, + 0xb8, 0xd3, 0xa8, 0x2d, 0x36, 0x02, 0x57, 0x96, 0xfc, 0x32, 0xa4, 0x2b, 0xd4, 0xac, 0xd8, 0x96, + 0xc3, 0x8f, 0xf2, 0xfa, 0xed, 0x6a, 0x58, 0x0b, 0x9d, 0x83, 0xb5, 0xba, 0x6d, 0x95, 0xad, 0x40, + 0x1c, 0x98, 0x87, 0xdf, 0x0c, 0xa8, 0xc9, 0x4c, 0xd8, 0xd2, 0x4e, 0x33, 0xa0, 0xfc, 0x18, 0x2f, + 0x61, 0x00, 0x26, 0x2d, 0xb1, 0x14, 0x72, 0x19, 0x26, 0x6a, 0xe6, 0x61, 0x49, 0x2d, 0x04, 0x58, + 0x68, 0xac, 0x66, 0x1e, 0x2e, 0xb6, 0xca, 0x9d, 0x83, 0x0c, 0x9e, 0x74, 0xe1, 0x12, 0x9c, 0xe5, + 0xad, 0xb0, 0x04, 0x5c, 0x82, 0x2f, 0xc1, 0x98, 0xe5, 0x8b, 0xb3, 0x46, 0xab, 0x6c, 0xda, 0xe8, + 0x4b, 0x4d, 0x1b, 0xa3, 0x96, 0x7f, 0x27, 0x4c, 0xc3, 0xe5, 0xd6, 0xb3, 0x5c, 0xcf, 0x0a, 0x9a, + 0x78, 0x6c, 0xc7, 0x96, 0x5b, 0xf1, 0xad, 0xff, 0x41, 0x02, 0xb2, 0xe8, 0x2f, 0xa4, 0x8f, 0x1a, + 0xd4, 0x6b, 0x92, 0x99, 0x50, 0x9e, 0x32, 0x4b, 0xc3, 0x8a, 0x50, 0x7c, 0x0d, 0x86, 0xd9, 0xc0, + 0x5a, 0x15, 0x64, 0xd5, 0xe8, 0xd2, 0xca, 0xb3, 0xc9, 0x5a, 0x8a, 0xc9, 0xef, 0x8a, 0x91, 0x0a, + 0x0e, 0x9d, 0xb5, 0x0a, 0x9b, 0x28, 0xfe, 0xbe, 0x34, 0x8a, 0xd9, 0xcf, 0x96, 0x0c, 0x26, 0x06, + 0x97, 0xc1, 0x57, 0x60, 0xdc, 0xf2, 0x4b, 0x15, 0xcb, 0x17, 0x31, 0x2d, 0xd2, 0xeb, 0x31, 0x66, + 0xf9, 0x2b, 0xad, 0x44, 0xb2, 0x04, 0xa9, 0xfa, 0x63, 0xe9, 0xc8, 0x18, 0xef, 0x1a, 0xd5, 0x1a, + 0xee, 0x45, 0x5a, 0x0c, 0x2a, 0x3e, 0x64, 0x75, 0x0c, 0x5e, 0x95, 0x73, 0x36, 0xf4, 0xb6, 0x69, + 0x57, 0x86, 0x14, 0x9f, 0xd9, 0x15, 0xc8, 0xf9, 0xfb, 0x76, 0xc9, 0x71, 0x4b, 0x65, 0xd7, 0xf1, + 0x03, 0x93, 0xd9, 0x62, 0x69, 0x3e, 0x17, 0xfc, 0x7d, 0x7b, 0xc3, 0x5d, 0x96, 0xa9, 0x18, 0x52, + 0xbd, 0x6f, 0x97, 0xfc, 0x46, 0xad, 0x66, 0x7a, 0x4d, 0x11, 0x08, 0x05, 0xfe, 0xbe, 0xbd, 0xc9, + 0x53, 0xf4, 0x57, 0x20, 0x85, 0xcd, 0x32, 0xa3, 0xe0, 0xa1, 0xb1, 0xfa, 0x70, 0xd1, 0x58, 0xdb, + 0xb8, 0x9d, 0x3b, 0xc5, 0x3e, 0x57, 0xbf, 0xba, 0xba, 0xcc, 0x6c, 0x84, 0xdb, 0x39, 0x4d, 0x7f, + 0x03, 0x26, 0x99, 0xe9, 0xbc, 0x49, 0x7d, 0x5f, 0x71, 0xf4, 0x32, 0x90, 0x0d, 0x9f, 0x7a, 0x8a, + 0x05, 0x19, 0x7e, 0xeb, 0xff, 0x2f, 0x09, 0x23, 0xa2, 0xfc, 0x0b, 0x5d, 0xf1, 0x55, 0x0c, 0x43, + 0x51, 0x0c, 0x6c, 0xbc, 0xca, 0xb6, 0x45, 0x9d, 0x20, 0x0c, 0xf4, 0xe2, 0x2a, 0x63, 0x8c, 0xa7, + 0xca, 0x78, 0xb1, 0xab, 0x90, 0x43, 0x27, 0x67, 0x19, 0x6f, 0x7b, 0xf0, 0x98, 0x31, 0xbe, 0x1d, + 0x9a, 0x50, 0xd2, 0x45, 0xdc, 0xd8, 0x38, 0x06, 0x26, 0xd0, 0x92, 0x70, 0xff, 0x88, 0x18, 0x94, + 0xcb, 0xfd, 0x8d, 0xb1, 0x0c, 0x16, 0x31, 0xc3, 0x24, 0xa6, 0x2e, 0x42, 0x91, 0x1c, 0x1e, 0x5c, + 0x24, 0xaf, 0xc1, 0x69, 0xdb, 0xf4, 0x83, 0x92, 0x82, 0xaa, 0x29, 0x84, 0x61, 0x82, 0x65, 0x74, + 0xce, 0xc0, 0x0c, 0xce, 0x32, 0x75, 0x06, 0xb6, 0x29, 0x14, 0xe8, 0x47, 0xa1, 0x64, 0xbb, 0x29, + 0x94, 0x45, 0x00, 0x81, 0x23, 0x38, 0x74, 0x50, 0x61, 0xc4, 0x87, 0x15, 0x8a, 0xa5, 0xc6, 0xc8, + 0xf0, 0x5a, 0x5b, 0x87, 0x0e, 0x59, 0x82, 0xd9, 0x8e, 0xfe, 0x44, 0x25, 0x9d, 0xeb, 0x99, 0x42, + 0x5b, 0xe7, 0x14, 0xa9, 0xbf, 0x9b, 0x4c, 0x8f, 0xe4, 0xd2, 0xfa, 0x5f, 0xd2, 0xe0, 0xb4, 0x2a, + 0xb4, 0xdc, 0x98, 0x7b, 0x91, 0xa2, 0x78, 0xf2, 0x49, 0xd7, 0xdf, 0xd1, 0x60, 0x2a, 0x3a, 0x81, + 0x84, 0xc5, 0xb2, 0x02, 0x69, 0x5f, 0xa4, 0x09, 0x93, 0x25, 0x8e, 0x5f, 0xa2, 0xba, 0xf4, 0xc4, + 0xc8, 0x9a, 0xe4, 0x6e, 0x9b, 0x99, 0x11, 0xa7, 0x71, 0x3a, 0x58, 0x12, 0xb5, 0x34, 0xf4, 0x7d, + 0x20, 0xcb, 0xa6, 0x53, 0xa6, 0x36, 0xb2, 0xb5, 0xe7, 0xee, 0xe6, 0x32, 0xa4, 0xf9, 0x18, 0x59, + 0xdc, 0xd7, 0x99, 0x59, 0xca, 0x32, 0x73, 0x17, 0x2b, 0x33, 0xb3, 0x15, 0x33, 0xdb, 0xa6, 0x69, + 0xa2, 0x4d, 0x55, 0xdc, 0x86, 0xc9, 0x48, 0x93, 0x82, 0x37, 0x6c, 0xab, 0x89, 0xc9, 0xb4, 0x22, + 0x1c, 0xdc, 0xe1, 0x77, 0xeb, 0xd4, 0x65, 0x48, 0x39, 0x75, 0xd1, 0x9b, 0x30, 0xc5, 0x09, 0x89, + 0x0e, 0xf6, 0x44, 0x7f, 0x1d, 0x40, 0x30, 0x51, 0xe2, 0x1f, 0xe5, 0xd1, 0x17, 0x82, 0xc0, 0xda, + 0x8a, 0x91, 0x11, 0x05, 0x7a, 0xf4, 0x61, 0x0d, 0xa6, 0xdb, 0x9a, 0x7e, 0xea, 0x5e, 0xbc, 0x04, + 0xe7, 0xd8, 0x20, 0x2d, 0x87, 0x57, 0x0b, 0xf1, 0x08, 0x32, 0x8c, 0xae, 0x91, 0x72, 0x1d, 0x86, + 0xfa, 0xfc, 0x0c, 0xe5, 0xfa, 0x1f, 0x6b, 0x70, 0xbe, 0x3b, 0x56, 0xd1, 0xfb, 0xf5, 0xf0, 0xb8, + 0x95, 0x1f, 0x1c, 0x47, 0xb6, 0xbf, 0xfb, 0x76, 0x51, 0xbd, 0x4c, 0x59, 0xdc, 0xa4, 0x9e, 0x65, + 0xda, 0xd6, 0xd7, 0x69, 0xc5, 0xa0, 0x55, 0xb6, 0x9e, 0x36, 0x43, 0xd9, 0x44, 0x1a, 0x03, 0xcb, + 0x79, 0x84, 0x45, 0x6d, 0x72, 0x7e, 0x16, 0xce, 0xb0, 0x22, 0x6c, 0xdd, 0xde, 0x7c, 0xb4, 0xfe, + 0x81, 0xed, 0x7e, 0x1c, 0x72, 0xf8, 0xbf, 0x25, 0x80, 0x88, 0x74, 0x83, 0xd6, 0xdc, 0x80, 0x62, + 0x2e, 0xd9, 0x85, 0x91, 0x5d, 0xdb, 0xfd, 0xb8, 0x14, 0x5a, 0xc5, 0xf7, 0x85, 0xa5, 0xf2, 0x85, + 0xbe, 0x98, 0xdb, 0x76, 0x31, 0xb4, 0xc8, 0x88, 0xa2, 0xc5, 0x32, 0xcc, 0x7f, 0x19, 0xc3, 0x8c, + 0xfa, 0x5a, 0x85, 0x6c, 0x40, 0xca, 0x72, 0x76, 0x5d, 0xd9, 0xc9, 0xb8, 0x28, 0x9b, 0x4e, 0x94, + 0x6a, 0xc8, 0x25, 0x27, 0x53, 0xf8, 0xbf, 0x1a, 0x24, 0xd1, 0xa2, 0x7f, 0x91, 0x32, 0xb2, 0x04, + 0x99, 0xf0, 0x3e, 0xe5, 0x40, 0xe6, 0x7d, 0xab, 0x1a, 0x13, 0x16, 0xe1, 0x02, 0xe6, 0x3e, 0x94, + 0x37, 0x07, 0xeb, 0xb9, 0xd8, 0xce, 0x08, 0x1a, 0xfa, 0x45, 0x18, 0x16, 0x5b, 0xf2, 0x2c, 0x8c, + 0x18, 0xdb, 0x1b, 0x1b, 0xdc, 0xb2, 0x01, 0x18, 0x7e, 0xb4, 0xbd, 0xba, 0xbd, 0xba, 0x92, 0xd3, + 0xf4, 0x1f, 0x6a, 0x90, 0xef, 0x14, 0x02, 0x21, 0xba, 0x6b, 0x90, 0x62, 0x03, 0xd2, 0xcf, 0xfd, + 0xb8, 0x4e, 0x30, 0xe1, 0x79, 0x21, 0x4a, 0xce, 0xf3, 0x94, 0xdb, 0xff, 0xa8, 0x41, 0x6e, 0xb3, + 0x6e, 0x3a, 0x11, 0x1f, 0xd1, 0xa5, 0x36, 0x05, 0xb7, 0x04, 0xad, 0x91, 0x0d, 0x87, 0xc8, 0x50, + 0xa3, 0x4c, 0xb8, 0xae, 0xbb, 0xf5, 0xd9, 0xd1, 0xdc, 0x1b, 0x83, 0xed, 0x70, 0xef, 0xd1, 0xa6, + 0x12, 0x9c, 0xb2, 0xd1, 0x0a, 0x4e, 0x49, 0x3c, 0x0b, 0x45, 0x11, 0xd3, 0xc2, 0x14, 0xca, 0x69, + 0xa5, 0x77, 0x62, 0x28, 0xe6, 0x20, 0xcb, 0x0f, 0x14, 0xca, 0x6e, 0xc3, 0x09, 0xc4, 0xa1, 0x3a, + 0x60, 0xd2, 0x32, 0x4b, 0x21, 0x6f, 0xc2, 0x8c, 0x59, 0xaf, 0x7b, 0xee, 0xa1, 0x55, 0x33, 0x03, + 0xca, 0x2c, 0xf4, 0x3d, 0x61, 0xa7, 0xf0, 0x88, 0xb7, 0x29, 0x25, 0x77, 0xc5, 0xf2, 0xf7, 0xb8, + 0xb9, 0xb2, 0x0e, 0x59, 0x11, 0x59, 0x2a, 0xfc, 0x65, 0x1d, 0x31, 0x33, 0xed, 0x8e, 0xbb, 0xfb, + 0x5f, 0x59, 0x5e, 0x46, 0x68, 0xf2, 0x0a, 0x1d, 0x0f, 0x3f, 0x45, 0x7f, 0xd9, 0x17, 0x60, 0x4a, + 0x84, 0x4a, 0x44, 0xdd, 0xbe, 0xfd, 0x8c, 0x8d, 0xfe, 0xfd, 0x31, 0x98, 0x6e, 0xab, 0xdd, 0xe9, + 0x22, 0x4a, 0x3f, 0xef, 0x49, 0xfb, 0xfb, 0x1a, 0x4c, 0xca, 0x70, 0x0e, 0xf5, 0x3e, 0x56, 0xa6, + 0xe7, 0x7d, 0xac, 0xae, 0x58, 0x8b, 0x61, 0xa8, 0x48, 0xf7, 0xfb, 0x58, 0x6d, 0xd9, 0x4f, 0x7f, + 0x1f, 0xab, 0xde, 0xd6, 0x4e, 0xe1, 0xdf, 0x66, 0x78, 0x8c, 0x7b, 0x18, 0x1f, 0xd7, 0x11, 0x51, + 0xa3, 0x75, 0x89, 0xa8, 0xf9, 0x73, 0x1a, 0x4c, 0x2b, 0x21, 0x73, 0xa5, 0x76, 0xdf, 0xd5, 0x83, + 0xe3, 0xa3, 0xb9, 0xc9, 0xed, 0x56, 0x81, 0x67, 0x3e, 0xbf, 0x9a, 0x6c, 0xb4, 0x13, 0xab, 0xf8, + 0xe4, 0x77, 0x35, 0xb8, 0xac, 0xc4, 0xdb, 0x75, 0x84, 0xeb, 0x29, 0xb0, 0x12, 0x08, 0xeb, 0xe7, + 0x8f, 0x8f, 0xe6, 0x2e, 0xb4, 0x82, 0xf1, 0xa2, 0x01, 0x7c, 0xcf, 0x8c, 0xf1, 0x82, 0x17, 0x4b, + 0xb9, 0xe2, 0x93, 0x6f, 0x6b, 0x90, 0x8f, 0xc6, 0x08, 0x2a, 0x10, 0x93, 0x08, 0xf1, 0xe1, 0xf1, + 0xd1, 0xdc, 0xd4, 0x86, 0x12, 0x31, 0xf8, 0xcc, 0xb0, 0xa6, 0x9c, 0x0e, 0x6a, 0x15, 0x9f, 0x1c, + 0x02, 0x91, 0x51, 0x83, 0x0a, 0x86, 0x14, 0x62, 0xb8, 0x77, 0x7c, 0x34, 0x37, 0xb1, 0xc1, 0x63, + 0x08, 0x9f, 0xb9, 0xf9, 0x09, 0x47, 0x25, 0x54, 0xf1, 0xc9, 0xaf, 0x69, 0x70, 0xb6, 0x2d, 0xd6, + 0x51, 0x41, 0x30, 0x8c, 0x08, 0x36, 0x8f, 0x8f, 0xe6, 0xce, 0x6c, 0x47, 0x0b, 0x3d, 0x33, 0x92, + 0x33, 0x8d, 0x6e, 0x04, 0x2b, 0x3e, 0xf9, 0x55, 0x0d, 0xf2, 0xd1, 0xa0, 0x4a, 0x05, 0x50, 0x06, + 0x01, 0x19, 0xc7, 0x47, 0x73, 0x33, 0x0f, 0x0e, 0x9e, 0x2b, 0x9e, 0x19, 0xf7, 0xa0, 0x2b, 0x9c, + 0xdf, 0xd2, 0x40, 0x3f, 0x29, 0x6c, 0x53, 0x01, 0x36, 0x82, 0xc0, 0x3e, 0x3a, 0x3e, 0x9a, 0x9b, + 0x7d, 0xd4, 0x35, 0x88, 0xf3, 0x99, 0x01, 0xce, 0xee, 0xc7, 0xd0, 0xad, 0xf8, 0xe4, 0x37, 0x34, + 0x38, 0xdf, 0x19, 0x25, 0xaa, 0x40, 0x4c, 0xb7, 0x06, 0xd3, 0x88, 0xc6, 0x8c, 0x3e, 0xfb, 0x60, + 0x7a, 0xdd, 0x08, 0x56, 0x7c, 0xbc, 0x60, 0xda, 0x55, 0x9b, 0xf6, 0xba, 0x60, 0x9a, 0x8d, 0x7d, + 0x51, 0xa2, 0xbb, 0xda, 0x56, 0x35, 0xa7, 0x72, 0x1a, 0x7c, 0x37, 0x99, 0xd6, 0x72, 0x69, 0xfd, + 0x6d, 0xc8, 0xdd, 0x71, 0x83, 0xa7, 0x58, 0xd3, 0x7e, 0x79, 0x04, 0x4e, 0x2b, 0x35, 0x3f, 0x87, + 0xfb, 0xf8, 0xff, 0x4a, 0x83, 0xe9, 0xc7, 0x6e, 0xc0, 0xc7, 0xae, 0xcb, 0x0d, 0xe3, 0xe5, 0x18, + 0xd6, 0x74, 0x20, 0x6d, 0xa5, 0x44, 0x97, 0xb3, 0x87, 0x62, 0x39, 0x3b, 0xdd, 0x9e, 0xff, 0xd4, + 0xeb, 0xd9, 0xe9, 0xc7, 0xed, 0x2d, 0x15, 0x0e, 0x20, 0x2d, 0xc9, 0x93, 0x2f, 0x46, 0xee, 0x6b, + 0x75, 0xbb, 0x17, 0x89, 0xe5, 0x4e, 0xb8, 0xa8, 0xd5, 0x3d, 0x86, 0x79, 0xa8, 0x7b, 0x0c, 0x73, + 0xe1, 0xdf, 0x68, 0x30, 0x86, 0x81, 0x54, 0xe1, 0x78, 0xbd, 0xe8, 0x28, 0xad, 0x8f, 0x00, 0x5a, + 0x43, 0x26, 0xc6, 0xe9, 0xd6, 0x53, 0x8d, 0x53, 0x78, 0xed, 0x41, 0x96, 0x28, 0xfc, 0x92, 0xd6, + 0x76, 0xf5, 0xad, 0x2f, 0xb3, 0xc0, 0x60, 0xdb, 0x10, 0xd7, 0x0b, 0xd1, 0xbc, 0x37, 0x10, 0x9a, + 0x08, 0xf7, 0x0c, 0x41, 0xa9, 0xf0, 0x8b, 0x30, 0xd3, 0x5d, 0x9c, 0xba, 0xcc, 0xe7, 0x07, 0xd1, + 0xf9, 0xfc, 0xee, 0x40, 0xcd, 0xab, 0xdd, 0x55, 0x3d, 0x3b, 0x57, 0x61, 0xb4, 0xdf, 0x78, 0xa5, + 0xdf, 0x4e, 0x89, 0xc8, 0xc5, 0xcf, 0x65, 0xce, 0xaa, 0xfe, 0xd2, 0xa1, 0x17, 0xe0, 0x2f, 0xfd, + 0x97, 0x1a, 0x4c, 0x79, 0xa2, 0x23, 0x11, 0x95, 0xc0, 0xdd, 0x9e, 0x3f, 0xd7, 0xcb, 0x43, 0xac, + 0xdc, 0xf8, 0x11, 0x44, 0x4e, 0x50, 0x07, 0xed, 0xf9, 0x4f, 0xaf, 0x0e, 0xbc, 0xf6, 0x96, 0x0a, + 0xdf, 0x6d, 0x17, 0xe4, 0x02, 0xa4, 0x65, 0x29, 0x79, 0xaa, 0xe4, 0x9d, 0x28, 0xe4, 0xdd, 0x5e, + 0x03, 0xfa, 0xb2, 0x3c, 0x64, 0x48, 0x0c, 0x1c, 0xfd, 0x29, 0x8e, 0x15, 0x3e, 0x81, 0x99, 0xee, + 0x2c, 0xe9, 0x22, 0xd2, 0xf7, 0xa2, 0x22, 0x7d, 0xab, 0x6f, 0xa6, 0x9f, 0x20, 0xce, 0x22, 0x54, + 0xe6, 0x75, 0x20, 0x2b, 0xad, 0x97, 0xbc, 0x7a, 0x86, 0x22, 0x5c, 0x11, 0xba, 0xad, 0x77, 0xc9, + 0x1f, 0x0e, 0xc1, 0xa8, 0xb8, 0xde, 0xcb, 0x1f, 0x6d, 0x7a, 0xd1, 0x5a, 0xf0, 0x35, 0x38, 0x4d, + 0x9d, 0xb2, 0xd7, 0x44, 0x17, 0xa6, 0x0c, 0x84, 0xc7, 0x3d, 0xba, 0x91, 0x6b, 0x65, 0x88, 0xf3, + 0x8c, 0x39, 0xb9, 0x6f, 0xe5, 0xa1, 0x2b, 0x7c, 0x8b, 0xcb, 0xb7, 0xa2, 0x18, 0xdd, 0xd2, 0x2a, + 0xc0, 0xf7, 0xc0, 0x49, 0xa5, 0x00, 0xdf, 0xf9, 0x5e, 0x81, 0x9c, 0x38, 0x60, 0xdf, 0xa3, 0x4d, + 0x41, 0x86, 0xdf, 0x73, 0x12, 0xee, 0x8d, 0x7b, 0xb4, 0xc9, 0x49, 0x45, 0x4b, 0x72, 0x7a, 0xc3, + 0x6d, 0x25, 0x91, 0xa6, 0xfe, 0x21, 0x8c, 0x4b, 0xee, 0x86, 0x61, 0x79, 0x52, 0x91, 0xf6, 0x7e, + 0x34, 0x44, 0xe5, 0xb6, 0x3c, 0xf1, 0xe0, 0x95, 0xf5, 0x6f, 0x6b, 0x70, 0xba, 0xe5, 0xaf, 0x1d, + 0xe8, 0xc8, 0x03, 0xa3, 0x95, 0x6a, 0x3b, 0x96, 0x43, 0x2b, 0x32, 0xc6, 0x48, 0x7e, 0x93, 0x82, + 0xea, 0x08, 0x4c, 0x60, 0x9b, 0xa1, 0x57, 0x65, 0x06, 0x12, 0xd4, 0xe1, 0xde, 0x3d, 0x99, 0xc3, + 0x12, 0xf4, 0xff, 0x99, 0x01, 0xa2, 0x42, 0x11, 0x1d, 0xad, 0x63, 0x98, 0x9d, 0x48, 0x15, 0x9d, + 0xbd, 0x1b, 0x1f, 0x8e, 0xd5, 0x46, 0xa2, 0xb8, 0xec, 0xda, 0x36, 0x2d, 0x07, 0xb4, 0x12, 0xe6, + 0x75, 0x5c, 0x97, 0x50, 0xda, 0x20, 0xcb, 0x00, 0xe8, 0x26, 0xf1, 0xa8, 0x4f, 0x07, 0x73, 0x65, + 0x66, 0x58, 0x3d, 0x83, 0x55, 0x23, 0x6f, 0x43, 0x5e, 0xbe, 0xa2, 0x51, 0x32, 0xeb, 0x75, 0x74, + 0x7c, 0x95, 0xea, 0x1e, 0xdd, 0xb5, 0x0e, 0x85, 0xff, 0x6b, 0x5a, 0xe6, 0x2f, 0xd6, 0xeb, 0x1b, + 0x66, 0x8d, 0x3e, 0xc4, 0x4c, 0xf2, 0x0d, 0x18, 0xc5, 0xfb, 0x7a, 0x4c, 0x02, 0x5c, 0x47, 0xfa, + 0xc0, 0xb6, 0x06, 0xeb, 0xf1, 0xaa, 0x78, 0x02, 0x2b, 0xec, 0xf9, 0x56, 0x8b, 0x64, 0x47, 0xdf, + 0x23, 0xed, 0x15, 0xfe, 0xfb, 0x10, 0xcc, 0xca, 0xea, 0x5d, 0xf8, 0xc5, 0xaf, 0x14, 0xa4, 0x99, + 0xc0, 0x86, 0xf1, 0x9b, 0x6d, 0x07, 0x3b, 0xfb, 0x76, 0xb1, 0x7b, 0x45, 0x19, 0x6c, 0xb8, 0x47, + 0x9b, 0x2b, 0x66, 0x60, 0xaa, 0x4b, 0xdf, 0xd0, 0xf3, 0x5e, 0xfa, 0xd6, 0x60, 0xcc, 0xac, 0x56, + 0x3d, 0x5a, 0xc5, 0xcd, 0x5a, 0xe0, 0x0f, 0x34, 0x8e, 0xa3, 0xad, 0xaa, 0x5b, 0x3e, 0xf9, 0x0a, + 0x4c, 0xc9, 0x6f, 0x74, 0x66, 0xb0, 0x61, 0x3b, 0x10, 0x8f, 0xa5, 0x64, 0x17, 0xce, 0x76, 0x50, + 0x5c, 0x11, 0x4f, 0xdb, 0x71, 0x82, 0xdf, 0x67, 0x04, 0x27, 0x15, 0x02, 0x6b, 0xa2, 0x7e, 0xe1, + 0x6f, 0x0e, 0xc1, 0xf9, 0x38, 0xd1, 0x24, 0x95, 0x96, 0xae, 0xcf, 0x2e, 0xac, 0x3f, 0x9d, 0x04, + 0xc4, 0x8e, 0x04, 0xae, 0x1f, 0x5f, 0x45, 0xcf, 0x25, 0x2a, 0xba, 0xa5, 0x3b, 0xdc, 0x73, 0xf9, + 0xd9, 0xd1, 0xdc, 0xfb, 0x03, 0xea, 0xdc, 0x5a, 0xf0, 0x81, 0xe5, 0x54, 0xa9, 0x57, 0xf7, 0x2c, + 0x27, 0x10, 0xbe, 0xcf, 0xf7, 0x65, 0xb4, 0xfc, 0x50, 0xa7, 0xb7, 0xb2, 0xbb, 0x90, 0x44, 0xa2, + 0xe4, 0x0b, 0x3f, 0xd1, 0xe0, 0x72, 0x7f, 0x92, 0x4c, 0x0c, 0xae, 0x25, 0x7c, 0x55, 0x28, 0x5f, + 0x6f, 0x6b, 0xaf, 0xaf, 0xc9, 0x90, 0x41, 0x32, 0x2f, 0x5a, 0x3c, 0xf5, 0x2d, 0x98, 0x5d, 0x16, + 0xca, 0xb2, 0x35, 0x78, 0x91, 0x63, 0xe7, 0x50, 0x85, 0x6a, 0x27, 0xaa, 0xd0, 0xa1, 0x76, 0x15, + 0xfa, 0xed, 0x21, 0x28, 0x84, 0xe4, 0x22, 0xab, 0x77, 0xdd, 0xf5, 0x02, 0x32, 0x1e, 0x46, 0x89, + 0x24, 0x70, 0x7c, 0xce, 0x43, 0xa6, 0xec, 0xd6, 0xea, 0x36, 0x0d, 0x42, 0x15, 0xde, 0x4a, 0x20, + 0x37, 0x61, 0x3a, 0x54, 0x8a, 0xa5, 0xdd, 0xd6, 0xe0, 0x0a, 0xf7, 0xdc, 0x54, 0x98, 0xa9, 0x0c, + 0x3c, 0x79, 0x1f, 0xf2, 0xad, 0x4a, 0xca, 0x4b, 0xa0, 0x8c, 0x89, 0xaa, 0xc6, 0x9f, 0xf1, 0xbb, + 0xc0, 0xc4, 0x08, 0xbc, 0x51, 0x8f, 0x33, 0x80, 0xbf, 0x50, 0x91, 0x1a, 0x60, 0xd6, 0x66, 0xc3, + 0x9a, 0x8b, 0x81, 0xfe, 0x0b, 0xf0, 0xea, 0xb2, 0x47, 0xcd, 0x80, 0x9e, 0xcc, 0x0f, 0xc9, 0xe9, + 0x13, 0x3b, 0xaa, 0x9d, 0xdc, 0x51, 0xbd, 0x09, 0x57, 0x7a, 0xd3, 0x17, 0x4b, 0xd8, 0x7d, 0x18, + 0xf6, 0x30, 0x45, 0x08, 0xe6, 0xad, 0x7e, 0xa6, 0x72, 0x27, 0x39, 0x41, 0x44, 0x7f, 0x19, 0xf4, + 0x93, 0x4b, 0x85, 0x8e, 0xb6, 0x3f, 0x03, 0x97, 0x62, 0x4b, 0x09, 0x6c, 0xdb, 0x30, 0xc2, 0xc9, + 0xca, 0xb5, 0xf5, 0xe9, 0xc0, 0x49, 0xd5, 0x2e, 0x68, 0xe9, 0xbf, 0xa3, 0xc1, 0x54, 0xb7, 0xd2, + 0x1d, 0x32, 0x78, 0x22, 0xf3, 0x87, 0x62, 0xa4, 0xec, 0x36, 0x8c, 0x96, 0xe5, 0x64, 0x96, 0xcf, + 0x9c, 0xf4, 0x2d, 0x25, 0x61, 0xcd, 0x45, 0x19, 0x15, 0xfe, 0x21, 0x9c, 0xeb, 0xde, 0x33, 0x2e, + 0x1f, 0xef, 0xc4, 0xc8, 0x34, 0xef, 0xc8, 0x09, 0xd2, 0xac, 0xef, 0xc3, 0xf9, 0xee, 0x84, 0xc3, + 0x87, 0x1b, 0xb2, 0x0a, 0x3d, 0xa1, 0x26, 0xe7, 0x07, 0x1d, 0x00, 0x95, 0x86, 0x7e, 0x13, 0xf2, + 0x77, 0xdd, 0x1d, 0xe9, 0xe3, 0x15, 0x6e, 0xbb, 0x5e, 0x26, 0xf9, 0xff, 0xd2, 0xe0, 0x6c, 0x97, + 0x5a, 0x9f, 0xc3, 0x0e, 0xf5, 0x6b, 0x30, 0xea, 0x35, 0x1c, 0xc7, 0x72, 0xaa, 0xa5, 0x27, 0xee, + 0x8e, 0x3c, 0x15, 0x88, 0x0b, 0x2d, 0x3d, 0x11, 0x27, 0xe6, 0x64, 0x05, 0xb5, 0xbb, 0xee, 0x8e, + 0x5f, 0x98, 0x86, 0xc4, 0x5d, 0x77, 0xa7, 0x5d, 0xe4, 0xf4, 0xab, 0x90, 0xbb, 0xeb, 0xee, 0x44, + 0x59, 0x33, 0x0d, 0xc3, 0x4f, 0xdc, 0x9d, 0xd6, 0x88, 0xa6, 0x9e, 0xb8, 0x3b, 0x6b, 0x15, 0x7d, + 0x15, 0x4e, 0x2b, 0x45, 0x05, 0x3f, 0x6e, 0x40, 0xe2, 0x89, 0xbb, 0x23, 0xe6, 0xf2, 0x6c, 0xdb, + 0x22, 0x83, 0x0f, 0x32, 0xf3, 0xc7, 0x99, 0x11, 0x10, 0x2b, 0xaa, 0xbb, 0x30, 0x85, 0x56, 0xe1, + 0xe6, 0xa3, 0xf5, 0xc1, 0x5d, 0x8b, 0x0b, 0x30, 0x8d, 0x96, 0x68, 0xa9, 0x4e, 0x3d, 0xdf, 0x42, + 0xc5, 0xd8, 0x5a, 0x55, 0xd3, 0xc6, 0x24, 0x66, 0x3e, 0x94, 0x79, 0xdc, 0x5f, 0x76, 0x06, 0xa6, + 0xdb, 0x1a, 0xe4, 0xd8, 0xf5, 0x25, 0x38, 0xb7, 0xe6, 0x54, 0xe8, 0x21, 0x3e, 0x8c, 0xda, 0x5a, + 0xfc, 0x06, 0x3a, 0x7b, 0xfc, 0x47, 0x1a, 0x9c, 0xef, 0x4e, 0x44, 0x30, 0x48, 0x2c, 0xc6, 0x3c, + 0xb5, 0xdb, 0xf3, 0x93, 0xea, 0x62, 0xdc, 0x8d, 0x92, 0x6a, 0x94, 0x8b, 0x05, 0xfe, 0x79, 0x18, + 0xe5, 0xac, 0xf7, 0xf8, 0x23, 0xda, 0xe6, 0x60, 0xbd, 0x9f, 0x85, 0xf3, 0xdd, 0x69, 0xf0, 0xce, + 0x5f, 0xbb, 0x02, 0xd0, 0xba, 0x11, 0x42, 0xa6, 0x20, 0x17, 0xde, 0x07, 0x28, 0x6d, 0x6e, 0x2d, + 0x2e, 0xdf, 0xdb, 0xcc, 0x9d, 0xd2, 0x93, 0x69, 0x2d, 0xa7, 0x5d, 0x7b, 0x19, 0xd2, 0xf2, 0x86, + 0x85, 0x72, 0x95, 0x60, 0x1c, 0x20, 0xac, 0xb1, 0x99, 0xd3, 0x16, 0xfe, 0xf5, 0xad, 0xd0, 0xd7, + 0xfe, 0x7d, 0x0d, 0x46, 0xd5, 0x57, 0x67, 0x49, 0xb1, 0xbf, 0x77, 0x65, 0x65, 0x07, 0x0b, 0xf3, + 0x7d, 0x97, 0x17, 0xe2, 0xf2, 0xea, 0xb7, 0xfe, 0xdd, 0x7f, 0xfd, 0x2b, 0x43, 0x17, 0xc9, 0xdc, + 0xbc, 0xd8, 0x37, 0xcf, 0xab, 0x8f, 0xd2, 0xce, 0x7f, 0x22, 0xd8, 0xf5, 0x29, 0xf9, 0xf3, 0x1a, + 0x8c, 0xc8, 0xfd, 0x7c, 0x5c, 0x20, 0x76, 0xf4, 0x0d, 0xdb, 0xc2, 0xb5, 0x7e, 0x8a, 0x0a, 0x2c, + 0x3a, 0x62, 0x39, 0x4f, 0x0a, 0x21, 0x16, 0x71, 0x91, 0x4b, 0x81, 0xb1, 0x03, 0x23, 0xe2, 0x31, + 0x99, 0x58, 0x14, 0xd1, 0x37, 0x75, 0x62, 0x51, 0xb4, 0xbd, 0x4d, 0xa3, 0x9f, 0x22, 0x1e, 0xa4, + 0xf0, 0xc5, 0x4c, 0xf2, 0x6a, 0xef, 0x37, 0x35, 0x39, 0xfd, 0x2b, 0xfd, 0x3e, 0xbe, 0xa9, 0xcf, + 0x60, 0x1f, 0x73, 0x64, 0x3c, 0xec, 0x23, 0x7f, 0xdc, 0xf3, 0x1b, 0x90, 0xc4, 0xeb, 0x1d, 0x97, + 0x7b, 0xbe, 0x29, 0xc4, 0x5b, 0x1c, 0xe8, 0xb1, 0x57, 0xfd, 0x02, 0xb6, 0x5a, 0x20, 0xf9, 0x68, + 0xab, 0x0a, 0x5f, 0x7f, 0x11, 0x46, 0x10, 0xe8, 0xf6, 0x5a, 0xff, 0xbd, 0xbe, 0x31, 0xe8, 0x93, + 0xa3, 0xfa, 0x59, 0xc4, 0x31, 0x49, 0x4e, 0x47, 0x71, 0x94, 0x1a, 0x16, 0xf9, 0x06, 0xe0, 0x3c, + 0xdc, 0x5e, 0xeb, 0x9b, 0x05, 0xfd, 0xbe, 0xa2, 0xaa, 0x5f, 0xc2, 0x56, 0x5f, 0x22, 0xe7, 0x3a, + 0x5a, 0x55, 0x18, 0xf0, 0x29, 0x7f, 0x57, 0x08, 0xef, 0x34, 0x90, 0xd7, 0xfa, 0xbb, 0xf9, 0x70, + 0xf2, 0x50, 0x9c, 0x78, 0x4d, 0x42, 0x9f, 0x46, 0x30, 0x13, 0x64, 0x2c, 0x04, 0xe3, 0x99, 0xbb, + 0x01, 0xf9, 0xa6, 0x06, 0xc3, 0xfc, 0x30, 0x9a, 0xf4, 0x7c, 0x53, 0x22, 0x1c, 0x80, 0xab, 0x7d, + 0x94, 0x14, 0xcd, 0x5e, 0xc4, 0x66, 0xcf, 0x91, 0xb3, 0x4a, 0xb3, 0xac, 0x80, 0xc2, 0x01, 0x1f, + 0x86, 0xf9, 0x7d, 0xf4, 0x58, 0x04, 0x91, 0x2b, 0xeb, 0x05, 0xf5, 0xda, 0x9f, 0xf8, 0x1b, 0x06, + 0x6b, 0xce, 0xae, 0x2b, 0xc4, 0xae, 0xb3, 0x51, 0xf1, 0xe7, 0x0e, 0x5a, 0x8d, 0xfe, 0x55, 0x0d, + 0xb2, 0xca, 0x45, 0x6a, 0xf2, 0x7a, 0x7f, 0x17, 0xae, 0x65, 0xfb, 0xc5, 0x7e, 0x8b, 0x0b, 0x36, + 0x5c, 0x46, 0x44, 0x17, 0xc8, 0x6c, 0x88, 0x88, 0x47, 0xa7, 0xe0, 0x7a, 0xab, 0xc0, 0xfa, 0xae, + 0x06, 0x99, 0xf0, 0xa6, 0x6b, 0xac, 0x38, 0xb4, 0xdf, 0xef, 0x8d, 0x15, 0x87, 0x8e, 0xcb, 0xb7, + 0xfa, 0x55, 0x04, 0x74, 0x89, 0x5c, 0x0c, 0x01, 0x99, 0xb2, 0x0c, 0x4a, 0xa9, 0x82, 0xe9, 0x07, + 0x1a, 0x8c, 0x47, 0x6f, 0x42, 0x93, 0x1b, 0x7d, 0xb5, 0xa5, 0x78, 0x2e, 0x0a, 0x6f, 0x0c, 0x50, + 0x43, 0x40, 0x7c, 0x0d, 0x21, 0xbe, 0x42, 0x2e, 0x75, 0x81, 0x88, 0x42, 0x34, 0xff, 0x89, 0xf4, + 0x41, 0x7c, 0x4a, 0x7e, 0x59, 0x83, 0x51, 0x35, 0x48, 0x36, 0x76, 0x05, 0xeb, 0x12, 0x15, 0x1f, + 0xbb, 0x82, 0x75, 0x0b, 0x02, 0xee, 0xa2, 0x53, 0xc2, 0xc8, 0xde, 0xef, 0x89, 0x60, 0x4f, 0x7c, + 0xf9, 0xf4, 0xf3, 0x43, 0x34, 0x87, 0x88, 0xce, 0x92, 0x33, 0x21, 0x22, 0x7c, 0x63, 0xb5, 0x14, + 0xe2, 0xfa, 0xbe, 0x06, 0x59, 0x25, 0x66, 0x37, 0x56, 0xe8, 0x3b, 0xc3, 0x89, 0x63, 0x85, 0xbe, + 0x4b, 0x28, 0xb0, 0x7e, 0x0d, 0xf1, 0xbc, 0xac, 0x2b, 0x6b, 0x3c, 0x96, 0xe2, 0xf1, 0xe1, 0x2d, + 0x09, 0x7b, 0x4f, 0xbb, 0x46, 0xfe, 0x9a, 0x06, 0x39, 0x4e, 0x03, 0x99, 0xf6, 0xb9, 0xe0, 0x13, + 0x93, 0x52, 0x3f, 0xd7, 0x8e, 0x8f, 0xb3, 0x0d, 0x51, 0x32, 0x6c, 0x7f, 0x57, 0xc4, 0x81, 0xb7, + 0xc7, 0xcb, 0x92, 0xb7, 0x7a, 0x8c, 0xd0, 0x09, 0xc1, 0xc0, 0x85, 0xb7, 0x07, 0xae, 0x77, 0xa2, + 0xa5, 0xd2, 0x0a, 0xc5, 0x2d, 0x89, 0x70, 0xdb, 0x7f, 0xa8, 0xc1, 0xd9, 0x50, 0xf8, 0x7e, 0xf6, + 0x90, 0xaf, 0x20, 0x64, 0x9d, 0x5c, 0x68, 0x13, 0xca, 0x4e, 0xe0, 0x7f, 0x5d, 0x83, 0x5c, 0x7b, + 0x5c, 0x27, 0x59, 0xe8, 0xd1, 0x6e, 0x97, 0x48, 0xe0, 0xc2, 0xcd, 0x81, 0xea, 0x08, 0x9c, 0xb3, + 0x88, 0x33, 0x4f, 0x66, 0x5a, 0x46, 0xa0, 0xe5, 0x07, 0xfe, 0xbe, 0x5d, 0xe2, 0xd1, 0xa0, 0xbf, + 0x85, 0x8f, 0x87, 0x0b, 0xb6, 0xfe, 0x6c, 0x20, 0xbe, 0x8c, 0x10, 0x67, 0xc9, 0xf9, 0x36, 0x56, + 0x46, 0x81, 0xfe, 0xa6, 0x06, 0x63, 0x91, 0xa0, 0x76, 0x32, 0xdf, 0x73, 0x5e, 0x44, 0x23, 0xef, + 0x63, 0x0d, 0xac, 0xae, 0xf1, 0xf2, 0xfa, 0x75, 0x84, 0x76, 0x59, 0xbf, 0xd8, 0x3e, 0x95, 0x84, + 0xee, 0x89, 0x4e, 0xf6, 0xbf, 0xad, 0xc9, 0xeb, 0x0a, 0xaa, 0x86, 0xfc, 0x3c, 0x70, 0x0a, 0x69, + 0xd4, 0x5f, 0xea, 0x3e, 0xe5, 0x05, 0x5a, 0x86, 0xf1, 0x9b, 0x1a, 0x64, 0xc2, 0x98, 0xd6, 0xd8, + 0x95, 0xb8, 0x3d, 0xae, 0x37, 0x76, 0x25, 0xee, 0x08, 0x93, 0xd5, 0xf3, 0x08, 0x89, 0xe8, 0x2d, + 0xc3, 0xcc, 0xaf, 0x9b, 0x08, 0xe1, 0x1b, 0xb8, 0x3f, 0x2b, 0xef, 0xc5, 0x9b, 0x66, 0x91, 0x67, + 0x07, 0x62, 0x8d, 0x53, 0xf5, 0x99, 0x8c, 0x2e, 0x36, 0x92, 0x8f, 0x84, 0x94, 0x85, 0xff, 0xcf, + 0x6a, 0x30, 0x22, 0x6e, 0xb7, 0xc7, 0x6e, 0x7a, 0xa2, 0x37, 0xe0, 0xfb, 0x87, 0xd0, 0xa9, 0xcd, + 0xea, 0x9c, 0x52, 0x1b, 0x06, 0x71, 0x69, 0x3d, 0x16, 0x43, 0xf4, 0x62, 0xfb, 0xb3, 0x60, 0x10, + 0x2f, 0x0c, 0x2b, 0x18, 0xfe, 0x82, 0x06, 0x69, 0xf9, 0x06, 0x01, 0x89, 0xdb, 0xd2, 0xb5, 0x3d, + 0xa3, 0x50, 0x78, 0xad, 0xaf, 0xb2, 0x02, 0x49, 0xe7, 0x5e, 0x09, 0x3d, 0xbc, 0x51, 0x9b, 0x75, + 0x54, 0x7d, 0x13, 0x23, 0xde, 0xa2, 0xe8, 0x7c, 0x6c, 0x23, 0xde, 0xa2, 0xe8, 0xf2, 0xd8, 0x46, + 0x97, 0x1d, 0x8c, 0xed, 0x56, 0xdb, 0x61, 0xfd, 0x9a, 0x06, 0x23, 0xa2, 0x76, 0xec, 0x10, 0x45, + 0x1f, 0xdf, 0x28, 0xbc, 0x1e, 0x5f, 0xb4, 0xed, 0xe9, 0x11, 0x69, 0x4c, 0x10, 0x3d, 0x06, 0xca, + 0xfc, 0x27, 0x2c, 0xe1, 0x53, 0xb6, 0xa9, 0x5d, 0x77, 0xab, 0x7e, 0xec, 0x8e, 0x4e, 0x79, 0x9f, + 0x65, 0x50, 0x28, 0xdd, 0xec, 0xac, 0xaa, 0xca, 0x91, 0xdf, 0xd0, 0xf0, 0x99, 0xd1, 0x56, 0xd0, + 0x5e, 0xac, 0x6a, 0xeb, 0x16, 0x7f, 0x1e, 0xab, 0xda, 0xba, 0xc6, 0x03, 0x76, 0x59, 0xc0, 0x44, + 0xc0, 0xb5, 0xb8, 0xd5, 0xfe, 0x2d, 0x0d, 0x32, 0x61, 0xe4, 0x51, 0xac, 0x42, 0x6b, 0x0f, 0x1c, + 0x8c, 0x55, 0x68, 0x1d, 0xc1, 0x4c, 0x7a, 0x01, 0x81, 0x4c, 0x11, 0x12, 0x02, 0x79, 0xec, 0x06, + 0x02, 0xc4, 0xa7, 0x90, 0xe2, 0x3b, 0x88, 0x57, 0x7b, 0x07, 0x93, 0xf4, 0x3e, 0xe2, 0x88, 0xee, + 0x17, 0x4e, 0xd8, 0x6a, 0xaa, 0xbb, 0x84, 0x1f, 0x68, 0x90, 0x55, 0x7d, 0x06, 0xf1, 0xd7, 0x43, + 0xda, 0xcf, 0xeb, 0x0b, 0x5f, 0xec, 0x2c, 0xae, 0xfe, 0x4d, 0xba, 0xc8, 0x5f, 0xab, 0x53, 0xea, + 0x73, 0x47, 0x46, 0x97, 0x3d, 0xa0, 0xfa, 0xa7, 0xee, 0x5a, 0xd2, 0xc3, 0xb6, 0xe4, 0x3c, 0x26, + 0xa3, 0x87, 0xde, 0x57, 0x82, 0x62, 0x62, 0xb7, 0xe4, 0xd1, 0x00, 0x8f, 0xae, 0x9a, 0x9f, 0x15, + 0x50, 0x20, 0xfc, 0x45, 0x0d, 0xcf, 0x1a, 0x65, 0xdc, 0xc2, 0xf5, 0x3e, 0x3d, 0xc4, 0xbd, 0x67, + 0x53, 0xa7, 0x3f, 0x59, 0x3f, 0x87, 0x70, 0xa6, 0xc9, 0xa4, 0xba, 0x10, 0xc9, 0x96, 0x7f, 0xa0, + 0xc1, 0x4c, 0x87, 0x3b, 0x93, 0x2f, 0xc9, 0x71, 0x11, 0x76, 0xf1, 0x1e, 0xd0, 0x41, 0x11, 0x76, + 0xce, 0x2c, 0x19, 0x8e, 0xe2, 0x07, 0xb5, 0xc0, 0x27, 0x3f, 0xd6, 0xe0, 0x42, 0x2f, 0x97, 0x1d, + 0x59, 0x8a, 0x83, 0xdb, 0x9f, 0x3f, 0xb1, 0xb0, 0xfc, 0x4c, 0x34, 0xa2, 0x3a, 0x5d, 0xcf, 0x2b, + 0xfc, 0xae, 0x05, 0x4c, 0x14, 0x85, 0x8b, 0x8d, 0x99, 0x1e, 0xff, 0x5c, 0x3b, 0xc9, 0xc1, 0x84, + 0x48, 0x7c, 0xf2, 0xa5, 0xa7, 0x72, 0xe6, 0x85, 0x23, 0xf0, 0xfe, 0xd3, 0x56, 0x3f, 0x71, 0xb1, + 0x6c, 0xeb, 0x04, 0xf9, 0xa7, 0x27, 0xf9, 0x09, 0xdf, 0x1a, 0xb8, 0xe9, 0xde, 0x3b, 0xa0, 0x38, + 0x5f, 0x9c, 0xfe, 0x26, 0x62, 0x2d, 0x92, 0xeb, 0x1d, 0x58, 0xe7, 0x3f, 0x39, 0xc9, 0xfd, 0xf7, + 0x29, 0xf9, 0x6d, 0x0d, 0x3d, 0x44, 0x51, 0x8f, 0x14, 0xb9, 0x39, 0x98, 0xff, 0x8a, 0x23, 0x7f, + 0xf3, 0x69, 0x9c, 0x5e, 0x5d, 0x4e, 0xe8, 0x9f, 0xb8, 0x3b, 0x25, 0x4f, 0x14, 0x8e, 0x9a, 0x47, + 0x99, 0xd0, 0x97, 0x15, 0xbb, 0xb0, 0xb4, 0x3b, 0xc7, 0x62, 0x17, 0x96, 0x0e, 0xf7, 0x98, 0xfe, + 0x12, 0x22, 0x3a, 0x43, 0xa6, 0x55, 0x44, 0xf3, 0x9f, 0x70, 0xf7, 0xda, 0xa7, 0xe4, 0x7b, 0x1a, + 0xbe, 0xf2, 0xd6, 0xf2, 0x4d, 0xc5, 0xae, 0xba, 0xdd, 0xdc, 0x66, 0xb1, 0xab, 0x6e, 0x77, 0xb7, + 0x97, 0x50, 0xa6, 0x7a, 0x4b, 0x37, 0xa0, 0x1f, 0xc9, 0xdf, 0xb7, 0xf1, 0x68, 0x8f, 0xcd, 0xa5, + 0xdf, 0xd5, 0x60, 0xaa, 0x9b, 0x2f, 0x2a, 0x56, 0x12, 0x63, 0x7c, 0x69, 0xb1, 0x92, 0x18, 0xe7, + 0x3e, 0xd3, 0x5f, 0x41, 0xb0, 0x73, 0xa4, 0xb5, 0xfb, 0xc1, 0x3f, 0xd2, 0x8a, 0x7f, 0xe3, 0x50, + 0xf1, 0x88, 0xfd, 0x50, 0x13, 0x5e, 0xc5, 0x36, 0x4f, 0x54, 0x2c, 0xe0, 0x18, 0xf7, 0x57, 0xe1, + 0xed, 0x81, 0xeb, 0x9d, 0xb8, 0x5d, 0x43, 0xee, 0x46, 0x51, 0x33, 0x26, 0x2f, 0x5d, 0xfb, 0xd1, + 0x7f, 0x99, 0x3d, 0xf5, 0xa3, 0xe3, 0x59, 0xed, 0xc7, 0xc7, 0xb3, 0xda, 0x4f, 0x8e, 0x67, 0xb5, + 0xff, 0x7c, 0x3c, 0xab, 0xfd, 0xfa, 0x4f, 0x67, 0x4f, 0xfd, 0xf8, 0xa7, 0xb3, 0xa7, 0x7e, 0xf2, + 0xd3, 0xd9, 0x53, 0x1f, 0xa5, 0x65, 0x93, 0x3b, 0xc3, 0xe8, 0xd5, 0xbb, 0xf9, 0xff, 0x03, 0x00, + 0x00, 0xff, 0xff, 0x26, 0x0c, 0xf0, 0xf0, 0x50, 0x77, 0x00, 0x00, } func (this *PrettySpan) Equal(that interface{}) bool { @@ -5488,6 +5562,7 @@ type StatusClient interface { JobStatus(ctx context.Context, in *JobStatusRequest, opts ...grpc.CallOption) (*JobStatusResponse, error) ResetSQLStats(ctx context.Context, in *ResetSQLStatsRequest, opts ...grpc.CallOption) (*ResetSQLStatsResponse, error) IndexUsageStatistics(ctx context.Context, in *IndexUsageStatisticsRequest, opts ...grpc.CallOption) (*IndexUsageStatisticsResponse, error) + ResetIndexUsageStats(ctx context.Context, in *ResetIndexUsageStatsRequest, opts ...grpc.CallOption) (*ResetIndexUsageStatsResponse, error) } type statusClient struct { @@ -5903,6 +5978,15 @@ func (c *statusClient) IndexUsageStatistics(ctx context.Context, in *IndexUsageS return out, nil } +func (c *statusClient) ResetIndexUsageStats(ctx context.Context, in *ResetIndexUsageStatsRequest, opts ...grpc.CallOption) (*ResetIndexUsageStatsResponse, error) { + out := new(ResetIndexUsageStatsResponse) + err := c.cc.Invoke(ctx, "/cockroach.server.serverpb.Status/ResetIndexUsageStats", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // StatusServer is the server API for Status service. type StatusServer interface { // Certificates retrieves a copy of the TLS certificates. @@ -6043,6 +6127,7 @@ type StatusServer interface { JobStatus(context.Context, *JobStatusRequest) (*JobStatusResponse, error) ResetSQLStats(context.Context, *ResetSQLStatsRequest) (*ResetSQLStatsResponse, error) IndexUsageStatistics(context.Context, *IndexUsageStatisticsRequest) (*IndexUsageStatisticsResponse, error) + ResetIndexUsageStats(context.Context, *ResetIndexUsageStatsRequest) (*ResetIndexUsageStatsResponse, error) } // UnimplementedStatusServer can be embedded to have forward compatible implementations. @@ -6184,6 +6269,9 @@ func (*UnimplementedStatusServer) ResetSQLStats(ctx context.Context, req *ResetS func (*UnimplementedStatusServer) IndexUsageStatistics(ctx context.Context, req *IndexUsageStatisticsRequest) (*IndexUsageStatisticsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method IndexUsageStatistics not implemented") } +func (*UnimplementedStatusServer) ResetIndexUsageStats(ctx context.Context, req *ResetIndexUsageStatsRequest) (*ResetIndexUsageStatsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResetIndexUsageStats not implemented") +} func RegisterStatusServer(s *grpc.Server, srv StatusServer) { s.RegisterService(&_Status_serviceDesc, srv) @@ -6999,6 +7087,24 @@ func _Status_IndexUsageStatistics_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Status_ResetIndexUsageStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetIndexUsageStatsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StatusServer).ResetIndexUsageStats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cockroach.server.serverpb.Status/ResetIndexUsageStats", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StatusServer).ResetIndexUsageStats(ctx, req.(*ResetIndexUsageStatsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Status_serviceDesc = grpc.ServiceDesc{ ServiceName: "cockroach.server.serverpb.Status", HandlerType: (*StatusServer)(nil), @@ -7183,6 +7289,10 @@ var _Status_serviceDesc = grpc.ServiceDesc{ MethodName: "IndexUsageStatistics", Handler: _Status_IndexUsageStatistics_Handler, }, + { + MethodName: "ResetIndexUsageStats", + Handler: _Status_ResetIndexUsageStats_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "server/serverpb/status.proto", @@ -12810,6 +12920,14 @@ func (m *IndexUsageStatisticsResponse) MarshalToSizedBuffer(dAtA []byte) (int, e _ = i var l int _ = l + n77, err77 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.LastReset, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.LastReset):]) + if err77 != nil { + return 0, err77 + } + i -= n77 + i = encodeVarintStatus(dAtA, i, uint64(n77)) + i-- + dAtA[i] = 0x1a if len(m.Statistics) > 0 { for iNdEx := len(m.Statistics) - 1; iNdEx >= 0; iNdEx-- { { @@ -12827,6 +12945,59 @@ func (m *IndexUsageStatisticsResponse) MarshalToSizedBuffer(dAtA []byte) (int, e return len(dAtA) - i, nil } +func (m *ResetIndexUsageStatsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResetIndexUsageStatsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResetIndexUsageStatsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintStatus(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResetIndexUsageStatsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResetIndexUsageStatsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResetIndexUsageStatsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintStatus(dAtA []byte, offset int, v uint64) int { offset -= sovStatus(v) base := offset @@ -15166,6 +15337,30 @@ func (m *IndexUsageStatisticsResponse) Size() (n int) { n += 1 + l + sovStatus(uint64(l)) } } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.LastReset) + n += 1 + l + sovStatus(uint64(l)) + return n +} + +func (m *ResetIndexUsageStatsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.NodeID) + if l > 0 { + n += 1 + l + sovStatus(uint64(l)) + } + return n +} + +func (m *ResetIndexUsageStatsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l return n } @@ -31418,6 +31613,171 @@ func (m *IndexUsageStatisticsResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastReset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStatus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStatus + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStatus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.LastReset, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStatus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStatus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResetIndexUsageStatsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStatus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResetIndexUsageStatsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResetIndexUsageStatsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStatus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthStatus + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStatus + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipStatus(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStatus + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResetIndexUsageStatsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStatus + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResetIndexUsageStatsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResetIndexUsageStatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { default: iNdEx = preIndex skippy, err := skipStatus(dAtA[iNdEx:]) diff --git a/pkg/server/serverpb/status.pb.gw.go b/pkg/server/serverpb/status.pb.gw.go index 8abfc784c51d..a033271a1592 100644 --- a/pkg/server/serverpb/status.pb.gw.go +++ b/pkg/server/serverpb/status.pb.gw.go @@ -2093,6 +2093,40 @@ func local_request_Status_IndexUsageStatistics_0(ctx context.Context, marshaler } +func request_Status_ResetIndexUsageStats_0(ctx context.Context, marshaler runtime.Marshaler, client StatusClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ResetIndexUsageStatsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ResetIndexUsageStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Status_ResetIndexUsageStats_0(ctx context.Context, marshaler runtime.Marshaler, server StatusServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ResetIndexUsageStatsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ResetIndexUsageStats(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterStatusHandlerServer registers the http handlers for service Status to "mux". // UnaryRPC :call StatusServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -3111,6 +3145,29 @@ func RegisterStatusHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser }) + mux.Handle("POST", pattern_Status_ResetIndexUsageStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Status_ResetIndexUsageStats_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Status_ResetIndexUsageStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -4032,6 +4089,26 @@ func RegisterStatusHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli }) + mux.Handle("POST", pattern_Status_ResetIndexUsageStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Status_ResetIndexUsageStats_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Status_ResetIndexUsageStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -4123,6 +4200,8 @@ var ( pattern_Status_ResetSQLStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"_status", "resetsqlstats"}, "", runtime.AssumeColonVerbOpt(true))) pattern_Status_IndexUsageStatistics_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"_status", "indexusagestatistics"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Status_ResetIndexUsageStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"_status", "resetindexusagestats"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( @@ -4213,4 +4292,6 @@ var ( forward_Status_ResetSQLStats_0 = runtime.ForwardResponseMessage forward_Status_IndexUsageStatistics_0 = runtime.ForwardResponseMessage + + forward_Status_ResetIndexUsageStats_0 = runtime.ForwardResponseMessage ) diff --git a/pkg/server/serverpb/status.proto b/pkg/server/serverpb/status.proto index 16ed9f2e5fcb..355abb70dca1 100644 --- a/pkg/server/serverpb/status.proto +++ b/pkg/server/serverpb/status.proto @@ -1324,6 +1324,17 @@ message IndexUsageStatisticsRequest { // Response object returned by IndexUsageStatistics. message IndexUsageStatisticsResponse { repeated cockroach.sql.CollectedIndexUsageStatistics statistics = 1 [(gogoproto.nullable) = false]; + // Timestamp of the last index usage stats reset. + google.protobuf.Timestamp last_reset = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + +// Request object for issuing a index usage stats reset request. +message ResetIndexUsageStatsRequest { + string node_id = 1 [(gogoproto.customname) = "NodeID"]; +} + +// Response object returned by ResetIndexUsageStatsRequest. +message ResetIndexUsageStatsResponse { } service Status { @@ -1684,4 +1695,11 @@ service Status { get: "/_status/indexusagestatistics" }; } + + rpc ResetIndexUsageStats(ResetIndexUsageStatsRequest) returns (ResetIndexUsageStatsResponse) { + option (google.api.http) = { + post: "/_status/resetindexusagestats" + body: "*" + }; + } } diff --git a/pkg/server/tenant_status.go b/pkg/server/tenant_status.go index 017237bfce31..e406f6dfe919 100644 --- a/pkg/server/tenant_status.go +++ b/pkg/server/tenant_status.go @@ -346,7 +346,7 @@ func (t *tenantStatusServer) ResetSQLStats( ctx = propagateGatewayMetadata(ctx) ctx = t.AnnotateCtx(ctx) - if _, err := t.privilegeChecker.requireViewActivityPermission(ctx); err != nil { + if _, err := t.privilegeChecker.requireAdminUser(ctx); err != nil { return nil, err } @@ -743,3 +743,63 @@ func (t *tenantStatusServer) IndexUsageStatistics( return resp, nil } + +func (t *tenantStatusServer) ResetIndexUsageStats( + ctx context.Context, req *serverpb.ResetIndexUsageStatsRequest, +) (*serverpb.ResetIndexUsageStatsResponse, error) { + ctx = propagateGatewayMetadata(ctx) + ctx = t.AnnotateCtx(ctx) + + if _, err := t.privilegeChecker.requireAdminUser(ctx); err != nil { + return nil, err + } + + localReq := &serverpb.ResetIndexUsageStatsRequest{ + NodeID: "local", + } + resp := &serverpb.ResetIndexUsageStatsResponse{} + + if len(req.NodeID) > 0 { + parsedInstanceID, local, err := t.parseInstanceID(req.NodeID) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + if local { + t.sqlServer.pgServer.SQLServer.GetLocalIndexStatistics().Reset() + return resp, nil + } + + instance, err := t.sqlServer.sqlInstanceProvider.GetInstance(ctx, parsedInstanceID) + if err != nil { + return nil, err + } + statusClient, err := t.dialPod(ctx, parsedInstanceID, instance.InstanceAddr) + if err != nil { + return nil, err + } + + return statusClient.ResetIndexUsageStats(ctx, localReq) + } + + resetIndexUsageStats := func(ctx context.Context, client interface{}, _ base.SQLInstanceID) (interface{}, error) { + statusClient := client.(serverpb.StatusClient) + return statusClient.ResetIndexUsageStats(ctx, localReq) + } + + var combinedError error + + if err := t.iteratePods(ctx, fmt.Sprintf("Resetting index usage stats for instance %s", req.NodeID), + t.dialCallback, + resetIndexUsageStats, + func(instanceID base.SQLInstanceID, resp interface{}) { + // Nothing to do here. + }, + func(_ base.SQLInstanceID, err error) { + combinedError = errors.CombineErrors(combinedError, err) + }, + ); err != nil { + return nil, err + } + + return resp, nil +} diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index 038cfee95415..6dcf367eb03e 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -262,6 +262,10 @@ type Server struct { // sqlStatsController is the control-plane interface for sqlStats. sqlStatsController *persistedsqlstats.Controller + // indexUsageStatsController is the control-plane interface for + // indexUsageStats. + indexUsageStatsController *idxusage.Controller + // reportedStats is a pool of stats that is held for reporting, and is // cleared on a lower interval than sqlStats. Stats from sqlStats flow // into reported stats when sqlStats is cleared. @@ -377,6 +381,7 @@ func NewServer(cfg *ExecutorConfig, pool *mon.BytesMonitor) *Server { s.sqlStats = persistedSQLStats s.sqlStatsController = persistedSQLStats.GetController(cfg.SQLStatusServer) + s.indexUsageStatsController = idxusage.NewController(cfg.SQLStatusServer) return s } @@ -2435,30 +2440,31 @@ func (ex *connExecutor) initEvalCtx(ctx context.Context, evalCtx *extendedEvalCo *evalCtx = extendedEvalContext{ EvalContext: tree.EvalContext{ - Planner: p, - PrivilegedAccessor: p, - SessionAccessor: p, - ClientNoticeSender: p, - Sequence: p, - Tenant: p, - Regions: p, - JoinTokenCreator: p, - PreparedStatementState: &ex.extraTxnState.prepStmtsNamespace, - SessionDataStack: ex.sessionDataStack, - Settings: ex.server.cfg.Settings, - TestingKnobs: ex.server.cfg.EvalContextTestingKnobs, - ClusterID: ex.server.cfg.ClusterID(), - ClusterName: ex.server.cfg.RPCContext.ClusterName(), - NodeID: ex.server.cfg.NodeID, - Codec: ex.server.cfg.Codec, - Locality: ex.server.cfg.Locality, - Tracer: ex.server.cfg.AmbientCtx.Tracer, - ReCache: ex.server.reCache, - InternalExecutor: &ie, - DB: ex.server.cfg.DB, - SQLLivenessReader: ex.server.cfg.SQLLiveness, - SQLStatsController: ex.server.sqlStatsController, - CompactEngineSpan: ex.server.cfg.CompactEngineSpanFunc, + Planner: p, + PrivilegedAccessor: p, + SessionAccessor: p, + ClientNoticeSender: p, + Sequence: p, + Tenant: p, + Regions: p, + JoinTokenCreator: p, + PreparedStatementState: &ex.extraTxnState.prepStmtsNamespace, + SessionDataStack: ex.sessionDataStack, + Settings: ex.server.cfg.Settings, + TestingKnobs: ex.server.cfg.EvalContextTestingKnobs, + ClusterID: ex.server.cfg.ClusterID(), + ClusterName: ex.server.cfg.RPCContext.ClusterName(), + NodeID: ex.server.cfg.NodeID, + Codec: ex.server.cfg.Codec, + Locality: ex.server.cfg.Locality, + Tracer: ex.server.cfg.AmbientCtx.Tracer, + ReCache: ex.server.reCache, + InternalExecutor: &ie, + DB: ex.server.cfg.DB, + SQLLivenessReader: ex.server.cfg.SQLLiveness, + SQLStatsController: ex.server.sqlStatsController, + IndexUsageStatsController: ex.server.indexUsageStatsController, + CompactEngineSpan: ex.server.cfg.CompactEngineSpanFunc, }, VirtualSchemas: ex.server.cfg.VirtualSchemas, Tracing: &ex.sessionTracing, diff --git a/pkg/sql/distsql/server.go b/pkg/sql/distsql/server.go index cdfa98c3f984..8bfea13b3681 100644 --- a/pkg/sql/distsql/server.go +++ b/pkg/sql/distsql/server.go @@ -352,18 +352,19 @@ func (ds *ServerImpl) setupFlow( Tracer: ds.ServerConfig.Tracer, // Most processors will override this Context with their own context in // ProcessorBase. StartInternal(). - Context: ctx, - Planner: &faketreeeval.DummyEvalPlanner{}, - PrivilegedAccessor: &faketreeeval.DummyPrivilegedAccessor{}, - SessionAccessor: &faketreeeval.DummySessionAccessor{}, - ClientNoticeSender: &faketreeeval.DummyClientNoticeSender{}, - Sequence: &faketreeeval.DummySequenceOperators{}, - Tenant: &faketreeeval.DummyTenantOperator{}, - Regions: &faketreeeval.DummyRegionOperator{}, - InternalExecutor: ie, - Txn: leafTxn, - SQLLivenessReader: ds.ServerConfig.SQLLivenessReader, - SQLStatsController: ds.ServerConfig.SQLStatsController, + Context: ctx, + Planner: &faketreeeval.DummyEvalPlanner{}, + PrivilegedAccessor: &faketreeeval.DummyPrivilegedAccessor{}, + SessionAccessor: &faketreeeval.DummySessionAccessor{}, + ClientNoticeSender: &faketreeeval.DummyClientNoticeSender{}, + Sequence: &faketreeeval.DummySequenceOperators{}, + Tenant: &faketreeeval.DummyTenantOperator{}, + Regions: &faketreeeval.DummyRegionOperator{}, + InternalExecutor: ie, + Txn: leafTxn, + SQLLivenessReader: ds.ServerConfig.SQLLivenessReader, + SQLStatsController: ds.ServerConfig.SQLStatsController, + IndexUsageStatsController: ds.ServerConfig.IndexUsageStatsController, } evalCtx.SetStmtTimestamp(timeutil.Unix(0 /* sec */, req.EvalContext.StmtTimestampNanos)) evalCtx.SetTxnTimestamp(timeutil.Unix(0 /* sec */, req.EvalContext.TxnTimestampNanos)) diff --git a/pkg/sql/execinfra/server_config.go b/pkg/sql/execinfra/server_config.go index 86ef7b5e7481..75feb6fc9503 100644 --- a/pkg/sql/execinfra/server_config.go +++ b/pkg/sql/execinfra/server_config.go @@ -151,6 +151,10 @@ type ServerConfig struct { // introduce dependency on the sql package. SQLStatsController tree.SQLStatsController + // IndexUsageStatsController is an interface used to reset index usage stats without + // the need to introduce dependency on the sql package. + IndexUsageStatsController tree.IndexUsageStatsController + // SQLSQLResponseAdmissionQ is the admission queue to use for // SQLSQLResponseWork. SQLSQLResponseAdmissionQ *admission.WorkQueue diff --git a/pkg/sql/idxusage/BUILD.bazel b/pkg/sql/idxusage/BUILD.bazel index 23b5cfec295d..320560d2e43f 100644 --- a/pkg/sql/idxusage/BUILD.bazel +++ b/pkg/sql/idxusage/BUILD.bazel @@ -19,6 +19,7 @@ go_library( name = "idxusage", srcs = [ "cluster_settings.go", + "index_usage_stats_controller.go", "local_idx_usage_stats.go", "test_utils.go", ], @@ -27,6 +28,7 @@ go_library( deps = [ "//pkg/base", "//pkg/roachpb:with-mocks", + "//pkg/server/serverpb", "//pkg/settings", "//pkg/settings/cluster", "//pkg/util/log", diff --git a/pkg/sql/idxusage/index_usage_stats_controller.go b/pkg/sql/idxusage/index_usage_stats_controller.go new file mode 100644 index 000000000000..9d4556d07e2c --- /dev/null +++ b/pkg/sql/idxusage/index_usage_stats_controller.go @@ -0,0 +1,42 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package idxusage + +import ( + "context" + + "github.com/cockroachdb/cockroach/pkg/server/serverpb" +) + +// Controller implements the index usage stats subsystem control plane. This exposes +// administrative interfaces that can be consumed by other parts of the database +// (e.g. status server, builtins) to control the behavior of index usage stas +// subsystem. +type Controller struct { + statusServer serverpb.SQLStatusServer +} + +// NewController returns a new instance of idxusage.Controller. +func NewController(status serverpb.SQLStatusServer) *Controller { + return &Controller{ + statusServer: status, + } +} + +// ResetIndexUsageStats implements the tree.IndexUsageStatsController interface. +func (s *Controller) ResetIndexUsageStats(ctx context.Context) error { + req := &serverpb.ResetIndexUsageStatsRequest{} + _, err := s.statusServer.ResetIndexUsageStats(ctx, req) + if err != nil { + return err + } + return nil +} diff --git a/pkg/sql/idxusage/local_idx_usage_stats.go b/pkg/sql/idxusage/local_idx_usage_stats.go index ad3d2e438702..16a27ea30312 100644 --- a/pkg/sql/idxusage/local_idx_usage_stats.go +++ b/pkg/sql/idxusage/local_idx_usage_stats.go @@ -14,6 +14,7 @@ import ( "context" "math" "sort" + "time" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" @@ -66,6 +67,9 @@ type LocalIndexUsageStats struct { // usageStats stores index usage statistics per unique roachpb.TableID. usageStats map[roachpb.TableID]*tableIndexStats + + // lastReset is the last time the index usage statistics were reset. + lastReset time.Time } // testingKnobs provide utilities for tests to hook into the internal states @@ -268,6 +272,13 @@ func (s *LocalIndexUsageStats) clear() { for _, tableStats := range s.mu.usageStats { tableStats.clear() } + s.mu.lastReset = timeutil.Now() +} + +// Reset resets read info for index usage metrics, although leaves the +// table and index mappings in place. +func (s *LocalIndexUsageStats) Reset() { + s.clear() } func (s *LocalIndexUsageStats) insertIndexUsage(idxUse *indexUse) { @@ -351,6 +362,13 @@ func (t *tableIndexStats) getStatsForIndexID( return nil } +// GetLastReset returns the last time the table was reset. +func (s *LocalIndexUsageStats) GetLastReset() time.Time { + s.mu.Lock() + defer s.mu.Unlock() + return s.mu.lastReset +} + func (t *tableIndexStats) iterateIndexStats( orderedIndexID bool, iterLimit uint64, visitor StatsVisitor, ) (newIterLimit uint64, err error) { diff --git a/pkg/sql/planner.go b/pkg/sql/planner.go index b0915a92b492..0278b198e646 100644 --- a/pkg/sql/planner.go +++ b/pkg/sql/planner.go @@ -417,10 +417,12 @@ func internalExtendedEvalCtx( var indexUsageStats *idxusage.LocalIndexUsageStats var sqlStatsController tree.SQLStatsController + var indexUsageStatsController tree.IndexUsageStatsController if execCfg.InternalExecutor != nil { if execCfg.InternalExecutor.s != nil { indexUsageStats = execCfg.InternalExecutor.s.indexUsageStats sqlStatsController = execCfg.InternalExecutor.s.sqlStatsController + indexUsageStatsController = execCfg.InternalExecutor.s.indexUsageStatsController } else { // If the indexUsageStats is nil from the sql.Server, we create a dummy // index usage stats collector. The sql.Server in the ExecutorConfig @@ -429,24 +431,26 @@ func internalExtendedEvalCtx( Setting: execCfg.Settings, }) sqlStatsController = &persistedsqlstats.Controller{} + indexUsageStatsController = &idxusage.Controller{} } } return extendedEvalContext{ EvalContext: tree.EvalContext{ - Txn: txn, - SessionDataStack: sds, - TxnReadOnly: false, - TxnImplicit: true, - Settings: execCfg.Settings, - Codec: execCfg.Codec, - Tracer: execCfg.AmbientCtx.Tracer, - Context: ctx, - Mon: plannerMon, - TestingKnobs: evalContextTestingKnobs, - StmtTimestamp: stmtTimestamp, - TxnTimestamp: txnTimestamp, - InternalExecutor: execCfg.InternalExecutor, - SQLStatsController: sqlStatsController, + Txn: txn, + SessionDataStack: sds, + TxnReadOnly: false, + TxnImplicit: true, + Settings: execCfg.Settings, + Codec: execCfg.Codec, + Tracer: execCfg.AmbientCtx.Tracer, + Context: ctx, + Mon: plannerMon, + TestingKnobs: evalContextTestingKnobs, + StmtTimestamp: stmtTimestamp, + TxnTimestamp: txnTimestamp, + InternalExecutor: execCfg.InternalExecutor, + SQLStatsController: sqlStatsController, + IndexUsageStatsController: indexUsageStatsController, }, VirtualSchemas: execCfg.VirtualSchemas, Tracing: &SessionTracing{}, diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index 620952eab823..a3b9fd24c2c7 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -6058,7 +6058,27 @@ table's zone configuration this will return NULL.`, tree.VolatilityStable, ), ), - + "crdb_internal.reset_index_usage_stats": makeBuiltin( + tree.FunctionProperties{ + Category: categorySystemInfo, + }, + tree.Overload{ + Types: tree.ArgTypes{}, + ReturnType: tree.FixedReturnType(types.Bool), + Fn: func(evalCtx *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + if evalCtx.IndexUsageStatsController == nil { + return nil, errors.AssertionFailedf("index usage stats controller not set") + } + ctx := evalCtx.Ctx() + if err := evalCtx.IndexUsageStatsController.ResetIndexUsageStats(ctx); err != nil { + return nil, err + } + return tree.MakeDBool(true), nil + }, + Info: `This function is used to clear the collected index usage statistics.`, + Volatility: tree.VolatilityVolatile, + }, + ), "crdb_internal.reset_sql_stats": makeBuiltin( tree.FunctionProperties{ Category: categorySystemInfo, diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index 38c789a3074e..806d9f63716c 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -3446,6 +3446,13 @@ type SQLStatsController interface { CreateSQLStatsCompactionSchedule(ctx context.Context) error } +// IndexUsageStatsController is an interface embedded in EvalCtx which can be +// used by the builtins to reset index usage stats in the cluster. This interface +// is introduced to avoid circular dependency. +type IndexUsageStatsController interface { + ResetIndexUsageStats(ctx context.Context) error +} + // EvalContext defines the context in which to evaluate an expression, allowing // the retrieval of state such as the node ID or statement start time. // @@ -3590,6 +3597,8 @@ type EvalContext struct { SQLStatsController SQLStatsController + IndexUsageStatsController IndexUsageStatsController + // CompactEngineSpan is used to force compaction of a span in a store. CompactEngineSpan CompactEngineSpanFunc } From 9b93b72adbb7051fca9b5bf9c36692c419b8e1ba Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Tue, 26 Oct 2021 15:25:44 -0400 Subject: [PATCH 117/205] pgwire: add a test for server aborting on repeated errors This adds a test for a recent commit that changed the pgwire loop to give up if it saw an error repeatedly. I added a testing knob to inject an error, and did a small refactor so that errors are handled consistently. Previously, some error handling would only happen for "MessageTooBig" errors. Release note: None --- pkg/sql/exec_util.go | 4 ++ pkg/sql/pgwire/conn.go | 101 +++++++++++++++++++++--------------- pkg/sql/pgwire/conn_test.go | 52 +++++++++++++++++++ 3 files changed, 114 insertions(+), 43 deletions(-) diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index b549d597111c..8ddfaa26948d 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -1314,6 +1314,10 @@ type PGWireTestingKnobs struct { // AuthHook is used to override the normal authentication handling on new // connections. AuthHook func(context.Context) error + + // AfterReadMsgTestingKnob is called after reading a message from the + // pgwire read buffer. + AfterReadMsgTestingKnob func(context.Context) error } var _ base.ModuleTestingKnobs = &PGWireTestingKnobs{} diff --git a/pkg/sql/pgwire/conn.go b/pkg/sql/pgwire/conn.go index c4600b690417..f8f2eeafb41b 100644 --- a/pkg/sql/pgwire/conn.go +++ b/pkg/sql/pgwire/conn.go @@ -101,6 +101,9 @@ type conn struct { // alwaysLogAuthActivity is used force-enables logging of authn events. alwaysLogAuthActivity bool + + // afterReadMsgTestingKnob is called after reading every message. + afterReadMsgTestingKnob func(context.Context) error } // serveConn creates a conn that will serve the netConn. It returns once the @@ -155,6 +158,9 @@ func (s *Server) serveConn( c := newConn(netConn, sArgs, &s.metrics, connStart, &s.execCfg.Settings.SV) c.alwaysLogAuthActivity = alwaysLogAuthActivity || atomic.LoadInt32(&s.testingAuthLogEnabled) > 0 + if s.execCfg.PGWireTestingKnobs != nil { + c.afterReadMsgTestingKnob = s.execCfg.PGWireTestingKnobs.AfterReadMsgTestingKnob + } // Do the reading of commands from the network. c.serveImpl(ctx, s.IsDraining, s.SQLServer, reserved, authOpt) @@ -208,6 +214,11 @@ func (c *conn) authLogEnabled() bool { return c.alwaysLogAuthActivity || logSessionAuth.Get(c.sv) } +// maxRepeatedErrorCount is the number of times an error can be received +// while reading from the network connection before the server decides to give +// up and abort the connection. +const maxRepeatedErrorCount = 1 << 15 + // serveImpl continuously reads from the network connection and pushes execution // instructions into a sql.StmtBuf, from where they'll be processed by a command // "processor" goroutine (a connExecutor). @@ -344,9 +355,13 @@ func (c *conn) serveImpl( var authDone, ignoreUntilSync bool var repeatedErrorCount int for { - breakLoop, err := func() (bool, error) { + breakLoop, isSimpleQuery, err := func() (bool, bool, error) { typ, n, err := c.readBuf.ReadTypedMsg(&c.rd) c.metrics.BytesInCount.Inc(int64(n)) + if err == nil && c.afterReadMsgTestingKnob != nil { + err = c.afterReadMsgTestingKnob(ctx) + } + isSimpleQuery := typ == pgwirebase.ClientMsgSimpleQuery if err != nil { if pgwirebase.IsMessageTooBigError(err) { log.VInfof(ctx, 1, "pgwire: found big error message; attempting to slurp bytes and return error: %s", err) @@ -355,33 +370,30 @@ func (c *conn) serveImpl( slurpN, slurpErr := c.readBuf.SlurpBytes(&c.rd, pgwirebase.GetMessageTooBigSize(err)) c.metrics.BytesInCount.Inc(int64(slurpN)) if slurpErr != nil { - return false, errors.Wrap(slurpErr, "pgwire: error slurping remaining bytes") + return false, isSimpleQuery, errors.Wrap(slurpErr, "pgwire: error slurping remaining bytes") } + } - // Write out the error over pgwire. - if err := c.stmtBuf.Push(ctx, sql.SendError{Err: err}); err != nil { - return false, errors.New("pgwire: error writing too big error message to the client") - } + // Write out the error over pgwire. + if err := c.stmtBuf.Push(ctx, sql.SendError{Err: err}); err != nil { + return false, isSimpleQuery, errors.New("pgwire: error writing too big error message to the client") + } - // If this is a simple query, we have to send the sync message back as - // well. Otherwise, we ignore all messages until receiving a sync. - if typ == pgwirebase.ClientMsgSimpleQuery { - if err = c.stmtBuf.Push(ctx, sql.Sync{}); err != nil { - return false, errors.New("pgwire: error writing sync to the client whilst message is too big") - } - } else { - ignoreUntilSync = true + // If this is a simple query, we have to send the sync message back as + // well. + if isSimpleQuery { + if err := c.stmtBuf.Push(ctx, sql.Sync{}); err != nil { + return false, isSimpleQuery, errors.New("pgwire: error writing sync to the client whilst message is too big") } - - // We need to continue processing here for pgwire clients to be able to - // successfully read the error message off pgwire. - // - // If break here, we terminate the connection. The client will instead see that - // we terminated the connection prematurely (as opposed to seeing a ClientMsgTerminate - // packet) and instead return a broken pipe or io.EOF error message. - return false, nil } - return false, errors.Wrap(err, "pgwire: error reading input") + + // We need to continue processing here for pgwire clients to be able to + // successfully read the error message off pgwire. + // + // If break here, we terminate the connection. The client will instead see that + // we terminated the connection prematurely (as opposed to seeing a ClientMsgTerminate + // packet) and instead return a broken pipe or io.EOF error message. + return false, isSimpleQuery, errors.Wrap(err, "pgwire: error reading input") } timeReceived := timeutil.Now() log.VEventf(ctx, 2, "pgwire: processing %s", typ) @@ -389,7 +401,7 @@ func (c *conn) serveImpl( if ignoreUntilSync { if typ != pgwirebase.ClientMsgSync { log.VInfof(ctx, 1, "pgwire: skipping non-sync message after encountering error") - return false, nil + return false, isSimpleQuery, nil } ignoreUntilSync = false } @@ -398,20 +410,20 @@ func (c *conn) serveImpl( if typ == pgwirebase.ClientMsgPassword { var pwd []byte if pwd, err = c.readBuf.GetBytes(n - 4); err != nil { - return false, err + return false, isSimpleQuery, err } // Pass the data to the authenticator. This hopefully causes it to finish // authentication in the background and give us an intSizer when we loop // around. if err = authenticator.sendPwdData(pwd); err != nil { - return false, err + return false, isSimpleQuery, err } - return false, nil + return false, isSimpleQuery, nil } // Wait for the auth result. if err = authenticator.authResult(); err != nil { // The error has already been sent to the client. - return true, nil //nolint:returnerrcheck + return true, isSimpleQuery, nil //nolint:returnerrcheck } authDone = true @@ -425,35 +437,35 @@ func (c *conn) serveImpl( case pgwirebase.ClientMsgPassword: // This messages are only acceptable during the auth phase, handled above. err = pgwirebase.NewProtocolViolationErrorf("unexpected authentication data") - return true, writeErr( + return true, isSimpleQuery, writeErr( ctx, &sqlServer.GetExecutorConfig().Settings.SV, err, &c.msgBuilder, &c.writerState.buf) case pgwirebase.ClientMsgSimpleQuery: if err = c.handleSimpleQuery( ctx, &c.readBuf, timeReceived, parser.NakedIntTypeFromDefaultIntSize(atomic.LoadInt32(atomicUnqualifiedIntSize)), ); err != nil { - return false, err + return false, isSimpleQuery, err } - return false, c.stmtBuf.Push(ctx, sql.Sync{}) + return false, isSimpleQuery, c.stmtBuf.Push(ctx, sql.Sync{}) case pgwirebase.ClientMsgExecute: - return false, c.handleExecute(ctx, &c.readBuf, timeReceived) + return false, isSimpleQuery, c.handleExecute(ctx, &c.readBuf, timeReceived) case pgwirebase.ClientMsgParse: - return false, c.handleParse(ctx, &c.readBuf, parser.NakedIntTypeFromDefaultIntSize(atomic.LoadInt32(atomicUnqualifiedIntSize))) + return false, isSimpleQuery, c.handleParse(ctx, &c.readBuf, parser.NakedIntTypeFromDefaultIntSize(atomic.LoadInt32(atomicUnqualifiedIntSize))) case pgwirebase.ClientMsgDescribe: - return false, c.handleDescribe(ctx, &c.readBuf) + return false, isSimpleQuery, c.handleDescribe(ctx, &c.readBuf) case pgwirebase.ClientMsgBind: - return false, c.handleBind(ctx, &c.readBuf) + return false, isSimpleQuery, c.handleBind(ctx, &c.readBuf) case pgwirebase.ClientMsgClose: - return false, c.handleClose(ctx, &c.readBuf) + return false, isSimpleQuery, c.handleClose(ctx, &c.readBuf) case pgwirebase.ClientMsgTerminate: terminateSeen = true - return true, nil + return true, isSimpleQuery, nil case pgwirebase.ClientMsgSync: // We're starting a batch here. If the client continues using the extended @@ -461,10 +473,10 @@ func (c *conn) serveImpl( // message has to be skipped. See: // https://www.postgresql.org/docs/current/10/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY - return false, c.stmtBuf.Push(ctx, sql.Sync{}) + return false, isSimpleQuery, c.stmtBuf.Push(ctx, sql.Sync{}) case pgwirebase.ClientMsgFlush: - return false, c.handleFlush(ctx) + return false, isSimpleQuery, c.handleFlush(ctx) case pgwirebase.ClientMsgCopyData, pgwirebase.ClientMsgCopyDone, pgwirebase.ClientMsgCopyFail: // We're supposed to ignore these messages, per the protocol spec. This @@ -472,18 +484,21 @@ func (c *conn) serveImpl( // operation: the server will send an error and a ready message back to // the client, and must then ignore further copy messages. See: // https://github.com/postgres/postgres/blob/6e1dd2773eb60a6ab87b27b8d9391b756e904ac3/src/backend/tcop/postgres.c#L4295 - return false, nil + return false, isSimpleQuery, nil default: - return false, c.stmtBuf.Push( + return false, isSimpleQuery, c.stmtBuf.Push( ctx, sql.SendError{Err: pgwirebase.NewUnrecognizedMsgTypeErr(typ)}) } }() if err != nil { log.VEventf(ctx, 1, "pgwire: error processing message: %s", err) - ignoreUntilSync = true + if !isSimpleQuery { + // In the extended protocol, after seeing an error, we ignore all + // messages until receiving a sync. + ignoreUntilSync = true + } repeatedErrorCount++ - const maxRepeatedErrorCount = 1 << 15 // If we can't read data because of any one of the following conditions, // then we should break: // 1. the connection was closed. diff --git a/pkg/sql/pgwire/conn_test.go b/pkg/sql/pgwire/conn_test.go index 262ee148311e..2cc4d65508e9 100644 --- a/pkg/sql/pgwire/conn_test.go +++ b/pkg/sql/pgwire/conn_test.go @@ -14,6 +14,7 @@ import ( "bytes" "context" gosql "database/sql" + "database/sql/driver" "fmt" "io" "io/ioutil" @@ -22,6 +23,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -1274,6 +1276,56 @@ func TestConnCloseCancelsAuth(t *testing.T) { <-authBlocked } +// TestConnServerAbortsOnRepeatedErrors checks that if the server keeps seeing +// a non-connection-closed error repeatedly, then it eventually the server +// aborts the connection. +func TestConnServerAbortsOnRepeatedErrors(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + var shouldError uint32 = 0 + testingKnobError := fmt.Errorf("a random error") + s, db, _ := serverutils.StartServer(t, + base.TestServerArgs{ + Insecure: true, + Knobs: base.TestingKnobs{ + PGWireTestingKnobs: &sql.PGWireTestingKnobs{ + AfterReadMsgTestingKnob: func(ctx context.Context) error { + if atomic.LoadUint32(&shouldError) == 0 { + return nil + } + return testingKnobError + }, + }, + }, + }) + ctx := context.Background() + defer s.Stopper().Stop(ctx) + defer db.Close() + + conn, err := db.Conn(ctx) + require.NoError(t, err) + + atomic.StoreUint32(&shouldError, 1) + for i := 0; i < maxRepeatedErrorCount+100; i++ { + var s int + err := conn.QueryRowContext(ctx, "SELECT 1").Scan(&s) + if err != nil { + if strings.Contains(err.Error(), testingKnobError.Error()) { + continue + } + if errors.Is(err, driver.ErrBadConn) { + // The server closed the connection, which is what we want! + require.GreaterOrEqualf(t, i, maxRepeatedErrorCount, + "the server should have aborted after seeing %d errors", + maxRepeatedErrorCount, + ) + return + } + } + } + require.FailNow(t, "should have seen ErrBadConn before getting here") +} + func TestParseClientProvidedSessionParameters(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) From 0e1d1f01bfefc9270ac11b9e773857510e9f3933 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Thu, 28 Oct 2021 14:10:45 -0700 Subject: [PATCH 118/205] storage/metamorphic: Don't have two txns write to a key Previously, we were letting two txns concurrently write to a key as long as they were not using the same writer. At first this seemed to be deterministic enough for our purposes despite violating MVCC invariants, but turns out `intentInterleavingIter` makes some optimizations where it assumes there are never two intents for the same key, so we need to mimic KV-style latching here. Fixes #72077, #72078. Release note: None. --- pkg/storage/metamorphic/generator.go | 2 +- pkg/storage/metamorphic/meta_test.go | 2 - pkg/storage/metamorphic/operands.go | 79 +++++++++++++++------------- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/pkg/storage/metamorphic/generator.go b/pkg/storage/metamorphic/generator.go index 1e254c26827c..64245bc08057 100644 --- a/pkg/storage/metamorphic/generator.go +++ b/pkg/storage/metamorphic/generator.go @@ -136,7 +136,7 @@ func (m *metaTestRunner) init() { tsGenerator: &m.tsGenerator, txnIDMap: make(map[txnID]*roachpb.Transaction), openBatches: make(map[txnID]map[readWriterID]struct{}), - inUseKeys: make(map[txnID][]writtenKeySpan), + inUseKeys: make([]writtenKeySpan, 0), openSavepoints: make(map[txnID]int), testRunner: m, } diff --git a/pkg/storage/metamorphic/meta_test.go b/pkg/storage/metamorphic/meta_test.go index d73b78e94784..cc1fdbf339d3 100644 --- a/pkg/storage/metamorphic/meta_test.go +++ b/pkg/storage/metamorphic/meta_test.go @@ -162,7 +162,6 @@ func runMetaTest(run testRun) { // for matching outputs by the test suite between different options of Pebble. func TestPebbleEquivalence(t *testing.T) { defer leaktest.AfterTest(t)() - skip.WithIssue(t, 72077, "flaky test") defer log.Scope(t).Close(t) ctx := context.Background() @@ -210,7 +209,6 @@ func TestPebbleEquivalence(t *testing.T) { // engine sequences with restarts in between. func TestPebbleRestarts(t *testing.T) { defer leaktest.AfterTest(t)() - skip.WithIssue(t, 72078, "flaky test") defer log.Scope(t).Close(t) // This test times out with the race detector enabled. skip.UnderRace(t) diff --git a/pkg/storage/metamorphic/operands.go b/pkg/storage/metamorphic/operands.go index fc77b623cb37..05a0fe8e7ca2 100644 --- a/pkg/storage/metamorphic/operands.go +++ b/pkg/storage/metamorphic/operands.go @@ -161,10 +161,8 @@ func (k *txnKeyGenerator) opener() string { } func (k *txnKeyGenerator) count(args []string) int { - // Always return a nonzero value so opener() is never called directly. - txn := txnID(args[1]) - openKeys := k.txns.inUseKeys[txn] - return len(openKeys) + 1 + // Always return a non-zero count so opener() is never called. + return len(k.txns.inUseKeys) + 1 } func (k *txnKeyGenerator) get(args []string) string { @@ -237,6 +235,7 @@ type txnID string type writtenKeySpan struct { key roachpb.Span writer readWriterID + txn txnID } type txnGenerator struct { @@ -251,9 +250,9 @@ type txnGenerator struct { // Number of open savepoints for each transaction. As savepoints cannot be // "closed", this count can only go up. openSavepoints map[txnID]int - // Stores keys written to by each transaction during operation generation. - // The slices are sorted in key order. - inUseKeys map[txnID][]writtenKeySpan + // Stores keys written to by each writer/txn during operation generation. + // Sorted in key order, with no overlaps. + inUseKeys []writtenKeySpan // Counts "generated" transactions - i.e. how many txn_open()s have been // inserted so far. Could stay 0 in check mode. txnGenCounter uint64 @@ -295,7 +294,17 @@ func (t *txnGenerator) generateClose(id txnID) { delete(t.openBatches, id) delete(t.txnIDMap, id) delete(t.openSavepoints, id) - delete(t.inUseKeys, id) + + // Delete all keys in inUseKeys where inUseKeys[i].txn == id. j points to the + // next slot in t.inUseKeys where a key span being retained will go. + j := 0 + for i := range t.inUseKeys { + if t.inUseKeys[i].txn != id { + t.inUseKeys[j] = t.inUseKeys[i] + j++ + } + } + t.inUseKeys = t.inUseKeys[:j] for i := range t.liveTxns { if t.liveTxns[i] == id { @@ -319,18 +328,17 @@ func (t *txnGenerator) forEachConflict( endKey = key.Next() } - openKeys := t.inUseKeys[txn] - start := sort.Search(len(openKeys), func(i int) bool { - return key.Compare(openKeys[i].key.EndKey) < 0 + start := sort.Search(len(t.inUseKeys), func(i int) bool { + return key.Compare(t.inUseKeys[i].key.EndKey) < 0 }) - end := sort.Search(len(openKeys), func(i int) bool { - return endKey.Compare(openKeys[i].key.Key) <= 0 + end := sort.Search(len(t.inUseKeys), func(i int) bool { + return endKey.Compare(t.inUseKeys[i].key.Key) <= 0 }) for i := start; i < end; i++ { - if openKeys[i].writer != w { + if t.inUseKeys[i].writer != w || t.inUseKeys[i].txn != txn { // Conflict found. - if cont := fn(openKeys[i].key); !cont { + if cont := fn(t.inUseKeys[i].key); !cont { return } } @@ -348,36 +356,35 @@ func (t *txnGenerator) addWrittenKeySpan( // writtenKeys is sorted in key order, and no two spans are overlapping. // However we do _not_ merge perfectly-adjacent spans when adding; // as it is legal to have [a,b) and [b,c) belonging to two different writers. - writtenKeys := t.inUseKeys[txn] // start is the earliest span that either contains key, or if there is no such // span, is the first span beyond key. end is the earliest span that does not // include any keys in [key, endKey). - start := sort.Search(len(writtenKeys), func(i int) bool { - return key.Compare(writtenKeys[i].key.EndKey) < 0 + start := sort.Search(len(t.inUseKeys), func(i int) bool { + return key.Compare(t.inUseKeys[i].key.EndKey) < 0 }) - end := sort.Search(len(writtenKeys), func(i int) bool { - return endKey.Compare(writtenKeys[i].key.Key) <= 0 + end := sort.Search(len(t.inUseKeys), func(i int) bool { + return endKey.Compare(t.inUseKeys[i].key.Key) <= 0 }) - if start == len(writtenKeys) { + if start == len(t.inUseKeys) { // Append at end. - writtenKeys = append(writtenKeys, writtenKeySpan{ + t.inUseKeys = append(t.inUseKeys, writtenKeySpan{ key: span, writer: w, + txn: txn, }) - t.inUseKeys[txn] = writtenKeys return } if start == end { // start == end implies that the span cannot contain start, and by // definition it is beyond end. So [key, endKey) does not overlap with an // existing span and needs to be placed before start. - writtenKeys = append(writtenKeys, writtenKeySpan{}) - copy(writtenKeys[start+1:], writtenKeys[start:]) - writtenKeys[start] = writtenKeySpan{ + t.inUseKeys = append(t.inUseKeys, writtenKeySpan{}) + copy(t.inUseKeys[start+1:], t.inUseKeys[start:]) + t.inUseKeys[start] = writtenKeySpan{ key: span, writer: w, + txn: txn, } - t.inUseKeys[txn] = writtenKeys return } else if start > end { panic(fmt.Sprintf("written keys not in sorted order: %d > %d", start, end)) @@ -386,31 +393,31 @@ func (t *txnGenerator) addWrittenKeySpan( // know end > 0. We will be merging existing spans, and need to compute which // spans to merge and what keys to use for the resulting span. The resulting // span will go into `span`, and will replace writtenKeys[start:end]. - if writtenKeys[start].key.Key.Compare(key) < 0 { + if t.inUseKeys[start].key.Key.Compare(key) < 0 { // The start span contains key. So use the start key of the start span since // it will result in the wider span. - span.Key = writtenKeys[start].key.Key + span.Key = t.inUseKeys[start].key.Key } // Else, span.Key is equal to key which is the wider span. Note that no span // overlaps with key so we are not extending this into the span at start-1. // // The span at end-1 may end at a key less or greater than endKey. - if writtenKeys[end-1].key.EndKey.Compare(endKey) > 0 { + if t.inUseKeys[end-1].key.EndKey.Compare(endKey) > 0 { // Existing span ends at a key greater than endKey, so construct the wider // span. - span.EndKey = writtenKeys[end-1].key.EndKey + span.EndKey = t.inUseKeys[end-1].key.EndKey } // We blindly replace the existing spans in writtenKeys[start:end] with the // new one. This is okay as long as any conflicting operation looks at // inUseKeys before generating itself; this method is only called once no // conflicts are found and the write operation is successfully generated. - writtenKeys[start] = writtenKeySpan{ + t.inUseKeys[start] = writtenKeySpan{ key: span, writer: w, + txn: txn, } - n := copy(writtenKeys[start+1:], writtenKeys[end:]) - writtenKeys = writtenKeys[:start+1+n] - t.inUseKeys[txn] = writtenKeys + n := copy(t.inUseKeys[start+1:], t.inUseKeys[end:]) + t.inUseKeys = t.inUseKeys[:start+1+n] } func (t *txnGenerator) trackTransactionalWrite(w readWriterID, txn txnID, key, endKey roachpb.Key) { @@ -430,7 +437,7 @@ func (t *txnGenerator) closeAll() { t.liveTxns = nil t.txnIDMap = make(map[txnID]*roachpb.Transaction) t.openBatches = make(map[txnID]map[readWriterID]struct{}) - t.inUseKeys = make(map[txnID][]writtenKeySpan) + t.inUseKeys = t.inUseKeys[:0] t.openSavepoints = make(map[txnID]int) } From dc148a4129f839260023107513eb43d0afb64272 Mon Sep 17 00:00:00 2001 From: Oleg Afanasyev Date: Wed, 18 Aug 2021 16:39:55 +0100 Subject: [PATCH 119/205] kvclient: when retrying transaction bump ts to now Previously, when transaction failed with retryable error its timestamp was bumped by whatever event caused it like a push or a closed ts. Upon retry it would read at closed timestamp and will most likely has to make a refresh again for its writes to succeed. If transaction uses fixed timestamp, needs non negligible time to retry or hits any contention it could get into infinite retry loop. This behaviour is more pronounced with a faster closed timestamp refresh that is in place since 21.1. To prevent such outcome, this patch changes restart timestamp to now. Release note: None --- pkg/kv/kvclient/kvcoord/BUILD.bazel | 1 + pkg/kv/kvclient/kvcoord/txn_test.go | 51 ++++++++++++++++++++++++++ pkg/roachpb/data.go | 25 +++++++++++-- pkg/sql/tests/monotonic_insert_test.go | 1 - 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/pkg/kv/kvclient/kvcoord/BUILD.bazel b/pkg/kv/kvclient/kvcoord/BUILD.bazel index d5ac193d94a2..5b426f55783c 100644 --- a/pkg/kv/kvclient/kvcoord/BUILD.bazel +++ b/pkg/kv/kvclient/kvcoord/BUILD.bazel @@ -148,6 +148,7 @@ go_test( "//pkg/kv/kvbase", "//pkg/kv/kvclient/rangecache:with-mocks", "//pkg/kv/kvserver", + "//pkg/kv/kvserver/closedts", "//pkg/kv/kvserver/concurrency/lock", "//pkg/kv/kvserver/kvserverbase", "//pkg/kv/kvserver/tscache", diff --git a/pkg/kv/kvclient/kvcoord/txn_test.go b/pkg/kv/kvclient/kvcoord/txn_test.go index 9a33a94302e8..317be88f73b8 100644 --- a/pkg/kv/kvclient/kvcoord/txn_test.go +++ b/pkg/kv/kvclient/kvcoord/txn_test.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv" "github.com/cockroachdb/cockroach/pkg/kv/kvserver" + "github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/tscache" "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -896,3 +897,53 @@ func TestTxnReturnsWriteTooOldErrorOnConflictingDeleteRange(t *testing.T) { require.Regexp(t, "TransactionRetryWithProtoRefreshError: WriteTooOldError", err) require.Len(t, b.Results[0].Keys, 0) } + +// TestRetrySerializableBumpsToNow verifies that transaction read time is forwarded to the +// current HLC time rather than closed timestamp to give it enough time to retry. +// To achieve that, test fixes transaction time to prevent refresh from succeeding first +// then waits for closed timestamp to advance sufficiently and commits. Since write +// can't succeed below closed timestamp and commit timestamp has leaked and can't be bumped +// txn is restarted. Test then verifies that it can proceed even if closed timestamp +// is again updated. +func TestRetrySerializableBumpsToNow(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + s := createTestDB(t) + defer s.Stop() + ctx := context.Background() + + bumpClosedTimestamp := func(delay time.Duration) { + s.Manual.Increment(delay.Nanoseconds()) + // We need to bump closed timestamp for clock increment to have effect + // on further kv writes. Putting anything into proposal buffer will + // trigger achieve this. + // We write a value to a non-overlapping key to ensure we are not + // bumping test transaction. We can't use reads because they could + // only bump tscache directly which we try not to do in this test case. + require.NoError(t, s.DB.Put(ctx, roachpb.Key("z"), []byte{0})) + } + + attempt := 0 + require.NoError(t, s.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { + require.Less(t, attempt, 2, "Transaction can't recover after initial retry error, too many retries") + closedTsTargetDuration := closedts.TargetDuration.Get(&s.Cfg.Settings.SV) + delay := closedTsTargetDuration / 2 + if attempt == 0 { + delay = closedTsTargetDuration * 2 + } + bumpClosedTimestamp(delay) + attempt++ + // Fixing transaction commit timestamp to disallow read refresh. + _ = txn.CommitTimestamp() + // Perform a scan to populate the transaction's read spans and mandate a refresh + // if the transaction's write timestamp is ever bumped. Because we fixed the + // transaction's commit timestamp, it will be forced to retry. + _, err := txn.Scan(ctx, roachpb.Key("a"), roachpb.Key("p"), 1000) + require.NoError(t, err, "Failed Scan request") + // Perform a write, which will run into the closed timestamp and get pushed. + require.NoError(t, txn.Put(ctx, roachpb.Key("b"), []byte(fmt.Sprintf("value-%d", attempt)))) + return nil + })) + require.Greater(t, attempt, 1, "Transaction is expected to retry once") +} diff --git a/pkg/roachpb/data.go b/pkg/roachpb/data.go index f1c4e060ab11..617533938d01 100644 --- a/pkg/roachpb/data.go +++ b/pkg/roachpb/data.go @@ -1457,9 +1457,28 @@ func PrepareTransactionForRetry( txn.WriteTimestamp.Forward(tErr.PusheeTxn.WriteTimestamp) txn.UpgradePriority(tErr.PusheeTxn.Priority - 1) case *TransactionRetryError: - // Nothing to do. Transaction.Timestamp has already been forwarded to be - // ahead of any timestamp cache entries or newer versions which caused - // the restart. + // Transaction.Timestamp has already been forwarded to be ahead of any + // timestamp cache entries or newer versions which caused the restart. + if tErr.Reason == RETRY_SERIALIZABLE { + // For RETRY_SERIALIZABLE case, we want to bump timestamp further than + // timestamp cache. + // This helps transactions that had their commit timestamp fixed (See + // roachpb.Transaction.CommitTimestampFixed for details on when it happens) + // or transactions that hit read-write contention and can't bump + // read timestamp because of later writes. + // Upon retry, we want those transactions to restart on now() instead of + // closed ts to give them some time to complete without a need to refresh + // read spans yet again and possibly fail. + // The tradeoff here is that transactions that failed because they were + // waiting on locks or were slowed down in their first epoch for any other + // reason (e.g. lease transfers, network congestion, node failure, etc.) + // would have a chance to retry and succeed, but transactions that are + // just slow would still retry indefinitely and delay transactions that + // try to write to the keys this transaction reads because reads are not + // in the past anymore. + now := clock.Now() + txn.WriteTimestamp.Forward(now) + } case *WriteTooOldError: // Increase the timestamp to the ts at which we've actually written. txn.WriteTimestamp.Forward(writeTooOldRetryTimestamp(tErr)) diff --git a/pkg/sql/tests/monotonic_insert_test.go b/pkg/sql/tests/monotonic_insert_test.go index 6a344e35b92c..7ee06c56c793 100644 --- a/pkg/sql/tests/monotonic_insert_test.go +++ b/pkg/sql/tests/monotonic_insert_test.go @@ -77,7 +77,6 @@ type mtClient struct { // https://github.com/jepsen-io/jepsen/blob/master/cockroachdb/src/jepsen/cockroach/monotonic.clj func TestMonotonicInserts(t *testing.T) { defer leaktest.AfterTest(t)() - skip.WithIssue(t, 67802, "flaky test") for _, distSQLMode := range []sessiondatapb.DistSQLExecMode{ sessiondatapb.DistSQLOff, sessiondatapb.DistSQLOn, From 072cb20cb7b9626c68a1fbf348233af4e1ea3354 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Fri, 29 Oct 2021 12:37:49 -0400 Subject: [PATCH 120/205] sql: populate indexprs column of pg_catalog.pg_index This commit also fixes the formatting of user-defined types in the `indexdef` column of `pg_catalog.pg_indexes` and the `indpred` column of `pg_catalog.pg_index`. Release note (bug fix): The `indexprs` column of `pg_catalog.pg_index` is now populated with string representations of every expression element in the index. If the index is not an expression index, `indexprs` is `NULL`. The `indexdef` column of `pg_catalog.pg_indexes` and the `indpred` column of `pg_catalog.pg_index` now correctly display user-defined types. --- .../logictest/testdata/logic_test/pg_catalog | 358 ++++++++++-------- pkg/sql/pg_catalog.go | 42 +- 2 files changed, 235 insertions(+), 165 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index b1ba416e62de..72fc223dc2c9 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -403,15 +403,20 @@ CREATE TABLE constraint_db.t5 ( CONSTRAINT fk_b_c FOREIGN KEY (b, c) REFERENCES constraint_db.t4(b, c) MATCH FULL ON UPDATE RESTRICT ) +statement ok +CREATE TYPE constraint_db.mytype AS ENUM ('foo', 'bar', 'baz') + statement ok CREATE TABLE constraint_db.t6 ( a INT, b INT, c STRING, + m constraint_db.mytype, INDEX ((a + b)) ); CREATE INDEX ON constraint_db.t6 (lower(c), (a + b)); CREATE UNIQUE INDEX ON constraint_db.t6 (lower(c)); +CREATE INDEX ON constraint_db.t6 ((m = 'foo')) WHERE m = 'foo' ## pg_catalog.pg_namespace @@ -550,13 +555,14 @@ oid relname relnamespace reltype reloftype relowner rel 4179599057 t4_pkey 2332901747 0 0 1546506610 2631952481 0 0 62 t5 2332901747 100062 0 1546506610 2631952481 0 0 3919862786 t5_pkey 2332901747 0 0 1546506610 2631952481 0 0 -63 t6 2332901747 100063 0 1546506610 2631952481 0 0 -2574785779 t6_pkey 2332901747 0 0 1546506610 2631952481 0 0 -2574785776 t6_expr_idx 2332901747 0 0 1546506610 2631952481 0 0 -2574785777 t6_expr_expr1_idx 2332901747 0 0 1546506610 2631952481 0 0 -2574785782 t6_expr_key 2332901747 0 0 1546506610 2631952481 0 0 -64 mv1 2332901747 100064 0 1546506610 0 0 0 -51576700 mv1_pkey 2332901747 0 0 1546506610 2631952481 0 0 +65 t6 2332901747 100065 0 1546506610 2631952481 0 0 +3001466989 t6_pkey 2332901747 0 0 1546506610 2631952481 0 0 +3001466990 t6_expr_idx 2332901747 0 0 1546506610 2631952481 0 0 +3001466991 t6_expr_expr1_idx 2332901747 0 0 1546506610 2631952481 0 0 +3001466984 t6_expr_key 2332901747 0 0 1546506610 2631952481 0 0 +3001466985 t6_expr_idx1 2332901747 0 0 1546506610 2631952481 0 0 +66 mv1 2332901747 100066 0 1546506610 0 0 0 +2741730718 mv1_pkey 2332901747 0 0 1546506610 2631952481 0 0 query TIRIOBBT colnames SELECT relname, relpages, reltuples, relallvisible, reltoastrelid, relhasindex, relisshared, relpersistence @@ -587,6 +593,7 @@ t6_pkey NULL NULL 0 0 false t6_expr_idx NULL NULL 0 0 false false p t6_expr_expr1_idx NULL NULL 0 0 false false p t6_expr_key NULL NULL 0 0 false false p +t6_expr_idx1 NULL NULL 0 0 false false p mv1 NULL NULL 0 0 true false p mv1_pkey NULL NULL 0 0 false false p @@ -614,11 +621,12 @@ t4 false r 4 0 false true t4_pkey false i 1 0 false false t5 false r 4 0 false true t5_pkey false i 1 0 false false -t6 false r 4 0 false true +t6 false r 5 0 false true t6_pkey false i 1 0 false false t6_expr_idx false i 1 0 false false t6_expr_expr1_idx false i 2 0 false false t6_expr_key false i 1 0 false false +t6_expr_idx1 false i 1 0 false false mv1 false m 2 0 false true mv1_pkey false i 1 0 false false @@ -651,6 +659,7 @@ t6_pkey false false false 0 NU t6_expr_idx false false false 0 NULL NULL t6_expr_expr1_idx false false false 0 NULL NULL t6_expr_key false false false 0 NULL NULL +t6_expr_idx1 false false false 0 NULL NULL mv1 false false false 0 NULL NULL mv1_pkey false false false 0 NULL NULL @@ -710,18 +719,20 @@ attrelid relname attname atttypid attstattarget 62 t5 c 25 0 -1 3 0 -1 62 t5 rowid 20 0 8 4 0 -1 3919862786 t5_pkey rowid 20 0 8 4 0 -1 -63 t6 a 20 0 8 1 0 -1 -63 t6 b 20 0 8 2 0 -1 -63 t6 c 25 0 -1 3 0 -1 -63 t6 rowid 20 0 8 5 0 -1 -2574785779 t6_pkey rowid 20 0 8 5 0 -1 -2574785776 t6_expr_idx crdb_internal_idx_expr 20 0 8 4 0 -1 -2574785777 t6_expr_expr1_idx crdb_internal_idx_expr_1 25 0 -1 6 0 -1 -2574785777 t6_expr_expr1_idx crdb_internal_idx_expr_2 20 0 8 7 0 -1 -2574785782 t6_expr_key crdb_internal_idx_expr_3 25 0 -1 8 0 -1 -64 mv1 ?column? 20 0 8 1 0 -1 -64 mv1 rowid 20 0 8 2 0 -1 -51576700 mv1_pkey rowid 20 0 8 2 0 -1 +65 t6 a 20 0 8 1 0 -1 +65 t6 b 20 0 8 2 0 -1 +65 t6 c 25 0 -1 3 0 -1 +65 t6 m 100063 0 -1 4 0 -1 +65 t6 rowid 20 0 8 6 0 -1 +3001466989 t6_pkey rowid 20 0 8 6 0 -1 +3001466990 t6_expr_idx crdb_internal_idx_expr 20 0 8 5 0 -1 +3001466991 t6_expr_expr1_idx crdb_internal_idx_expr_1 25 0 -1 7 0 -1 +3001466991 t6_expr_expr1_idx crdb_internal_idx_expr_2 20 0 8 8 0 -1 +3001466984 t6_expr_key crdb_internal_idx_expr_3 25 0 -1 9 0 -1 +3001466985 t6_expr_idx1 crdb_internal_idx_expr_4 16 0 1 10 0 -1 +66 mv1 ?column? 20 0 8 1 0 -1 +66 mv1 rowid 20 0 8 2 0 -1 +2741730718 mv1_pkey rowid 20 0 8 2 0 -1 query TTIBTTBBTT colnames SELECT c.relname, attname, atttypmod, attbyval, attstorage, attalign, attnotnull, atthasdef, attidentity, attgenerated @@ -780,12 +791,14 @@ t5_pkey rowid -1 NULL NULL NU t6 a -1 NULL NULL NULL false false · · t6 b -1 NULL NULL NULL false false · · t6 c -1 NULL NULL NULL false false · · +t6 m -1 NULL NULL NULL false false · · t6 rowid -1 NULL NULL NULL true true · · t6_pkey rowid -1 NULL NULL NULL true true · · t6_expr_idx crdb_internal_idx_expr -1 NULL NULL NULL false false · v t6_expr_expr1_idx crdb_internal_idx_expr_1 -1 NULL NULL NULL false false · v t6_expr_expr1_idx crdb_internal_idx_expr_2 -1 NULL NULL NULL false false · v t6_expr_key crdb_internal_idx_expr_3 -1 NULL NULL NULL false false · v +t6_expr_idx1 crdb_internal_idx_expr_4 -1 NULL NULL NULL false false · v mv1 ?column? -1 NULL NULL NULL false false · · mv1 rowid -1 NULL NULL NULL true true · · mv1_pkey rowid -1 NULL NULL NULL true true · · @@ -847,12 +860,14 @@ t5_pkey rowid false true 0 t6 a false true 0 NULL NULL NULL t6 b false true 0 NULL NULL NULL t6 c false true 0 NULL NULL NULL +t6 m false true 0 NULL NULL NULL t6 rowid false true 0 NULL NULL NULL t6_pkey rowid false true 0 NULL NULL NULL t6_expr_idx crdb_internal_idx_expr false true 0 NULL NULL NULL t6_expr_expr1_idx crdb_internal_idx_expr_1 false true 0 NULL NULL NULL t6_expr_expr1_idx crdb_internal_idx_expr_2 false true 0 NULL NULL NULL t6_expr_key crdb_internal_idx_expr_3 false true 0 NULL NULL NULL +t6_expr_idx1 crdb_internal_idx_expr_4 false true 0 NULL NULL NULL mv1 ?column? false true 0 NULL NULL NULL mv1 rowid false true 0 NULL NULL NULL mv1_pkey rowid false true 0 NULL NULL NULL @@ -952,8 +967,8 @@ oid relname adrelid adnum adbin adsrc 581442131 t3 59 4 unique_rowid() unique_rowid() unique_rowid() 1100914677 t4 61 4 unique_rowid() unique_rowid() unique_rowid() 2445991686 t5 62 4 unique_rowid() unique_rowid() unique_rowid() -3791068694 t6 63 5 unique_rowid() unique_rowid() unique_rowid() -2779881566 mv1 64 2 unique_rowid() unique_rowid() unique_rowid() +4124958571 t6 65 6 unique_rowid() unique_rowid() unique_rowid() +1175068284 mv1 66 2 unique_rowid() unique_rowid() unique_rowid() ## pg_catalog.pg_indexes @@ -974,11 +989,12 @@ crdb_oid schemaname tablename indexname tablespace 3660126516 public t3 t3_a_b_idx NULL 4179599057 public t4 t4_pkey NULL 3919862786 public t5 t5_pkey NULL -2574785779 public t6 t6_pkey NULL -2574785776 public t6 t6_expr_idx NULL -2574785777 public t6 t6_expr_expr1_idx NULL -2574785782 public t6 t6_expr_key NULL -51576700 public mv1 mv1_pkey NULL +3001466989 public t6 t6_pkey NULL +3001466990 public t6 t6_expr_idx NULL +3001466991 public t6 t6_expr_expr1_idx NULL +3001466984 public t6 t6_expr_key NULL +3001466985 public t6 t6_expr_idx1 NULL +2741730718 public mv1 mv1_pkey NULL query OTTT colnames SELECT crdb_oid, tablename, indexname, indexdef @@ -997,11 +1013,12 @@ crdb_oid tablename indexname indexdef 3660126516 t3 t3_a_b_idx CREATE INDEX t3_a_b_idx ON constraint_db.public.t3 USING btree (a ASC, b DESC) STORING (c) 4179599057 t4 t4_pkey CREATE UNIQUE INDEX t4_pkey ON constraint_db.public.t4 USING btree (rowid ASC) 3919862786 t5 t5_pkey CREATE UNIQUE INDEX t5_pkey ON constraint_db.public.t5 USING btree (rowid ASC) -2574785779 t6 t6_pkey CREATE UNIQUE INDEX t6_pkey ON constraint_db.public.t6 USING btree (rowid ASC) -2574785776 t6 t6_expr_idx CREATE INDEX t6_expr_idx ON constraint_db.public.t6 USING btree ((a + b) ASC) -2574785777 t6 t6_expr_expr1_idx CREATE INDEX t6_expr_expr1_idx ON constraint_db.public.t6 USING btree (lower(c) ASC, (a + b) ASC) -2574785782 t6 t6_expr_key CREATE UNIQUE INDEX t6_expr_key ON constraint_db.public.t6 USING btree (lower(c) ASC) -51576700 mv1 mv1_pkey CREATE UNIQUE INDEX mv1_pkey ON constraint_db.public.mv1 USING btree (rowid ASC) +3001466989 t6 t6_pkey CREATE UNIQUE INDEX t6_pkey ON constraint_db.public.t6 USING btree (rowid ASC) +3001466990 t6 t6_expr_idx CREATE INDEX t6_expr_idx ON constraint_db.public.t6 USING btree ((a + b) ASC) +3001466991 t6 t6_expr_expr1_idx CREATE INDEX t6_expr_expr1_idx ON constraint_db.public.t6 USING btree (lower(c) ASC, (a + b) ASC) +3001466984 t6 t6_expr_key CREATE UNIQUE INDEX t6_expr_key ON constraint_db.public.t6 USING btree (lower(c) ASC) +3001466985 t6 t6_expr_idx1 CREATE INDEX t6_expr_idx1 ON constraint_db.public.t6 USING btree ((m = 'foo'::public.mytype) ASC) WHERE (m = 'foo'::public.mytype) +2741730718 mv1 mv1_pkey CREATE UNIQUE INDEX mv1_pkey ON constraint_db.public.mv1 USING btree (rowid ASC) ## pg_catalog.pg_index @@ -1013,7 +1030,7 @@ WHERE indnkeyatts = 2 indexrelid indrelid indnatts indisunique indisprimary indisexclusion 450499961 55 2 true false false 3660126516 59 3 false false false -2574785777 63 2 false false false +3001466991 65 2 false false false query OBBBBB colnames SELECT indexrelid, indimmediate, indisclustered, indisvalid, indcheckxmin, indisready @@ -1023,30 +1040,34 @@ WHERE indnkeyatts = 2 indexrelid indimmediate indisclustered indisvalid indcheckxmin indisready 450499961 true false true false false 3660126516 false false true false false -2574785777 false false true false false +3001466991 false false true false false query OOBBTTTTTT colnames SELECT indexrelid, indrelid, indislive, indisreplident, indkey, indcollation, indclass, indoption, indexprs, indpred FROM pg_catalog.pg_index WHERE indnkeyatts = 2 ---- -indexrelid indrelid indislive indisreplident indkey indcollation indclass indoption indexprs indpred -450499961 55 true false 3 4 0 0 0 0 2 2 NULL NULL -3660126516 59 true false 1 2 3 0 0 0 0 2 1 NULL NULL -2574785777 63 true false 0 0 3403232968 0 0 0 2 2 NULL NULL +indexrelid indrelid indislive indisreplident indkey indcollation indclass indoption indexprs indpred +450499961 55 true false 3 4 0 0 0 0 2 2 NULL NULL +3660126516 59 true false 1 2 3 0 0 0 0 2 1 NULL NULL +3001466991 65 true false 0 0 3403232968 0 0 0 2 2 (lower(c)) (a + b) NULL -# indkey should be 0 for expression elements an index. -# TODO: In Postgres indexprs are values that represent the expression. Matching -# this representation may be difficult and unnecessary. -query TTT colnames -SELECT c.relname, i.indkey, i.indexprs +# Index expression elements should have an indkey of 0 and be included in +# indexprs. +# TODO(#72241): The types for the 'foo' casts should be constraint_db.mytype, +# not public.mytype. +query TTTT colnames +SELECT c.relname, i.indkey, i.indexprs, i.indpred FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid -WHERE c.relname IN ('t6_expr_idx', 't6_expr_expr1_idx') +WHERE c.relname LIKE 't6_%' ---- -relname indkey indexprs -t6_expr_idx 0 NULL -t6_expr_expr1_idx 0 0 NULL +relname indkey indexprs indpred +t6_pkey 6 NULL NULL +t6_expr_idx 0 (a + b) NULL +t6_expr_expr1_idx 0 0 (lower(c)) (a + b) NULL +t6_expr_key 0 (lower(c)) NULL +t6_expr_idx1 0 (m = 'foo'::public.mytype) m = 'foo'::public.mytype statement ok SET DATABASE = system @@ -1056,56 +1077,56 @@ SELECT * FROM pg_catalog.pg_index ORDER BY indexrelid ---- -indexrelid indrelid indnatts indisunique indisprimary indisexclusion indimmediate indisclustered indisvalid indcheckxmin indisready indislive indisreplident indkey indcollation indclass indoption indexprs indpred indnkeyatts -144368028 32 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -404104299 39 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -543291288 23 1 false false false false false true false false true false 1 3403232968 0 2 NULL NULL 1 -543291289 23 1 false false false false false true false false true false 2 3403232968 0 2 NULL NULL 1 -543291291 23 2 true true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 -663840565 42 2 false false false false false true false false true false 2 3 0 0 0 0 2 2 NULL NULL 2 -663840566 42 6 true true false true false true false false true false 1 2 3 4 5 6 0 0 0 0 3403232968 0 0 0 0 0 0 0 2 2 2 2 2 2 NULL NULL 6 -803027558 26 3 true true false true false true false false true false 1 2 3 0 0 3403232968 0 0 0 2 2 2 NULL NULL 3 -923576837 41 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -1062763829 25 4 true true false true false true false false true false 1 2 3 4 0 0 3403232968 3403232968 0 0 0 0 2 2 2 2 NULL NULL 4 -1183313104 44 2 true true false true false true false false true false 1 2 0 3403232968 0 0 2 2 NULL NULL 2 -1276104432 12 2 true true false true false true false false true false 1 6 0 0 0 0 2 2 NULL NULL 2 -1322500096 28 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -1489445036 35 3 false false false false false true false false true false 2 1 3 0 0 0 0 2 2 NULL NULL 2 -1489445039 35 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -1582236367 3 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -1628632026 19 1 false false false false false true false false true false 6 0 0 2 NULL NULL 1 -1628632027 19 1 false false false false false true false false true false 7 0 0 2 NULL NULL 1 -1628632028 19 1 false false false false false true false false true false 5 0 0 2 NULL NULL 1 -1628632029 19 1 false false false false false true false false true false 4 0 0 2 NULL NULL 1 -1628632031 19 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -1841972634 6 1 true true false true false true false false true false 1 3403232968 0 2 NULL NULL 1 -2008917577 37 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -2008917578 37 1 false false false false false true false false true false 5 0 0 2 NULL NULL 1 -2101708905 5 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -2148104569 21 2 true true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 -2268653844 40 4 true true false true false true false false true false 1 2 3 4 0 0 0 0 0 0 0 0 2 2 2 2 NULL NULL 4 -2361445172 8 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -2407840836 24 3 true true false true false true false false true false 1 2 3 0 0 0 0 0 0 2 2 2 NULL NULL 3 -2528390115 47 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -2621181440 15 2 false false false false false true false false true false 2 3 3403232968 0 0 0 2 2 NULL NULL 2 -2621181441 15 3 false false false false false true false false true false 6 7 2 3403232968 0 0 0 2 2 NULL NULL 2 -2621181443 15 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -2621181446 15 6 false false false false false true false false true false 8 2 3 11 10 9 0 3403232968 0 0 0 0 2 2 2 NULL status IN ('running':::STRING, 'reverting':::STRING, 'pending':::STRING, 'pause-requested':::STRING, 'cancel-requested':::STRING) 3 -2667577107 31 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -2834522046 34 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -3094258317 33 2 true true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 -3353994584 36 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -3446785912 4 1 true true false true false true false false true false 1 3403232968 0 2 NULL NULL 1 -3493181576 20 2 true true false true false true false false true false 1 2 0 0 0 0 2 2 NULL NULL 2 -3613730852 43 1 false false false false false true false false true false 2 0 0 2 NULL NULL 1 -3613730855 43 4 true true false true false true false false true false 1 2 3 4 0 0 3403232968 0 0 0 0 0 2 2 2 2 NULL NULL 4 -3706522183 11 4 true true false true false true false false true false 1 2 4 3 0 0 0 0 0 0 0 0 2 2 2 2 NULL NULL 4 -3752917847 27 2 true true false true false true false false true false 1 2 0 0 0 0 2 2 NULL NULL 2 -3873467122 46 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 -3966258450 14 1 true true false true false true false false true false 1 3403232968 0 2 NULL NULL 1 -4012654114 30 3 true true false true false true false false true false 1 2 3 0 0 3403232968 0 0 0 2 2 2 NULL NULL 3 -4133203393 45 2 true true false true false true false false true false 1 2 0 0 0 0 2 2 NULL NULL 2 -4225994721 13 2 true true false true false true false false true false 1 7 0 0 0 0 2 2 NULL NULL 2 +indexrelid indrelid indnatts indisunique indisprimary indisexclusion indimmediate indisclustered indisvalid indcheckxmin indisready indislive indisreplident indkey indcollation indclass indoption indexprs indpred indnkeyatts +144368028 32 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +404104299 39 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +543291288 23 1 false false false false false true false false true false 1 3403232968 0 2 NULL NULL 1 +543291289 23 1 false false false false false true false false true false 2 3403232968 0 2 NULL NULL 1 +543291291 23 2 true true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 +663840565 42 2 false false false false false true false false true false 2 3 0 0 0 0 2 2 NULL NULL 2 +663840566 42 6 true true false true false true false false true false 1 2 3 4 5 6 0 0 0 0 3403232968 0 0 0 0 0 0 0 2 2 2 2 2 2 NULL NULL 6 +803027558 26 3 true true false true false true false false true false 1 2 3 0 0 3403232968 0 0 0 2 2 2 NULL NULL 3 +923576837 41 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +1062763829 25 4 true true false true false true false false true false 1 2 3 4 0 0 3403232968 3403232968 0 0 0 0 2 2 2 2 NULL NULL 4 +1183313104 44 2 true true false true false true false false true false 1 2 0 3403232968 0 0 2 2 NULL NULL 2 +1276104432 12 2 true true false true false true false false true false 1 6 0 0 0 0 2 2 NULL NULL 2 +1322500096 28 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +1489445036 35 3 false false false false false true false false true false 2 1 3 0 0 0 0 2 2 NULL NULL 2 +1489445039 35 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +1582236367 3 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +1628632026 19 1 false false false false false true false false true false 6 0 0 2 NULL NULL 1 +1628632027 19 1 false false false false false true false false true false 7 0 0 2 NULL NULL 1 +1628632028 19 1 false false false false false true false false true false 5 0 0 2 NULL NULL 1 +1628632029 19 1 false false false false false true false false true false 4 0 0 2 NULL NULL 1 +1628632031 19 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +1841972634 6 1 true true false true false true false false true false 1 3403232968 0 2 NULL NULL 1 +2008917577 37 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +2008917578 37 1 false false false false false true false false true false 5 0 0 2 NULL NULL 1 +2101708905 5 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +2148104569 21 2 true true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 +2268653844 40 4 true true false true false true false false true false 1 2 3 4 0 0 0 0 0 0 0 0 2 2 2 2 NULL NULL 4 +2361445172 8 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +2407840836 24 3 true true false true false true false false true false 1 2 3 0 0 0 0 0 0 2 2 2 NULL NULL 3 +2528390115 47 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +2621181440 15 2 false false false false false true false false true false 2 3 3403232968 0 0 0 2 2 NULL NULL 2 +2621181441 15 3 false false false false false true false false true false 6 7 2 3403232968 0 0 0 2 2 NULL NULL 2 +2621181443 15 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +2621181446 15 6 false false false false false true false false true false 8 2 3 11 10 9 0 3403232968 0 0 0 0 2 2 2 NULL status IN ('running'::STRING, 'reverting'::STRING, 'pending'::STRING, 'pause-requested'::STRING, 'cancel-requested'::STRING) 3 +2667577107 31 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +2834522046 34 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +3094258317 33 2 true true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 +3353994584 36 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +3446785912 4 1 true true false true false true false false true false 1 3403232968 0 2 NULL NULL 1 +3493181576 20 2 true true false true false true false false true false 1 2 0 0 0 0 2 2 NULL NULL 2 +3613730852 43 1 false false false false false true false false true false 2 0 0 2 NULL NULL 1 +3613730855 43 4 true true false true false true false false true false 1 2 3 4 0 0 3403232968 0 0 0 0 0 2 2 2 2 NULL NULL 4 +3706522183 11 4 true true false true false true false false true false 1 2 4 3 0 0 0 0 0 0 0 0 2 2 2 2 NULL NULL 4 +3752917847 27 2 true true false true false true false false true false 1 2 0 0 0 0 2 2 NULL NULL 2 +3873467122 46 1 true true false true false true false false true false 1 0 0 2 NULL NULL 1 +3966258450 14 1 true true false true false true false false true false 1 3403232968 0 2 NULL NULL 1 +4012654114 30 3 true true false true false true false false true false 1 2 3 0 0 3403232968 0 0 0 2 2 2 NULL NULL 3 +4133203393 45 2 true true false true false true false false true false 1 2 0 0 0 0 2 2 NULL NULL 2 +4225994721 13 2 true true false true false true false false true false 1 7 0 0 0 0 2 2 NULL NULL 2 # From #26504 query OOI colnames @@ -1243,13 +1264,13 @@ ORDER BY con.oid oid conname connamespace contype condef 109163875 fk 2332901747 f FOREIGN KEY (t1_id) REFERENCES t1(a) 576034241 primary 2332901747 p PRIMARY KEY (value ASC) +699838052 t6_expr_key 2332901747 u UNIQUE (lower(c) ASC) 1329876328 fk_b_c 2332901747 f FOREIGN KEY (b, c) REFERENCES t4(b, c) MATCH FULL ON UPDATE RESTRICT 1488778805 check_c 2332901747 c CHECK ((c != ''::STRING)) 1652586190 fk 2332901747 f FOREIGN KEY (a, b) REFERENCES t1(b, c) 1729749923 uwi_b_c 2332901747 u UNIQUE WITHOUT INDEX (b, c) 1921111248 primary 2332901747 p PRIMARY KEY (value ASC) 2093076183 t5_a_fkey 2332901747 f FOREIGN KEY (a) REFERENCES t4(a) ON DELETE CASCADE -2119068666 t6_expr_key 2332901747 u UNIQUE (lower(c) ASC) 2897367075 uwi_b_partial 2332901747 u UNIQUE WITHOUT INDEX (b) WHERE (c = 'foo'::STRING) 3572320190 t1_pkey 2332901747 p PRIMARY KEY (p ASC) 3989820597 check_b 2332901747 c CHECK ((b > 11)) @@ -1267,13 +1288,13 @@ ORDER BY con.oid conname contype condeferrable condeferred convalidated conrelid contypid conindid fk f false false true 58 0 450499960 primary p false false true 56 0 2315049508 +t6_expr_key u false false true 65 0 3001466984 fk_b_c f false false true 62 0 0 check_c c false false true 59 0 0 fk f false false true 59 0 450499961 uwi_b_c u false false true 61 0 0 primary p false false true 57 0 969972501 t5_a_fkey f false false true 62 0 0 -t6_expr_key u false false true 63 0 2574785782 uwi_b_partial u false false true 61 0 0 t1_pkey p false false true 55 0 450499963 check_b c false false true 59 0 0 @@ -1298,10 +1319,10 @@ ORDER BY con.oid ---- conname confrelid confupdtype confdeltype confmatchtype primary 0 NULL NULL NULL +t6_expr_key 0 NULL NULL NULL check_c 0 NULL NULL NULL uwi_b_c 0 NULL NULL NULL primary 0 NULL NULL NULL -t6_expr_key 0 NULL NULL NULL uwi_b_partial 0 NULL NULL NULL t1_pkey 0 NULL NULL NULL check_b 0 NULL NULL NULL @@ -1332,13 +1353,13 @@ ORDER BY con.oid conname conislocal coninhcount connoinherit conkey fk true 0 true {1} primary true 0 true {1} +t6_expr_key true 0 true {9} fk_b_c true 0 true {2,3} check_c true 0 true {3} fk true 0 true {1,2} uwi_b_c true 0 true NULL primary true 0 true {1} t5_a_fkey true 0 true {1} -t6_expr_key true 0 true {8} uwi_b_partial true 0 true NULL t1_pkey true 0 true {1} check_b true 0 true {2} @@ -1355,10 +1376,10 @@ ORDER BY con.oid ---- conname confkey conpfeqop conppeqop conffeqop conexclop conbin consrc condef primary NULL NULL NULL NULL NULL NULL NULL PRIMARY KEY (value ASC) +t6_expr_key NULL NULL NULL NULL NULL NULL NULL UNIQUE (lower(c) ASC) check_c NULL NULL NULL NULL NULL (c != ''::STRING) (c != ''::STRING) CHECK ((c != ''::STRING)) uwi_b_c NULL NULL NULL NULL NULL NULL NULL UNIQUE WITHOUT INDEX (b, c) primary NULL NULL NULL NULL NULL NULL NULL PRIMARY KEY (value ASC) -t6_expr_key NULL NULL NULL NULL NULL NULL NULL UNIQUE (lower(c) ASC) uwi_b_partial NULL NULL NULL NULL NULL NULL NULL UNIQUE WITHOUT INDEX (b) WHERE (c = 'foo'::STRING) t1_pkey NULL NULL NULL NULL NULL NULL NULL PRIMARY KEY (p ASC) check_b NULL NULL NULL NULL NULL (b > 11) (b > 11) CHECK ((b > 11)) @@ -1476,8 +1497,8 @@ SELECT * FROM pg_rewrite WHERE ev_class IN ( ) ORDER BY oid ---- oid rulename ev_class ev_type ev_enabled is_instead ev_qual ev_action -109588109 _RETURN 72 1 NULL true NULL NULL -3059478515 _RETURN 71 1 NULL true NULL NULL +2540005971 _RETURN 73 1 NULL true NULL NULL +3885082977 _RETURN 74 1 NULL true NULL NULL ## pg_catalog.pg_enum statement ok @@ -1488,10 +1509,13 @@ query OORT colnames SELECT * FROM pg_enum ---- oid enumtypid enumsortorder enumlabel -1747685667 100073 0 v1 -1747685859 100073 1 v2 -1781241033 100075 0 v3 -1781240841 100075 1 v4 +1043025669 100063 0 foo +1043025861 100063 1 bar +1043025797 100063 2 baz +1781241033 100075 0 v1 +1781240841 100075 1 v2 +1814796271 100077 0 v3 +1814796079 100077 1 v4 ## pg_catalog.pg_type @@ -1586,15 +1610,17 @@ oid typname typnamespace typowner typ 100060 v1 2332901747 1546506610 -1 false c 100061 t4 2332901747 1546506610 -1 false c 100062 t5 2332901747 1546506610 -1 false c -100063 t6 2332901747 1546506610 -1 false c -100064 mv1 2332901747 1546506610 -1 false c -100070 source_table 2332901747 1546506610 -1 false c -100071 depend_view 2332901747 1546506610 -1 false c -100072 view_dependingon_view 2332901747 1546506610 -1 false c -100073 newtype1 2332901747 1546506610 -1 false e -100074 _newtype1 2332901747 1546506610 -1 false b -100075 newtype2 2332901747 1546506610 -1 false e -100076 _newtype2 2332901747 1546506610 -1 false b +100063 mytype 2332901747 1546506610 -1 false e +100064 _mytype 2332901747 1546506610 -1 false b +100065 t6 2332901747 1546506610 -1 false c +100066 mv1 2332901747 1546506610 -1 false c +100072 source_table 2332901747 1546506610 -1 false c +100073 depend_view 2332901747 1546506610 -1 false c +100074 view_dependingon_view 2332901747 1546506610 -1 false c +100075 newtype1 2332901747 1546506610 -1 false e +100076 _newtype1 2332901747 1546506610 -1 false b +100077 newtype2 2332901747 1546506610 -1 false e +100078 _newtype2 2332901747 1546506610 -1 false b 4294967010 spatial_ref_sys 3553698885 3233629770 -1 false c 4294967011 geometry_columns 3553698885 3233629770 -1 false c 4294967012 geography_columns 3553698885 3233629770 -1 false c @@ -1969,15 +1995,17 @@ oid typname typcategory typispreferred 100060 v1 C false true , 60 0 0 100061 t4 C false true , 61 0 0 100062 t5 C false true , 62 0 0 -100063 t6 C false true , 63 0 0 -100064 mv1 C false true , 64 0 0 -100070 source_table C false true , 70 0 0 -100071 depend_view C false true , 71 0 0 -100072 view_dependingon_view C false true , 72 0 0 -100073 newtype1 E false true , 0 0 100074 -100074 _newtype1 A false true , 0 100073 0 -100075 newtype2 E false true , 0 0 100076 -100076 _newtype2 A false true , 0 100075 0 +100063 mytype E false true , 0 0 100064 +100064 _mytype A false true , 0 100063 0 +100065 t6 C false true , 65 0 0 +100066 mv1 C false true , 66 0 0 +100072 source_table C false true , 72 0 0 +100073 depend_view C false true , 73 0 0 +100074 view_dependingon_view C false true , 74 0 0 +100075 newtype1 E false true , 0 0 100076 +100076 _newtype1 A false true , 0 100075 0 +100077 newtype2 E false true , 0 0 100078 +100078 _newtype2 A false true , 0 100077 0 4294967010 spatial_ref_sys C false true , 4294967010 0 0 4294967011 geometry_columns C false true , 4294967011 0 0 4294967012 geography_columns C false true , 4294967012 0 0 @@ -2352,15 +2380,17 @@ oid typname typinput typoutput 100060 v1 record_in record_out record_recv record_send 0 0 0 100061 t4 record_in record_out record_recv record_send 0 0 0 100062 t5 record_in record_out record_recv record_send 0 0 0 -100063 t6 record_in record_out record_recv record_send 0 0 0 -100064 mv1 record_in record_out record_recv record_send 0 0 0 -100070 source_table record_in record_out record_recv record_send 0 0 0 -100071 depend_view record_in record_out record_recv record_send 0 0 0 -100072 view_dependingon_view record_in record_out record_recv record_send 0 0 0 -100073 newtype1 enum_in enum_out enum_recv enum_send 0 0 0 -100074 _newtype1 array_in array_out array_recv array_send 0 0 0 -100075 newtype2 enum_in enum_out enum_recv enum_send 0 0 0 -100076 _newtype2 array_in array_out array_recv array_send 0 0 0 +100063 mytype enum_in enum_out enum_recv enum_send 0 0 0 +100064 _mytype array_in array_out array_recv array_send 0 0 0 +100065 t6 record_in record_out record_recv record_send 0 0 0 +100066 mv1 record_in record_out record_recv record_send 0 0 0 +100072 source_table record_in record_out record_recv record_send 0 0 0 +100073 depend_view record_in record_out record_recv record_send 0 0 0 +100074 view_dependingon_view record_in record_out record_recv record_send 0 0 0 +100075 newtype1 enum_in enum_out enum_recv enum_send 0 0 0 +100076 _newtype1 array_in array_out array_recv array_send 0 0 0 +100077 newtype2 enum_in enum_out enum_recv enum_send 0 0 0 +100078 _newtype2 array_in array_out array_recv array_send 0 0 0 4294967010 spatial_ref_sys record_in record_out record_recv record_send 0 0 0 4294967011 geometry_columns record_in record_out record_recv record_send 0 0 0 4294967012 geography_columns record_in record_out record_recv record_send 0 0 0 @@ -2735,15 +2765,17 @@ oid typname typalign typstorage typnotn 100060 v1 NULL NULL false 0 -1 100061 t4 NULL NULL false 0 -1 100062 t5 NULL NULL false 0 -1 -100063 t6 NULL NULL false 0 -1 -100064 mv1 NULL NULL false 0 -1 -100070 source_table NULL NULL false 0 -1 -100071 depend_view NULL NULL false 0 -1 -100072 view_dependingon_view NULL NULL false 0 -1 -100073 newtype1 NULL NULL false 0 -1 -100074 _newtype1 NULL NULL false 0 -1 -100075 newtype2 NULL NULL false 0 -1 -100076 _newtype2 NULL NULL false 0 -1 +100063 mytype NULL NULL false 0 -1 +100064 _mytype NULL NULL false 0 -1 +100065 t6 NULL NULL false 0 -1 +100066 mv1 NULL NULL false 0 -1 +100072 source_table NULL NULL false 0 -1 +100073 depend_view NULL NULL false 0 -1 +100074 view_dependingon_view NULL NULL false 0 -1 +100075 newtype1 NULL NULL false 0 -1 +100076 _newtype1 NULL NULL false 0 -1 +100077 newtype2 NULL NULL false 0 -1 +100078 _newtype2 NULL NULL false 0 -1 4294967010 spatial_ref_sys NULL NULL false 0 -1 4294967011 geometry_columns NULL NULL false 0 -1 4294967012 geography_columns NULL NULL false 0 -1 @@ -3118,15 +3150,17 @@ oid typname typndims typcollation typde 100060 v1 0 0 NULL NULL NULL 100061 t4 0 0 NULL NULL NULL 100062 t5 0 0 NULL NULL NULL -100063 t6 0 0 NULL NULL NULL -100064 mv1 0 0 NULL NULL NULL -100070 source_table 0 0 NULL NULL NULL -100071 depend_view 0 0 NULL NULL NULL -100072 view_dependingon_view 0 0 NULL NULL NULL -100073 newtype1 0 0 NULL NULL NULL -100074 _newtype1 0 0 NULL NULL NULL -100075 newtype2 0 0 NULL NULL NULL -100076 _newtype2 0 0 NULL NULL NULL +100063 mytype 0 0 NULL NULL NULL +100064 _mytype 0 0 NULL NULL NULL +100065 t6 0 0 NULL NULL NULL +100066 mv1 0 0 NULL NULL NULL +100072 source_table 0 0 NULL NULL NULL +100073 depend_view 0 0 NULL NULL NULL +100074 view_dependingon_view 0 0 NULL NULL NULL +100075 newtype1 0 0 NULL NULL NULL +100076 _newtype1 0 0 NULL NULL NULL +100077 newtype2 0 0 NULL NULL NULL +100078 _newtype2 0 0 NULL NULL NULL 4294967010 spatial_ref_sys 0 0 NULL NULL NULL 4294967011 geometry_columns 0 0 NULL NULL NULL 4294967012 geography_columns 0 0 NULL NULL NULL @@ -3437,7 +3471,7 @@ FROM pg_catalog.pg_type WHERE typname = 'newtype1' ---- oid typname typnamespace typowner typlen typbyval typtype -100073 newtype1 2332901747 1546506610 -1 false e +100075 newtype1 2332901747 1546506610 -1 false e query OTOOIBT colnames SELECT oid, typname, typnamespace, typowner, typlen, typbyval, typtype @@ -3459,7 +3493,7 @@ FROM pg_catalog.pg_type WHERE typname = 'source_table' ---- oid typname typnamespace typowner typlen typbyval typtype -100070 source_table 2332901747 1546506610 -1 false c +100072 source_table 2332901747 1546506610 -1 false c let $sourceid SELECT oid @@ -3473,7 +3507,7 @@ FROM pg_catalog.pg_type WHERE oid = $sourceid ---- oid typname typnamespace typowner typlen typbyval typtype -100070 source_table 2332901747 1546506610 -1 false c +100072 source_table 2332901747 1546506610 -1 false c ## pg_catalog.pg_proc @@ -4294,8 +4328,8 @@ query OOIIIIIB colnames SELECT * FROM pg_catalog.pg_sequence ---- seqrelid seqtypid seqstart seqincrement seqmax seqmin seqcache seqcycle -78 20 1 1 9223372036854775807 1 1 false -79 20 6 2 10 5 1 false +80 20 1 1 9223372036854775807 1 1 false +81 20 6 2 10 5 1 false statement ok DROP DATABASE seq @@ -4348,8 +4382,8 @@ WHERE n.nspname = 'public' 581442131 t3 unique_rowid() 1100914677 t4 unique_rowid() 2445991686 t5 unique_rowid() -3791068694 t6 unique_rowid() -2779881566 mv1 unique_rowid() +4124958571 t6 unique_rowid() +1175068284 mv1 unique_rowid() # Verify that a set database shows tables from that database for a non-root # user, when that user has permissions. @@ -4836,13 +4870,13 @@ CREATE TABLE jt (a INT PRIMARY KEY); INSERT INTO jt VALUES(1); INSERT INTO jt VA query ITT SELECT a, oid, relname FROM jt INNER LOOKUP JOIN pg_class ON a::oid=oid ---- -105 105 jt +107 107 jt query ITT SELECT a, oid, relname FROM jt LEFT OUTER LOOKUP JOIN pg_class ON a::oid=oid ---- 1 NULL NULL -105 105 jt +107 107 jt subtest regression_49207 statement ok diff --git a/pkg/sql/pg_catalog.go b/pkg/sql/pg_catalog.go index e3e947ec62d0..a0ffbdf86b54 100644 --- a/pkg/sql/pg_catalog.go +++ b/pkg/sql/pg_catalog.go @@ -1696,6 +1696,7 @@ https://www.postgresql.org/docs/9.5/catalog-pg-index.html`, indoption := tree.NewDArray(types.Int) colIDs := make([]descpb.ColumnID, 0, index.NumKeyColumns()) + exprs := make([]string, 0, index.NumKeyColumns()) for i := index.IndexDesc().ExplicitColumnStartIdx(); i < index.NumKeyColumns(); i++ { columnID := index.GetKeyColumnID(i) col, err := table.FindColumnWithID(columnID) @@ -1706,6 +1707,17 @@ https://www.postgresql.org/docs/9.5/catalog-pg-index.html`, // should be 0. if col.IsExpressionIndexColumn() { colIDs = append(colIDs, 0) + formattedExpr, err := schemaexpr.FormatExprForDisplay( + ctx, + table, + col.GetComputeExpr(), + p.SemaCtx(), + tree.FmtPGCatalog, + ) + if err != nil { + return err + } + exprs = append(exprs, fmt.Sprintf("(%s)", formattedExpr)) } else { colIDs = append(colIDs, columnID) } @@ -1745,7 +1757,21 @@ https://www.postgresql.org/docs/9.5/catalog-pg-index.html`, } indpred := tree.DNull if index.IsPartial() { - indpred = tree.NewDString(index.GetPredicate()) + formattedPred, err := schemaexpr.FormatExprForDisplay( + ctx, + table, + index.GetPredicate(), + p.SemaCtx(), + tree.FmtPGCatalog, + ) + if err != nil { + return err + } + indpred = tree.NewDString(formattedPred) + } + indexprs := tree.DNull + if len(exprs) > 0 { + indexprs = tree.NewDString(strings.Join(exprs, " ")) } return addRow( h.IndexOid(table.GetID(), index.GetID()), // indexrelid @@ -1765,7 +1791,7 @@ https://www.postgresql.org/docs/9.5/catalog-pg-index.html`, collationOidVector, // indcollation indclass, // indclass indoptionIntVector, // indoption - tree.DNull, // indexprs + indexprs, // indexprs indpred, // indpred tree.NewDInt(tree.DInt(indnkeyatts)), // indnkeyatts ) @@ -1833,7 +1859,17 @@ func indexDefFromDescriptor( return "", err } if col.IsExpressionIndexColumn() { - expr, err := parser.ParseExpr(col.GetComputeExpr()) + formattedExpr, err := schemaexpr.FormatExprForDisplay( + ctx, + table, + col.GetComputeExpr(), + p.SemaCtx(), + tree.FmtPGCatalog, + ) + if err != nil { + return "", err + } + expr, err := parser.ParseExpr(formattedExpr) if err != nil { return "", err } From 95e5b69d038f796e71f69ab1513b86862ea1a771 Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Fri, 29 Oct 2021 12:07:01 -0400 Subject: [PATCH 121/205] kvserver: resurrect using_applied_state_key in ReplicaState Fixes #72029. using_applied_state_key was previously used to check whether the Range had upgraded to begin using the RangeAppliedState key. When set to true, replicas receiving the state (through a ReplicatedEvalResult) knew to begin using the new key. In #58088 (in 21.1) we introduced a migration to iterate through all ranges in the system and have them start using the RangeAppliedState key. In 21.2, this field was always set to true -- 21.2 nodes were already using the RangeAppliedState key, or receiving messages from 21.1 nodes that were also using the RangeAppliedState key. In 21.2 (and in 21.1 for that matter) we didn't need to trigger the "if set to true in an incoming message, start using the RangeAppliedState key" code path. When looking to get rid of this field in 22.1 (#70464), we observed that there was an unintentional read of this field in 21.2 nodes (see #72029 and \#72222); the saga is as follows: - Removing this field in 22.1 meant it was set as false when received at 21.2 nodes. - This should've been fine! We weren't using this field to trigger any upgrade code paths (like it was originally intended for). - It turns out that in 21.2 we were using the ReplicaState from the incoming snapshot to update our in-memory replica state - Because the proto field was being phased out, there was now a divergence between the on-disk state (field set to true, from earlier 21.2 operations) and the in-memory state (field set to false, because sent from a version that attempted to get rid of this field). Removing proto fields from the replica state are not possible until we stop using the protobuf copy of the replica state when applying a snapshot (#72222). Once that's done, we should be able to stop sending the replica state as part of the snapshot in the subsequent release. Release note: None --- pkg/kv/kvserver/kvserverpb/state.pb.go | 209 +++++++++++++++++-------- pkg/kv/kvserver/kvserverpb/state.proto | 37 ++++- pkg/kv/kvserver/replica.go | 5 + pkg/kv/kvserver/replica_command.go | 4 + pkg/kv/kvserver/store_snapshot.go | 4 + 5 files changed, 190 insertions(+), 69 deletions(-) diff --git a/pkg/kv/kvserver/kvserverpb/state.pb.go b/pkg/kv/kvserver/kvserverpb/state.pb.go index 77b5e7a4a728..be7726953ab3 100644 --- a/pkg/kv/kvserver/kvserverpb/state.pb.go +++ b/pkg/kv/kvserver/kvserverpb/state.pb.go @@ -64,6 +64,41 @@ type ReplicaState struct { // not be served. GCThreshold *hlc.Timestamp `protobuf:"bytes,6,opt,name=gc_threshold,json=gcThreshold,proto3" json:"gc_threshold,omitempty"` Stats *enginepb.MVCCStats `protobuf:"bytes,7,opt,name=stats,proto3" json:"stats,omitempty"` + // DeprecatedUsingAppliedStateKey was previously used to check whether the + // Range had upgraded to begin using the RangeAppliedState key. When set to + // true, replicas receiving the state (through a ReplicatedEvalResult) knew to + // begin using the new key. In #58088 (21.1) we introduced a migration to + // iterate through all ranges in the system and have them start using the + // RangeAppliedState key. This field was always set to true in 21.2, nodes + // nodes were already using the RangeAppliedState key, or receiving messages + // from 21.1 nodes that were also already using the RangeAppliedState key. In + // 21.2 (and in 21.1 for that matter) we no longer needed to trigger the "if + // set to true in an incoming message, upgrade to start using the + // RangeAppliedState key" code path. + // + // When looking to get rid of this field in 22.1 (#70464), we observed that + // there was an unintentional read of this field in 21.2 nodes (#72029). The + // saga is as follows: + // - Removing this field in 22.1 meant it was set as false when received at + // 21.2 nodes. + // - This should've been fine! We weren't using this field to trigger any + // upgrade code paths (like it was originally intended for). + // - It turns out that in 21.2 we were using the ReplicaState from the + // incoming snapshot to update our in-memory replica state + // - Because the proto field was being phased out, there was now a divergence + // between the on-disk state (field set to true, from earlier 21.2 + // operations) and the in-memory state (field set to false, because sent + // from a version that attempted to get rid of this field). + // + // Removing proto fields from the replica state are not possible until we stop + // using the protobuf copy of the replica state when applying a snapshot + // (#72222). Once that's done, we should be able to stop sending the replica + // state as part of the snapshot in the subsequent release. + // + // TODO(irfansharif): Remove this field in the release after the one where we + // stop consulting the protobuf copy of the replica state when applying a + // snapshot. + DeprecatedUsingAppliedStateKey bool `protobuf:"varint,11,opt,name=deprecated_using_applied_state_key,json=deprecatedUsingAppliedStateKey,proto3" json:"deprecated_using_applied_state_key,omitempty"` // Version tells us which migrations can be assumed to have run against this // particular replica. When we introduce backwards incompatible changes to the // replica state (for example using the unreplicated truncated state instead @@ -251,74 +286,76 @@ func init() { } var fileDescriptor_cc107fbd3ff296cb = []byte{ - // 1065 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x6e, 0xdb, 0x36, - 0x18, 0x8f, 0x66, 0xa5, 0x71, 0x68, 0x27, 0x71, 0x99, 0x25, 0x51, 0xd3, 0xd5, 0x36, 0x3c, 0x6c, - 0xf0, 0x80, 0x4e, 0xc2, 0xba, 0x61, 0xc5, 0xfe, 0x00, 0x43, 0x9c, 0x00, 0x83, 0x8d, 0x64, 0x48, - 0x18, 0xa3, 0x03, 0xb6, 0x83, 0x40, 0x4b, 0x8c, 0x4c, 0x44, 0x16, 0x55, 0x92, 0x36, 0xd2, 0x3e, - 0xc5, 0x1e, 0x61, 0x87, 0x3d, 0xc0, 0x1e, 0x23, 0xc7, 0x1c, 0x7b, 0x32, 0x56, 0xe7, 0xb2, 0xcb, - 0x5e, 0x60, 0xa7, 0x81, 0xa4, 0xe4, 0xd8, 0x4e, 0x80, 0x66, 0x40, 0x6f, 0xd4, 0xf7, 0xfd, 0x7e, - 0xdf, 0x3f, 0xfe, 0x3e, 0x49, 0xa0, 0x71, 0x3e, 0xf2, 0xce, 0x47, 0x82, 0xf0, 0x11, 0xe1, 0xd3, - 0x43, 0xda, 0xf3, 0x84, 0xc4, 0x92, 0xb8, 0x29, 0x67, 0x92, 0xc1, 0x5a, 0xc0, 0x82, 0x73, 0xce, - 0x70, 0xd0, 0x77, 0xcf, 0x47, 0x6e, 0x0e, 0x72, 0x85, 0x64, 0x1c, 0x47, 0x24, 0xed, 0xed, 0x3e, - 0xce, 0x8e, 0x1e, 0x49, 0x22, 0x9a, 0x90, 0xb4, 0xe7, 0x0d, 0x46, 0x41, 0x60, 0xd8, 0xbb, 0x8f, - 0x35, 0x33, 0xed, 0x79, 0x34, 0x91, 0x84, 0x27, 0x38, 0xf6, 0x39, 0x3e, 0x93, 0x99, 0x73, 0x3b, - 0x77, 0x0e, 0x88, 0xc4, 0x21, 0x96, 0x38, 0xb3, 0xc3, 0xdc, 0x3e, 0x63, 0x73, 0x86, 0x92, 0xc6, - 0x5e, 0x3f, 0x0e, 0x3c, 0x49, 0x07, 0x44, 0x48, 0x3c, 0x48, 0x33, 0xcf, 0x87, 0x11, 0x8b, 0x98, - 0x3e, 0x7a, 0xea, 0x64, 0xac, 0x8d, 0xb7, 0x36, 0x28, 0x23, 0x92, 0xc6, 0x34, 0xc0, 0xa7, 0xaa, - 0x1b, 0xf8, 0x14, 0x40, 0x95, 0xda, 0xc7, 0x69, 0x1a, 0x53, 0x12, 0xfa, 0x34, 0x09, 0xc9, 0x85, - 0x63, 0xd5, 0xad, 0xa6, 0x8d, 0x2a, 0xca, 0xb3, 0x67, 0x1c, 0x6d, 0x65, 0x87, 0x2e, 0xd8, 0x8c, - 0x09, 0x16, 0x64, 0x01, 0xfe, 0x81, 0x86, 0x3f, 0xd4, 0xae, 0x39, 0xfc, 0xd7, 0xc0, 0x0e, 0x89, - 0x08, 0x9c, 0x42, 0xdd, 0x6a, 0x96, 0x9e, 0x35, 0xdc, 0x9b, 0xa1, 0x65, 0xbd, 0xb8, 0x08, 0x27, - 0x11, 0x39, 0x20, 0x22, 0xe0, 0x34, 0x95, 0x8c, 0x23, 0x8d, 0x87, 0x2e, 0x58, 0xd6, 0xc1, 0x1c, - 0x5b, 0x13, 0x9d, 0x3b, 0x88, 0x87, 0xca, 0x8f, 0x0c, 0x0c, 0xfe, 0x04, 0x36, 0x24, 0x1f, 0x26, - 0x01, 0x96, 0x24, 0xf4, 0xf5, 0x35, 0x39, 0xcb, 0x9a, 0xf9, 0xc9, 0x9d, 0x29, 0xcf, 0x64, 0x37, - 0x47, 0xeb, 0x29, 0xa0, 0x75, 0x39, 0xf7, 0x0c, 0x4f, 0x40, 0x39, 0x0a, 0x7c, 0xd9, 0xe7, 0x44, - 0xf4, 0x59, 0x1c, 0x3a, 0x0f, 0x74, 0xb0, 0x27, 0x33, 0xc1, 0xd4, 0xdc, 0xdd, 0x7e, 0x1c, 0xb8, - 0xdd, 0x7c, 0xee, 0xad, 0x8d, 0xc9, 0xb8, 0x56, 0xfa, 0x71, 0xbf, 0x9b, 0xb3, 0x50, 0x29, 0x0a, - 0xa6, 0x0f, 0xf0, 0x3b, 0xb0, 0xac, 0x0a, 0x13, 0xce, 0xca, 0xad, 0xc2, 0x32, 0xa5, 0xb8, 0xb9, - 0x52, 0xdc, 0xa3, 0x17, 0xfb, 0xfb, 0xaa, 0x10, 0x81, 0x0c, 0x07, 0x7e, 0x05, 0x56, 0x46, 0x84, - 0x0b, 0xca, 0x12, 0xa7, 0xac, 0xe9, 0xbb, 0x77, 0xf4, 0xf5, 0xc2, 0x20, 0x50, 0x0e, 0x85, 0x3f, - 0x83, 0x2d, 0x7d, 0xb7, 0x41, 0xcc, 0x04, 0x09, 0xfd, 0xa9, 0x42, 0x9c, 0xb5, 0xfb, 0xb4, 0x63, - 0x5f, 0x8e, 0x6b, 0x4b, 0x68, 0x53, 0x45, 0xd8, 0xd7, 0x01, 0xa6, 0xae, 0x6f, 0xed, 0xbf, 0x7f, - 0xaf, 0x59, 0x1d, 0xbb, 0x58, 0xac, 0xac, 0x76, 0xec, 0xe2, 0x6a, 0x05, 0x74, 0xec, 0x22, 0xa8, - 0x94, 0x3a, 0x76, 0xb1, 0x54, 0x29, 0x37, 0xfe, 0x59, 0x01, 0xab, 0xfa, 0x5a, 0xdb, 0xc9, 0x19, - 0x83, 0x47, 0xa6, 0x6f, 0xa2, 0x35, 0x55, 0x7a, 0xf6, 0xb9, 0xfb, 0x8e, 0xc5, 0x71, 0x67, 0xe5, - 0xd9, 0x2a, 0xaa, 0x22, 0xae, 0xc6, 0x35, 0xcb, 0x4c, 0x82, 0xc0, 0x27, 0x00, 0xc4, 0x58, 0xc8, - 0x39, 0xe1, 0xad, 0x2a, 0x8b, 0x11, 0x5c, 0x0d, 0x94, 0x92, 0xe1, 0xc0, 0x4f, 0x49, 0x12, 0xd2, - 0x24, 0xd2, 0xba, 0xb3, 0x11, 0x48, 0x86, 0x83, 0x63, 0x63, 0xc9, 0x01, 0x21, 0x67, 0x69, 0x4a, - 0x42, 0xad, 0x12, 0x03, 0x38, 0x30, 0x16, 0xd8, 0x00, 0x6b, 0x7a, 0x68, 0x31, 0x8b, 0x7c, 0x41, - 0x5f, 0x13, 0x7d, 0xf7, 0x05, 0x54, 0x52, 0xc6, 0x43, 0x16, 0x9d, 0xd2, 0xd7, 0x04, 0x7e, 0x91, - 0x0d, 0x36, 0xc7, 0xf8, 0x92, 0x0f, 0x85, 0x24, 0xa1, 0x03, 0xea, 0x56, 0xb3, 0x88, 0xe0, 0x0c, - 0xb6, 0x6b, 0x3c, 0xf0, 0x7b, 0xb0, 0x8b, 0xd3, 0x94, 0xb3, 0x0b, 0x3a, 0xc0, 0x92, 0xf8, 0x29, - 0x67, 0x29, 0x13, 0x38, 0xf6, 0x5f, 0x0e, 0x99, 0xc4, 0x5a, 0x13, 0x05, 0xe4, 0xcc, 0x20, 0x8e, - 0x33, 0xc0, 0x89, 0xf2, 0xc3, 0x6f, 0xc0, 0xa3, 0x79, 0x86, 0xdf, 0x53, 0x5b, 0x68, 0x86, 0xb0, - 0xae, 0xc9, 0xdb, 0xe9, 0x2c, 0xa3, 0x85, 0x05, 0x31, 0x13, 0xf9, 0x01, 0x7c, 0xb4, 0x40, 0xe5, - 0xc4, 0xec, 0xf0, 0xcb, 0x21, 0x19, 0x12, 0x67, 0xa3, 0x5e, 0x68, 0x16, 0xd0, 0xa3, 0x39, 0x36, - 0x32, 0x88, 0x13, 0x05, 0x80, 0x9f, 0x82, 0x0d, 0xae, 0x6e, 0xd3, 0x1f, 0xe0, 0x0b, 0xbf, 0xf7, - 0x4a, 0x12, 0xe1, 0x14, 0x75, 0xc6, 0x35, 0x6d, 0x3e, 0xc2, 0x17, 0x2d, 0x65, 0x84, 0xbf, 0x82, - 0x1d, 0x1c, 0x48, 0x3a, 0x22, 0xb7, 0xf5, 0x56, 0xbe, 0xbf, 0xde, 0xb6, 0x4c, 0x8c, 0x05, 0xc5, - 0xc1, 0xe7, 0x60, 0x47, 0x67, 0x3b, 0x23, 0x24, 0xf4, 0x39, 0x89, 0xa8, 0x90, 0x1c, 0x4b, 0xca, - 0x12, 0xa1, 0xc5, 0x5c, 0x40, 0xdb, 0x53, 0x37, 0x9a, 0xf5, 0xc2, 0xcf, 0xc0, 0xaa, 0x24, 0x09, - 0x4e, 0xa4, 0x4f, 0x43, 0xa7, 0xa2, 0x6e, 0xbb, 0x55, 0x9e, 0x8c, 0x6b, 0xc5, 0xae, 0x36, 0xb6, - 0x0f, 0x50, 0xd1, 0xb8, 0xdb, 0x21, 0x24, 0x60, 0x67, 0xb1, 0x72, 0x3f, 0x65, 0x31, 0x0d, 0x5e, - 0x39, 0xb0, 0x6e, 0x35, 0xd7, 0xe7, 0xb4, 0x3b, 0xf7, 0xfe, 0x5a, 0xa8, 0xf6, 0x58, 0x93, 0xd0, - 0x56, 0x70, 0x97, 0x19, 0xfe, 0x69, 0x81, 0x8f, 0x6f, 0xe5, 0x11, 0x34, 0x24, 0x92, 0xe3, 0x44, - 0xa4, 0x8c, 0x2b, 0x71, 0x9f, 0x31, 0x67, 0x53, 0x0f, 0xed, 0xf9, 0xbb, 0xf7, 0x45, 0x55, 0x70, - 0x4a, 0x43, 0xd2, 0xcd, 0xf9, 0x6a, 0xef, 0x5a, 0x4d, 0x35, 0xce, 0xc9, 0xb8, 0x56, 0x5f, 0x28, - 0xee, 0x16, 0x12, 0xd5, 0x83, 0xdb, 0x08, 0x39, 0x8b, 0x98, 0xee, 0xbb, 0x5d, 0x59, 0x9e, 0xee, - 0x7b, 0xa9, 0x52, 0xee, 0xd8, 0xc5, 0x87, 0x15, 0xd8, 0xf8, 0xa3, 0x00, 0xb6, 0xef, 0x2e, 0x02, - 0x76, 0xc0, 0x3a, 0x37, 0xeb, 0x9c, 0x89, 0x22, 0x7b, 0x0b, 0xdc, 0x4b, 0x0a, 0x6b, 0x19, 0xd5, - 0x34, 0x00, 0x87, 0xa0, 0x94, 0xc7, 0x8a, 0x31, 0xd5, 0xab, 0x5f, 0x68, 0x75, 0x27, 0xe3, 0x1a, - 0xc8, 0xde, 0x18, 0x87, 0x7b, 0xed, 0x7f, 0xc7, 0xb5, 0x56, 0x44, 0x65, 0x7f, 0xd8, 0x73, 0x03, - 0x36, 0xf0, 0xa6, 0x49, 0xc2, 0xde, 0xcd, 0xd9, 0x4b, 0xcf, 0x23, 0x6f, 0xf6, 0x0b, 0x6f, 0x2a, - 0x93, 0xc2, 0x0b, 0xa4, 0xfa, 0xae, 0xec, 0xb5, 0x11, 0xc8, 0x12, 0x1d, 0x62, 0xaa, 0x5a, 0x08, - 0x48, 0x22, 0x39, 0x8e, 0xf3, 0x16, 0x0a, 0xff, 0xa3, 0x85, 0x8c, 0x7a, 0xd3, 0x42, 0x1e, 0x4b, - 0xb5, 0x60, 0xdf, 0xb4, 0xb0, 0x6f, 0xcc, 0xef, 0xb1, 0x85, 0x2c, 0xd1, 0x21, 0xa6, 0xe6, 0xfa, - 0x5a, 0x4f, 0x2f, 0xdf, 0x56, 0x97, 0x2e, 0x27, 0x55, 0xeb, 0x6a, 0x52, 0xb5, 0xde, 0x4c, 0xaa, - 0xd6, 0x5f, 0x93, 0xaa, 0xf5, 0xdb, 0x75, 0x75, 0xe9, 0xea, 0xba, 0xba, 0xf4, 0xe6, 0xba, 0xba, - 0xf4, 0x0b, 0xb8, 0xf9, 0xd7, 0xe9, 0x3d, 0xd0, 0xff, 0x0b, 0x5f, 0xfe, 0x17, 0x00, 0x00, 0xff, - 0xff, 0x26, 0xb9, 0x1b, 0x85, 0x0c, 0x09, 0x00, 0x00, + // 1098 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4d, 0x6f, 0xe3, 0xc4, + 0x1b, 0xaf, 0xff, 0x71, 0x77, 0xd3, 0x49, 0xda, 0x66, 0xa7, 0xff, 0xb6, 0xde, 0x2e, 0x9b, 0x44, + 0x41, 0xa0, 0x20, 0x2d, 0xb6, 0x58, 0x10, 0x2b, 0x5e, 0x24, 0xd4, 0xb4, 0x12, 0x4a, 0x68, 0x51, + 0xeb, 0x86, 0x45, 0x82, 0x83, 0x35, 0xb1, 0xa7, 0xce, 0x28, 0x8e, 0xc7, 0x3b, 0x33, 0x8e, 0xda, + 0xfd, 0x14, 0x7c, 0x04, 0x0e, 0x48, 0x5c, 0xf9, 0x18, 0x3d, 0xf6, 0xb8, 0xa7, 0x08, 0xd2, 0x0b, + 0x17, 0xbe, 0x00, 0x27, 0x34, 0x33, 0x76, 0xde, 0x5a, 0x69, 0x8b, 0xc4, 0x6d, 0xf2, 0x3c, 0xbf, + 0xdf, 0x3c, 0x6f, 0xbf, 0x67, 0x62, 0xd0, 0x18, 0x8c, 0x9c, 0xc1, 0x88, 0x63, 0x36, 0xc2, 0x6c, + 0x7a, 0x48, 0x7a, 0x0e, 0x17, 0x48, 0x60, 0x3b, 0x61, 0x54, 0x50, 0x58, 0xf3, 0xa9, 0x3f, 0x60, + 0x14, 0xf9, 0x7d, 0x7b, 0x30, 0xb2, 0x73, 0x90, 0xcd, 0x05, 0x65, 0x28, 0xc4, 0x49, 0x6f, 0xef, + 0x49, 0x76, 0x74, 0x70, 0x1c, 0x92, 0x18, 0x27, 0x3d, 0x67, 0x38, 0xf2, 0x7d, 0xcd, 0xde, 0x7b, + 0xa2, 0x98, 0x49, 0xcf, 0x21, 0xb1, 0xc0, 0x2c, 0x46, 0x91, 0xc7, 0xd0, 0xb9, 0xc8, 0x9c, 0x3b, + 0xb9, 0x73, 0x88, 0x05, 0x0a, 0x90, 0x40, 0x99, 0x1d, 0xe6, 0xf6, 0x39, 0x9b, 0x95, 0x0a, 0x12, + 0x39, 0xfd, 0xc8, 0x77, 0x04, 0x19, 0x62, 0x2e, 0xd0, 0x30, 0xc9, 0x3c, 0xff, 0x0f, 0x69, 0x48, + 0xd5, 0xd1, 0x91, 0x27, 0x6d, 0x6d, 0xfc, 0xba, 0x0a, 0xca, 0x2e, 0x4e, 0x22, 0xe2, 0xa3, 0x33, + 0x59, 0x0d, 0x7c, 0x06, 0xa0, 0x0c, 0xed, 0xa1, 0x24, 0x89, 0x08, 0x0e, 0x3c, 0x12, 0x07, 0xf8, + 0xc2, 0x32, 0xea, 0x46, 0xd3, 0x74, 0x2b, 0xd2, 0xb3, 0xaf, 0x1d, 0x6d, 0x69, 0x87, 0x36, 0xd8, + 0x8a, 0x30, 0xe2, 0x78, 0x09, 0xfe, 0x3f, 0x05, 0x7f, 0xa4, 0x5c, 0x0b, 0xf8, 0x4f, 0x81, 0x19, + 0x60, 0xee, 0x5b, 0x85, 0xba, 0xd1, 0x2c, 0x3d, 0x6f, 0xd8, 0xb3, 0xa6, 0x65, 0xb5, 0xd8, 0x2e, + 0x8a, 0x43, 0x7c, 0x88, 0xb9, 0xcf, 0x48, 0x22, 0x28, 0x73, 0x15, 0x1e, 0xda, 0x60, 0x55, 0x5d, + 0x66, 0x99, 0x8a, 0x68, 0xdd, 0x41, 0x3c, 0x92, 0x7e, 0x57, 0xc3, 0xe0, 0xb7, 0x60, 0x53, 0xb0, + 0x34, 0xf6, 0x91, 0xc0, 0x81, 0xa7, 0xc6, 0x64, 0xad, 0x2a, 0xe6, 0x7b, 0x77, 0x86, 0x3c, 0x17, + 0xdd, 0x1c, 0xad, 0xba, 0xe0, 0x6e, 0x88, 0x85, 0xdf, 0xf0, 0x14, 0x94, 0x43, 0xdf, 0x13, 0x7d, + 0x86, 0x79, 0x9f, 0x46, 0x81, 0xf5, 0x40, 0x5d, 0xf6, 0x74, 0xee, 0x32, 0xd9, 0x77, 0xbb, 0x1f, + 0xf9, 0x76, 0x37, 0xef, 0x7b, 0x6b, 0x73, 0x32, 0xae, 0x95, 0xbe, 0x3e, 0xe8, 0xe6, 0x2c, 0xb7, + 0x14, 0xfa, 0xd3, 0x1f, 0xf0, 0x0b, 0xb0, 0x2a, 0x13, 0xe3, 0xd6, 0xc3, 0x5b, 0x89, 0x65, 0x4a, + 0xb1, 0x73, 0xa5, 0xd8, 0xc7, 0x2f, 0x0f, 0x0e, 0x64, 0x22, 0xdc, 0xd5, 0x1c, 0xd8, 0x01, 0x8d, + 0x00, 0x27, 0x0c, 0xeb, 0x02, 0x53, 0x4e, 0xe2, 0x70, 0x3a, 0x02, 0x55, 0xae, 0x37, 0xc0, 0x97, + 0x56, 0xa9, 0x6e, 0x34, 0x8b, 0x6e, 0x75, 0x86, 0xfc, 0x4e, 0x02, 0xb3, 0x81, 0xa8, 0xc2, 0xbe, + 0xc1, 0x97, 0xf0, 0x13, 0xf0, 0x70, 0x84, 0x19, 0x27, 0x34, 0xb6, 0xca, 0x2a, 0x95, 0xbd, 0x3b, + 0x7a, 0xf4, 0x52, 0x23, 0xdc, 0x1c, 0x0a, 0xbf, 0x07, 0xdb, 0x4a, 0x27, 0x7e, 0x44, 0x39, 0x0e, + 0xbc, 0xa9, 0xda, 0xac, 0xf5, 0xfb, 0xb4, 0xc6, 0xbc, 0x1a, 0xd7, 0x56, 0xdc, 0x2d, 0x79, 0xc3, + 0x81, 0xba, 0x60, 0xea, 0xfa, 0xdc, 0xfc, 0xf3, 0xe7, 0x9a, 0xd1, 0x31, 0x8b, 0xc5, 0xca, 0x5a, + 0xc7, 0x2c, 0xae, 0x55, 0x40, 0xc7, 0x2c, 0x82, 0x4a, 0xa9, 0xf1, 0xd7, 0x43, 0xb0, 0xa6, 0xc4, + 0xd1, 0x8e, 0xcf, 0x29, 0x3c, 0xd6, 0xdd, 0xc3, 0x4a, 0x99, 0xa5, 0xe7, 0x1f, 0xda, 0x6f, 0x59, + 0x3f, 0x7b, 0x5e, 0xe4, 0xad, 0xa2, 0x0c, 0x7f, 0x3d, 0xae, 0x19, 0xba, 0x9f, 0x18, 0x3e, 0x05, + 0x20, 0x42, 0x5c, 0x2c, 0xc8, 0x77, 0x4d, 0x5a, 0xb4, 0x6c, 0x6b, 0xa0, 0x14, 0xa7, 0x43, 0x2f, + 0xc1, 0x71, 0x40, 0xe2, 0x50, 0xa9, 0xd7, 0x74, 0x41, 0x9c, 0x0e, 0x4f, 0xb4, 0x25, 0x07, 0x04, + 0x8c, 0x26, 0x09, 0x0e, 0x94, 0xd6, 0x34, 0xe0, 0x50, 0x5b, 0x60, 0x03, 0xac, 0xab, 0x76, 0x45, + 0x34, 0xf4, 0x38, 0x79, 0x8d, 0x95, 0x82, 0x0a, 0x6e, 0x49, 0x1a, 0x8f, 0x68, 0x78, 0x46, 0x5e, + 0x63, 0xf8, 0x51, 0xd6, 0xd2, 0x1c, 0xe3, 0x09, 0x96, 0x72, 0x81, 0x03, 0x0b, 0xa8, 0x39, 0xc2, + 0x39, 0x6c, 0x57, 0x7b, 0xe0, 0x97, 0x60, 0x0f, 0x25, 0x09, 0xa3, 0x17, 0x64, 0x28, 0x87, 0x9e, + 0x30, 0x9a, 0x50, 0x8e, 0x22, 0xef, 0x55, 0x4a, 0x05, 0x52, 0xca, 0x2a, 0xb8, 0xd6, 0x1c, 0xe2, + 0x24, 0x03, 0x9c, 0x4a, 0x3f, 0xfc, 0x0c, 0x3c, 0x5e, 0x64, 0x78, 0x3d, 0xb9, 0xcb, 0xba, 0x09, + 0x1b, 0x8a, 0xbc, 0x93, 0xcc, 0x33, 0x5a, 0x88, 0x63, 0xdd, 0x91, 0xaf, 0xc0, 0x3b, 0x4b, 0x54, + 0x86, 0xf5, 0x4b, 0xf0, 0x2a, 0xc5, 0x29, 0xb6, 0x36, 0xeb, 0x85, 0x66, 0xc1, 0x7d, 0xbc, 0xc0, + 0x76, 0x35, 0xe2, 0x54, 0x02, 0xe0, 0xfb, 0x60, 0x93, 0xc9, 0x69, 0x7a, 0x43, 0x74, 0xe1, 0xf5, + 0x2e, 0x05, 0xe6, 0x56, 0x51, 0x45, 0x5c, 0x57, 0xe6, 0x63, 0x74, 0xd1, 0x92, 0x46, 0xf8, 0x23, + 0xd8, 0x45, 0xbe, 0x20, 0x23, 0x7c, 0x5b, 0x69, 0xe5, 0xfb, 0x2b, 0x6d, 0x5b, 0xdf, 0xb1, 0xa4, + 0x35, 0xf8, 0x02, 0xec, 0xaa, 0x68, 0xe7, 0x18, 0x07, 0x1e, 0xc3, 0x21, 0xe1, 0x82, 0x21, 0x41, + 0x68, 0xcc, 0x95, 0x8c, 0x0b, 0xee, 0xce, 0xd4, 0xed, 0xce, 0x7b, 0xe1, 0x07, 0x60, 0x4d, 0xe0, + 0x18, 0xc5, 0xc2, 0x23, 0x81, 0x55, 0x91, 0xd3, 0x6e, 0x95, 0x27, 0xe3, 0x5a, 0xb1, 0xab, 0x8c, + 0xed, 0x43, 0xb7, 0xa8, 0xdd, 0xed, 0x00, 0x62, 0xb0, 0xbb, 0x9c, 0xb9, 0x97, 0xd0, 0x88, 0xf8, + 0x97, 0x16, 0xac, 0x1b, 0xcd, 0x8d, 0x05, 0xed, 0x2e, 0xbc, 0x82, 0x4b, 0xd9, 0x9e, 0x28, 0x92, + 0xbb, 0xed, 0xdf, 0x65, 0x86, 0xbf, 0x19, 0xe0, 0xdd, 0x5b, 0x71, 0x38, 0x09, 0xb0, 0x60, 0x28, + 0xe6, 0x09, 0x65, 0x52, 0xdc, 0xe7, 0xd4, 0xda, 0x52, 0x4d, 0x7b, 0xf1, 0xf6, 0x7d, 0x91, 0x19, + 0x9c, 0x91, 0x00, 0x77, 0x73, 0xbe, 0xdc, 0xbb, 0x56, 0x53, 0xb6, 0x73, 0x32, 0xae, 0xd5, 0x97, + 0x92, 0xbb, 0x85, 0x74, 0xeb, 0xfe, 0x6d, 0x84, 0x98, 0x47, 0x4c, 0x37, 0xdd, 0xac, 0xac, 0x4e, + 0x37, 0xbd, 0x54, 0x29, 0x77, 0xcc, 0xe2, 0xa3, 0x0a, 0x6c, 0xfc, 0x52, 0x00, 0x3b, 0x77, 0x27, + 0x01, 0x3b, 0x60, 0x83, 0xe9, 0x75, 0xce, 0x44, 0x91, 0xbd, 0x02, 0xf7, 0x92, 0xc2, 0x7a, 0x46, + 0xd5, 0x05, 0xc0, 0x14, 0x94, 0xf2, 0xbb, 0x22, 0x44, 0xd4, 0xea, 0x17, 0x5a, 0xdd, 0xc9, 0xb8, + 0x06, 0xb2, 0x17, 0xe3, 0x68, 0xbf, 0xfd, 0xf7, 0xb8, 0xd6, 0x0a, 0x89, 0xe8, 0xa7, 0x3d, 0xdb, + 0xa7, 0x43, 0x67, 0x1a, 0x24, 0xe8, 0xcd, 0xce, 0x4e, 0x32, 0x08, 0x9d, 0xf9, 0xef, 0x04, 0x9d, + 0x99, 0xe0, 0x8e, 0x2f, 0xe4, 0xbf, 0xd3, 0x7e, 0xdb, 0x05, 0x59, 0xa0, 0x23, 0x44, 0x64, 0x09, + 0x3e, 0x8e, 0x05, 0x43, 0x51, 0x5e, 0x42, 0xe1, 0x5f, 0x94, 0x90, 0x51, 0x67, 0x25, 0xe4, 0x77, + 0xc9, 0x12, 0xcc, 0x59, 0x09, 0x07, 0xda, 0xfc, 0x1f, 0x96, 0x90, 0x05, 0x3a, 0x42, 0x44, 0x8f, + 0xaf, 0xf5, 0xec, 0xea, 0x8f, 0xea, 0xca, 0xd5, 0xa4, 0x6a, 0x5c, 0x4f, 0xaa, 0xc6, 0x9b, 0x49, + 0xd5, 0xf8, 0x7d, 0x52, 0x35, 0x7e, 0xba, 0xa9, 0xae, 0x5c, 0xdf, 0x54, 0x57, 0xde, 0xdc, 0x54, + 0x57, 0x7e, 0x00, 0xb3, 0x2f, 0xa6, 0xde, 0x03, 0xf5, 0xd5, 0xf1, 0xf1, 0x3f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x6e, 0xa8, 0xa8, 0x9c, 0x52, 0x09, 0x00, 0x00, } func (this *ReplicaState) Equal(that interface{}) bool { @@ -361,6 +398,9 @@ func (this *ReplicaState) Equal(that interface{}) bool { if !this.Stats.Equal(that1.Stats) { return false } + if this.DeprecatedUsingAppliedStateKey != that1.DeprecatedUsingAppliedStateKey { + return false + } if !this.Version.Equal(that1.Version) { return false } @@ -515,6 +555,16 @@ func (m *ReplicaState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x62 } + if m.DeprecatedUsingAppliedStateKey { + i-- + if m.DeprecatedUsingAppliedStateKey { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } if m.Stats != nil { { size, err := m.Stats.MarshalToSizedBuffer(dAtA[:i]) @@ -822,6 +872,9 @@ func (m *ReplicaState) Size() (n int) { l = m.Stats.Size() n += 1 + l + sovState(uint64(l)) } + if m.DeprecatedUsingAppliedStateKey { + n += 2 + } if m.Version != nil { l = m.Version.Size() n += 1 + l + sovState(uint64(l)) @@ -1158,6 +1211,26 @@ func (m *ReplicaState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedUsingAppliedStateKey", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowState + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DeprecatedUsingAppliedStateKey = bool(v != 0) case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) diff --git a/pkg/kv/kvserver/kvserverpb/state.proto b/pkg/kv/kvserver/kvserverpb/state.proto index 857873a119ff..8f484529721f 100644 --- a/pkg/kv/kvserver/kvserverpb/state.proto +++ b/pkg/kv/kvserver/kvserverpb/state.proto @@ -59,6 +59,41 @@ message ReplicaState { // not be served. util.hlc.Timestamp gc_threshold = 6 [(gogoproto.customname) = "GCThreshold"]; storage.enginepb.MVCCStats stats = 7; + // DeprecatedUsingAppliedStateKey was previously used to check whether the + // Range had upgraded to begin using the RangeAppliedState key. When set to + // true, replicas receiving the state (through a ReplicatedEvalResult) knew to + // begin using the new key. In #58088 (21.1) we introduced a migration to + // iterate through all ranges in the system and have them start using the + // RangeAppliedState key. This field was always set to true in 21.2, nodes + // nodes were already using the RangeAppliedState key, or receiving messages + // from 21.1 nodes that were also already using the RangeAppliedState key. In + // 21.2 (and in 21.1 for that matter) we no longer needed to trigger the "if + // set to true in an incoming message, upgrade to start using the + // RangeAppliedState key" code path. + // + // When looking to get rid of this field in 22.1 (#70464), we observed that + // there was an unintentional read of this field in 21.2 nodes (#72029). The + // saga is as follows: + // - Removing this field in 22.1 meant it was set as false when received at + // 21.2 nodes. + // - This should've been fine! We weren't using this field to trigger any + // upgrade code paths (like it was originally intended for). + // - It turns out that in 21.2 we were using the ReplicaState from the + // incoming snapshot to update our in-memory replica state + // - Because the proto field was being phased out, there was now a divergence + // between the on-disk state (field set to true, from earlier 21.2 + // operations) and the in-memory state (field set to false, because sent + // from a version that attempted to get rid of this field). + // + // Removing proto fields from the replica state are not possible until we stop + // using the protobuf copy of the replica state when applying a snapshot + // (#72222). Once that's done, we should be able to stop sending the replica + // state as part of the snapshot in the subsequent release. + // + // TODO(irfansharif): Remove this field in the release after the one where we + // stop consulting the protobuf copy of the replica state when applying a + // snapshot. + bool deprecated_using_applied_state_key = 11; // Version tells us which migrations can be assumed to have run against this // particular replica. When we introduce backwards incompatible changes to the // replica state (for example using the unreplicated truncated state instead @@ -99,7 +134,7 @@ message ReplicaState { // "follower reads" at or below this timestamp. util.hlc.Timestamp raft_closed_timestamp = 13 [(gogoproto.nullable) = false]; - reserved 8, 9, 10, 11; + reserved 8, 9, 10; } // RangeInfo is used for reporting status information about a range out through diff --git a/pkg/kv/kvserver/replica.go b/pkg/kv/kvserver/replica.go index 798f5a9f59b0..8adb15ddad5f 100644 --- a/pkg/kv/kvserver/replica.go +++ b/pkg/kv/kvserver/replica.go @@ -1202,6 +1202,11 @@ func (r *Replica) assertStateRaftMuLockedReplicaMuRLocked( if err != nil { log.Fatalf(ctx, "%v", err) } + + // We don't care about this field; see comment on + // DeprecatedUsingAppliedStateKey for more details. This can be removed once + // we stop loading the replica state from snapshot protos. + diskState.DeprecatedUsingAppliedStateKey = r.mu.state.DeprecatedUsingAppliedStateKey if !diskState.Equal(r.mu.state) { // The roundabout way of printing here is to expose this information in sentry.io. // diff --git a/pkg/kv/kvserver/replica_command.go b/pkg/kv/kvserver/replica_command.go index cf7a9db9a649..d5abccc0198e 100644 --- a/pkg/kv/kvserver/replica_command.go +++ b/pkg/kv/kvserver/replica_command.go @@ -2451,6 +2451,10 @@ func (r *Replica) sendSnapshot( Term: snap.RaftSnap.Metadata.Term, } + // See comment on DeprecatedUsingAppliedStateKey for why we need to set this + // explicitly for snapshots going out to followers. + snap.State.DeprecatedUsingAppliedStateKey = true + req := SnapshotRequest_Header{ State: snap.State, DeprecatedUnreplicatedTruncatedState: true, diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 68af0e53c8ef..c8f8fd4e0a6a 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -941,6 +941,10 @@ func SendEmptySnapshot( if err != nil { return err } + // See comment on DeprecatedUsingAppliedStateKey for why we need to set this + // explicitly for snapshots going out to followers. + state.DeprecatedUsingAppliedStateKey = true + hs, err := sl.LoadHardState(ctx, eng) if err != nil { return err From 936d1574d81476961ede7790c2b2db9f8179e789 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Fri, 29 Oct 2021 15:03:28 -0500 Subject: [PATCH 122/205] ci: make sure `check generated files` output is dumped if check fails Closes #72245. Release note: None --- build/bazelutil/check.sh | 2 +- build/teamcity-check-genfiles.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/bazelutil/check.sh b/build/bazelutil/check.sh index 324f84df5485..c46962b04157 100755 --- a/build/bazelutil/check.sh +++ b/build/bazelutil/check.sh @@ -75,7 +75,7 @@ git grep '//go:generate' -- './*.go' | grep -v stringer | grep -v 'add-leaktest\ echo "$LINE" echo 'Please ensure that the equivalent logic to generate these files is' echo 'present in the Bazel build as well, then add the line to the' - echo 'EXISTING_GO_GENERATE_COMMENTS in build/bazelutil/check-genfiles.sh.' + echo 'EXISTING_GO_GENERATE_COMMENTS in build/bazelutil/check.sh.' echo 'Also see https://cockroachlabs.atlassian.net/wiki/spaces/CRDB/pages/1380090083/How+to+ensure+your+code+builds+with+Bazel' exit 1 done diff --git a/build/teamcity-check-genfiles.sh b/build/teamcity-check-genfiles.sh index 8e20639e114d..f6cef346b6f4 100755 --- a/build/teamcity-check-genfiles.sh +++ b/build/teamcity-check-genfiles.sh @@ -22,7 +22,7 @@ tc_prepare tc_start_block "Ensure generated code is up-to-date" # Buffer noisy output and only print it on failure. -if ! run run_bazel build/bazelutil/check.sh &> artifacts/buildshort.log || (cat artifacts/buildshort.log && false); then +if ! (run run_bazel build/bazelutil/check.sh &> artifacts/buildshort.log || (cat artifacts/buildshort.log && false)); then # The command will output instructions on how to fix the error. exit 1 fi From 8024194284cf4fb7fdd1b9b292cd4f152191372c Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Fri, 29 Oct 2021 14:31:24 -0400 Subject: [PATCH 123/205] internal/issues: remove author CC from Github issue Previously, automatically created issues would CC the author of the test. In many cases, tests can fail for reasons outside of the author's domain of knowledge, and incorrect CC's create unnecessary noise. Mentioning the team that owns a test is sufficient for routing an issue to the most appropriate subset of engineers. Release note: None --- pkg/cmd/github-post/main.go | 39 +++--------- pkg/cmd/github-post/main_test.go | 16 ----- pkg/cmd/internal/issues/issues.go | 63 ------------------- pkg/cmd/internal/issues/issues_test.go | 23 +------ .../failure-matching-and-related-issue.txt | 5 +- .../testdata/post/failure-matching-issue.txt | 5 +- .../issues/testdata/post/failure-no-issue.txt | 5 +- .../testdata/post/failure-related-issue.txt | 5 +- ...re-with-url-matching-and-related-issue.txt | 5 +- .../post/failure-with-url-matching-issue.txt | 5 +- .../post/failure-with-url-no-issue.txt | 5 +- .../post/failure-with-url-related-issue.txt | 5 +- .../post/fatal-matching-and-related-issue.txt | 5 +- .../testdata/post/fatal-matching-issue.txt | 5 +- .../issues/testdata/post/fatal-no-issue.txt | 5 +- .../testdata/post/fatal-related-issue.txt | 5 +- .../post/panic-matching-and-related-issue.txt | 5 +- .../testdata/post/panic-matching-issue.txt | 5 +- .../issues/testdata/post/panic-no-issue.txt | 5 +- .../testdata/post/panic-related-issue.txt | 5 +- .../rsg-crash-matching-and-related-issue.txt | 5 +- .../post/rsg-crash-matching-issue.txt | 5 +- .../testdata/post/rsg-crash-no-issue.txt | 5 +- .../testdata/post/rsg-crash-related-issue.txt | 5 +- ...h-artifacts-matching-and-related-issue.txt | 5 +- .../post/with-artifacts-matching-issue.txt | 5 +- .../testdata/post/with-artifacts-no-issue.txt | 5 +- .../post/with-artifacts-related-issue.txt | 5 +- 28 files changed, 56 insertions(+), 205 deletions(-) diff --git a/pkg/cmd/github-post/main.go b/pkg/cmd/github-post/main.go index f8a9564c1456..6a2b220349dd 100644 --- a/pkg/cmd/github-post/main.go +++ b/pkg/cmd/github-post/main.go @@ -47,7 +47,7 @@ const ( type formatter func(context.Context, failure) (issues.IssueFormatter, issues.PostRequest) func defaultFormatter(ctx context.Context, f failure) (issues.IssueFormatter, issues.PostRequest) { - teams, authorEmail := getOwner(ctx, f.packageName, f.testName) + teams := getOwner(ctx, f.packageName, f.testName) repro := fmt.Sprintf("make stressrace TESTS=%s PKG=./pkg/%s TESTTIMEOUT=5m STRESSFLAGS='-timeout 5m' 2>&1", f.testName, trimPkg(f.packageName)) @@ -64,7 +64,6 @@ func defaultFormatter(ctx context.Context, f failure) (issues.IssueFormatter, is PackageName: f.packageName, Message: f.testMessage, Artifacts: "/", // best we can do for unit tests - AuthorEmail: authorEmail, HelpCommand: func(r *issues.Renderer) { issues.ReproductionCommandFromString(repro) r.Escaped("See also: ") @@ -518,44 +517,20 @@ func getFileLine( } // getOwner looks up the file containing the given test and returns -// the owning teams as well as the author email. It does not return +// the owning teams. It does not return // errors, but instead simply returns what it can. -func getOwner( - ctx context.Context, packageName, testName string, -) (_teams []team.Team, _authorEmail string) { - filename, linenum, err := getFileLine(ctx, packageName, testName) +func getOwner(ctx context.Context, packageName, testName string) (_teams []team.Team) { + filename, _, err := getFileLine(ctx, packageName, testName) if err != nil { log.Printf("getting file:line for %s.%s: %s", packageName, testName, err) - return nil, "" + return nil } - authorEmail, _ := getAuthorEmail(ctx, filename, linenum) co, err := codeowners.DefaultLoadCodeOwners() if err != nil { log.Printf("loading codeowners: %s", err) - return nil, authorEmail - } - return co.Match(filename), authorEmail -} - -func getAuthorEmail(ctx context.Context, filename, linenum string) (string, error) { - // Run git blame. - cmd := exec.Command(`/bin/bash`, `-c`, - fmt.Sprintf(`cd "$(git rev-parse --show-toplevel)" && git blame --porcelain -L%s,+1 '%s' | grep author-mail`, - linenum, filename)) - // This command returns output such as: - // author-mail - out, err := cmd.CombinedOutput() - if err != nil { - return "", errors.Errorf("couldn't find author of %s:%s: %s\n%s", - filename, linenum, err, string(out)) - } - re := regexp.MustCompile("author-mail <(.*)>") - matches := re.FindSubmatch(out) - if matches == nil { - return "", errors.Errorf("couldn't find author email of %s:%s: %s", - filename, linenum, string(out)) + return nil } - return string(matches[1]), nil + return co.Match(filename) } func formatPebbleMetamorphicIssue( diff --git a/pkg/cmd/github-post/main_test.go b/pkg/cmd/github-post/main_test.go index bc6e6c727846..21582f73943f 100644 --- a/pkg/cmd/github-post/main_test.go +++ b/pkg/cmd/github-post/main_test.go @@ -27,7 +27,6 @@ func TestListFailures(t *testing.T) { testName string title string message string - author string expRepro string mention []string hasProject bool @@ -48,7 +47,6 @@ func TestListFailures(t *testing.T) { testName: "TestStopperWithCancelConcurrent", title: "util/stop: TestStopperWithCancelConcurrent failed", message: "this is just a testing issue", - author: "nvanbenschoten@gmail.com", mention: []string{"@cockroachdb/server"}, hasProject: true, }}, @@ -62,7 +60,6 @@ func TestListFailures(t *testing.T) { testName: "TestReplicateQueueRebalance", title: "kv/kvserver: TestReplicateQueueRebalance failed", message: "replicate_queue_test.go:88: condition failed to evaluate within 45s: not balanced: [10 1 10 1 8]", - author: "petermattis@gmail.com", mention: []string{"@cockroachdb/kv"}, hasProject: true, }}, @@ -76,7 +73,6 @@ func TestListFailures(t *testing.T) { testName: "TestGossipHandlesReplacedNode", title: "kv/kvserver: TestGossipHandlesReplacedNode failed", message: "F180711 20:13:15.826193 83 storage/replica.go:1877 [n?,s1,r1/1:/M{in-ax}] on-disk and in-memory state diverged:", - author: "alexdwanerobinson@gmail.com", mention: []string{"@cockroachdb/kv"}, hasProject: true, }}, @@ -90,7 +86,6 @@ func TestListFailures(t *testing.T) { testName: "(unknown)", title: "storage: package failed", message: "make: *** [bin/.submodules-initialized] Error 1", - author: "", }}, formatter: defaultFormatter, }, @@ -104,7 +99,6 @@ func TestListFailures(t *testing.T) { message: `=== RUN TestPretty/["hello",_["world"]] --- FAIL: TestPretty/["hello",_["world"]] (0.00s) json_test.go:1656: injected failure`, - author: "justin@cockroachlabs.com", mention: []string{"@cockroachdb/unowned"}, }}, formatter: defaultFormatter, @@ -121,7 +115,6 @@ func TestListFailures(t *testing.T) { testName: "TestTxnCoordSenderPipelining", title: "kv/kvclient/kvcoord: TestTxnCoordSenderPipelining failed", message: `injected failure`, - author: "nikhil.benesch@gmail.com", mention: []string{"@cockroachdb/kv"}, hasProject: true, }, @@ -135,7 +128,6 @@ TestTxnCoordSenderPipelining - 1.00s Slow passing tests: TestAnchorKey - 1.01s `, - author: "andrei@cockroachlabs.com", mention: []string{"@cockroachdb/kv"}, hasProject: true, }, @@ -159,7 +151,6 @@ TestXXX/sub3 - 0.50s Slow passing tests: TestXXA - 1.00s `, - author: "", }, }, formatter: defaultFormatter, @@ -181,7 +172,6 @@ Slow passing tests: TestXXB - 1.01s TestXXA - 1.00s `, - author: "", }, }, formatter: defaultFormatter, @@ -203,7 +193,6 @@ Slow passing tests: TestXXB - 1.01s TestXXA - 1.00s `, - author: "", }, }, formatter: defaultFormatter, @@ -218,7 +207,6 @@ TestXXA - 1.00s testName: "TestXXX", title: "kv: TestXXX failed", message: `panic: induced panic`, - author: "", }, }, formatter: defaultFormatter, @@ -233,7 +221,6 @@ TestXXA - 1.00s testName: "(unknown)", title: "kv: package failed", message: `panic: induced panic`, - author: "", }, }, formatter: defaultFormatter, @@ -282,9 +269,6 @@ TestXXA - 1.00s if exp := c.expIssues[curIssue].testName; exp != f.testName { t.Errorf("expected test name %s, but got %s", exp, f.testName) } - if exp := c.expIssues[curIssue].author; exp != "" && exp != req.AuthorEmail { - t.Errorf("expected author %s, but got %s", exp, req.AuthorEmail) - } if exp := c.expIssues[curIssue].title; exp != f.title { t.Errorf("expected title %s, but got %s", exp, f.title) } diff --git a/pkg/cmd/internal/issues/issues.go b/pkg/cmd/internal/issues/issues.go index 402e9f50e46e..7cbc0ce2ab5c 100644 --- a/pkg/cmd/internal/issues/issues.go +++ b/pkg/cmd/internal/issues/issues.go @@ -57,21 +57,6 @@ var ( searchLabel = issueLabels[1] ) -// Replace resolved AuthorGithubHandles according to this map. -// Helpful to avoid pinging former employees. The zero value -// pings nobody. -var oldFriendsMap = map[string]string{ - "a-robinson": "andreimatei", - "benesch": "nvanbenschoten", - "georgeutsin": "yuzefovich", - "tamird": "tbg", - "rohany": "solongordon", - "vivekmenezes": "", - "lucy-zhang": "ajwerner", - "mjibson": "rafiss", - "danhhz": "", -} - // context augments context.Context with a logger. type postCtx struct { context.Context @@ -85,42 +70,6 @@ func (ctx *postCtx) Printf(format string, args ...interface{}) { fmt.Fprintf(&ctx.Builder, format, args...) } -func (p *poster) getAuthorGithubHandle(ctx *postCtx, authorEmail string) string { - if authorEmail == "" { - return "" - } - commits, _, err := p.listCommits(ctx, p.Org, p.Repo, &github.CommitsListOptions{ - Author: authorEmail, - ListOptions: github.ListOptions{ - PerPage: 1, - }, - }) - if err != nil { - ctx.Printf("unable list commits by %s: %v", authorEmail, err) - return "" - } - if len(commits) == 0 { - ctx.Printf("no GitHub commits found for email %s", authorEmail) - return "" - } - - if commits[0].Author == nil { - ctx.Printf("no Author found for user email %s", authorEmail) - return "" - } - authorHandle := *commits[0].Author.Login - - if newAuthorHandle, ok := oldFriendsMap[authorHandle]; ok { - if newAuthorHandle == "" { - ctx.Printf("%s marked as alumn{us,a}; ignoring", authorHandle) - return "" - } - ctx.Printf("%s marked as alumn{us/a}; resolving to %s instead", authorHandle, newAuthorHandle) - return newAuthorHandle - } - return authorHandle -} - func getLatestTag() (string, error) { cmd := exec.Command("git", "describe", "--abbrev=0", "--tags", "--match=v[0-9]*") out, err := cmd.CombinedOutput() @@ -311,18 +260,6 @@ func (p *poster) templateData( func (p *poster) post(origCtx context.Context, formatter IssueFormatter, req PostRequest) error { ctx := &postCtx{Context: origCtx} - - authorHandle := p.getAuthorGithubHandle(ctx, req.AuthorEmail) - if authorHandle != "" { - // This is intentionally missing an "@" because we don't want - // to ping former interns and employees (and haven't done the - // work to let this code here determine whether the author is - // still a member of the repo). We rely primarily on - // mentioning a team and adding to its project column. The - // author is only informative. - req.Mention = append(req.Mention, authorHandle) - } - data := p.templateData( ctx, req, diff --git a/pkg/cmd/internal/issues/issues_test.go b/pkg/cmd/internal/issues/issues_test.go index 873c9f4490ec..3b0018f88a4b 100644 --- a/pkg/cmd/internal/issues/issues_test.go +++ b/pkg/cmd/internal/issues/issues_test.go @@ -287,6 +287,7 @@ test logs left over in: /go/src/github.com/cockroachdb/cockroach/artifacts/logTe TestName: c.testName, Message: c.message, Artifacts: c.artifacts, + Mention: []string{"@nights-watch"}, AuthorEmail: c.author, HelpCommand: repro, ExtraLabels: []string{"release-blocker"}, @@ -347,34 +348,12 @@ func TestPostEndToEnd(t *testing.T) { PackageName: "github.com/cockroachdb/cockroach/pkg/foo/bar", TestName: "TestFooBarBaz", Message: "I'm a message", - AuthorEmail: "tobias.schottdorf@gmail.com", ExtraLabels: []string{"release-blocker"}, } require.NoError(t, Post(context.Background(), UnitTestFormatter, req)) } -func TestGetAssignee(t *testing.T) { - p := &poster{ - Options: &Options{ - Org: "myorg", - Repo: "myrepo", - }, - } - listCommits := func(_ context.Context, owner string, repo string, - opts *github.CommitsListOptions) ([]*github.RepositoryCommit, *github.Response, error) { - require.Equal(t, owner, p.Options.Org) - require.Equal(t, repo, p.Options.Repo) - return []*github.RepositoryCommit{ - {}, - }, nil, nil - } - p.listCommits = listCommits - ctx := &postCtx{Context: context.Background()} - require.Zero(t, p.getAuthorGithubHandle(ctx, "foo@bar.xy")) - require.Equal(t, "no Author found for user email foo@bar.xy\n", ctx.Builder.String()) -} - // setEnv overrides the env variables corresponding to the input map. The // returned closure restores the status quo. func setEnv(kv map[string]string) func() { diff --git a/pkg/cmd/internal/issues/testdata/post/failure-matching-and-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-matching-and-related-issue.txt index 5ef98d2f30dc..fc510c4bbf43 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-matching-and-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-matching-and-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] createComment owner=cockroachdb repo=cockroach issue=30: @@ -29,11 +28,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestReplicateQueueRebalance.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-matching-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-matching-issue.txt index e18b0d3486c6..ccc255fbee0d 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-matching-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-matching-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" -label:branch-release-0.1: [] createComment owner=cockroachdb repo=cockroach issue=30: @@ -23,11 +22,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestReplicateQueueRebalance.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-no-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-no-issue.txt index f3874cc5d95c..d968a10ac798 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-no-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-no-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" -label:branch-release-0.1: [] getLatestTag: result v3.3.0 @@ -28,11 +27,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestReplicateQueueRebalance.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestReplicateQueueRebalance+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestReplicateQueueRebalance+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-related-issue.txt index 66d292824265..fc662e73f5c9 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestReplicateQueueRebalance failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] getLatestTag: result v3.3.0 @@ -34,11 +33,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestReplicateQueueRebalance.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestReplicateQueueRebalance+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestReplicateQueueRebalance+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0A%09%3Cautogenerated%3E%3A12%3A+storage%2Freplicate_queue_test.go%3A103%2C+condition+failed+to+evaluate+within+45s%3A+not+balanced%3A+%5B10+1+10+1+8%5D%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestReplicateQueueRebalance+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestReplicateQueueRebalance.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestReplicateQueueRebalance+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-and-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-and-related-issue.txt index d34487e7b739..c22dbf923b08 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-and-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-and-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] createComment owner=cockroachdb repo=cockroach issue=30: @@ -28,11 +27,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*some-roachtest.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-issue.txt index 772aff0fa8aa..78dd48bb9ffd 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-with-url-matching-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" -label:branch-release-0.1: [] createComment owner=cockroachdb repo=cockroach issue=30: @@ -22,11 +21,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*some-roachtest.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-with-url-no-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-with-url-no-issue.txt index cc301479b2c7..0a5f7a6ec8a0 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-with-url-no-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-with-url-no-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" -label:branch-release-0.1: [] getLatestTag: result v3.3.0 @@ -27,11 +26,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*some-roachtest.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=cmd%2Froachtest%3A+some-roachtest+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=cmd%2Froachtest%3A+some-roachtest+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/failure-with-url-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/failure-with-url-related-issue.txt index 0e88b59a7088..3210d95021e1 100644 --- a/pkg/cmd/internal/issues/testdata/post/failure-with-url-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/failure-with-url-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "cmd/roachtest: some-roachtest failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] getLatestTag: result v3.3.0 @@ -33,11 +32,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*some-roachtest.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=cmd%2Froachtest%3A+some-roachtest+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=cmd%2Froachtest.some-roachtest+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0Aboom%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%0ASee%3A+%5BFooBar+README%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%29%0A%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Asome-roachtest.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=cmd%2Froachtest%3A+some-roachtest+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/fatal-matching-and-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/fatal-matching-and-related-issue.txt index b55d706cde58..3599b340e0d6 100644 --- a/pkg/cmd/internal/issues/testdata/post/fatal-matching-and-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/fatal-matching-and-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] createComment owner=cockroachdb repo=cockroach issue=30: @@ -47,11 +46,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/fatal-matching-issue.txt b/pkg/cmd/internal/issues/testdata/post/fatal-matching-issue.txt index 6626566bbb80..7afe2ab729c3 100644 --- a/pkg/cmd/internal/issues/testdata/post/fatal-matching-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/fatal-matching-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [] createComment owner=cockroachdb repo=cockroach issue=30: @@ -41,11 +40,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/fatal-no-issue.txt b/pkg/cmd/internal/issues/testdata/post/fatal-no-issue.txt index 45cb79cc942b..eb70f7d9893a 100644 --- a/pkg/cmd/internal/issues/testdata/post/fatal-no-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/fatal-no-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [] getLatestTag: result v3.3.0 @@ -46,11 +45,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/fatal-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/fatal-related-issue.txt index 7f1d090d42b8..1af68ae67aaf 100644 --- a/pkg/cmd/internal/issues/testdata/post/fatal-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/fatal-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] getLatestTag: result v3.3.0 @@ -52,11 +51,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0AF170517+07%3A33%3A43.763059+69575+storage%2Freplica.go%3A1360++%5Bn3%2Cs3%2Cr1%2F3%3A%2FM%7Bin-ax%7D%5D+something+bad+happened%3A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/panic-matching-and-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/panic-matching-and-related-issue.txt index 26741b30d580..2b4dbd3e7175 100644 --- a/pkg/cmd/internal/issues/testdata/post/panic-matching-and-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/panic-matching-and-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] createComment owner=cockroachdb repo=cockroach issue=30: @@ -48,11 +47,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/panic-matching-issue.txt b/pkg/cmd/internal/issues/testdata/post/panic-matching-issue.txt index 08f5efa88ada..6f57ae6a5746 100644 --- a/pkg/cmd/internal/issues/testdata/post/panic-matching-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/panic-matching-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [] createComment owner=cockroachdb repo=cockroach issue=30: @@ -42,11 +41,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/panic-no-issue.txt b/pkg/cmd/internal/issues/testdata/post/panic-no-issue.txt index 10da2dc8fff2..2b918a03acae 100644 --- a/pkg/cmd/internal/issues/testdata/post/panic-no-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/panic-no-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [] getLatestTag: result v3.3.0 @@ -47,11 +46,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/panic-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/panic-related-issue.txt index 2250291a0464..55c7f609bfeb 100644 --- a/pkg/cmd/internal/issues/testdata/post/panic-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/panic-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: TestGossipHandlesReplacedNode failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] getLatestTag: result v3.3.0 @@ -53,11 +52,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestGossipHandlesReplacedNode.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.TestGossipHandlesReplacedNode+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0AFatal+error%3A%0A%0A%60%60%60%0Apanic%3A+something+bad+happened%3A%0A%0Afoo%0Abar%0A%0A%60%60%60%0AStack%3A+%0A%0A%60%60%60%0Agoroutine+12+%5Brunning%5D%3A%0A++doing+something%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3ELog+preceding+fatal+error%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A%60%60%60%0Alogging+something%0A%60%60%60%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+stressrace+TESTS%3DTestGossipHandlesReplacedNode+PKG%3D.%2Fpkg%2Fstorage+TESTTIMEOUT%3D5m+STRESSFLAGS%3D%27-timeout+5m%27+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestGossipHandlesReplacedNode.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+TestGossipHandlesReplacedNode+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-and-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-and-related-issue.txt index 1640988cf908..4326acee2167 100644 --- a/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-and-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-and-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] createComment owner=cockroachdb repo=cockroach issue=30: @@ -56,11 +55,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestRandomSyntaxSQLSmith.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-issue.txt b/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-issue.txt index 140d9341dc73..cc6c8062b86e 100644 --- a/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/rsg-crash-matching-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" -label:branch-release-0.1: [] createComment owner=cockroachdb repo=cockroach issue=30: @@ -50,11 +49,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestRandomSyntaxSQLSmith.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/rsg-crash-no-issue.txt b/pkg/cmd/internal/issues/testdata/post/rsg-crash-no-issue.txt index 411f650f4da0..db6f584ab9f8 100644 --- a/pkg/cmd/internal/issues/testdata/post/rsg-crash-no-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/rsg-crash-no-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" -label:branch-release-0.1: [] getLatestTag: result v3.3.0 @@ -55,11 +54,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestRandomSyntaxSQLSmith.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=sql%2Ftests%3A+TestRandomSyntaxSQLSmith+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=sql%2Ftests%3A+TestRandomSyntaxSQLSmith+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/rsg-crash-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/rsg-crash-related-issue.txt index f2633fb644e7..c3e0c796499f 100644 --- a/pkg/cmd/internal/issues/testdata/post/rsg-crash-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/rsg-crash-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "sql/tests: TestRandomSyntaxSQLSmith failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] getLatestTag: result v3.3.0 @@ -61,11 +60,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*TestRandomSyntaxSQLSmith.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=sql%2Ftests%3A+TestRandomSyntaxSQLSmith+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=sql%2Ftests.TestRandomSyntaxSQLSmith+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0ARandom+syntax+error%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A755%3A+Crash+detected%3A+server+panic%3A+pq%3A+internal+error%3A+something+bad%0A%60%60%60%0AQuery%3A%0A%0A%60%60%60%0A%09%09SELECT%0A%09%09%09foo%0A%09%09FROM%0A%09%09%09bar%0A%09%09LIMIT%0A%09%09%0933%3A%3A%3AINT8%3B%0A%60%60%60%0ASchema%3A%0A%0A%60%60%60%0A++++rsg_test.go%3A575%3A+To+reproduce%2C+use+schema%3A%0A++++rsg_test.go%3A577%3A+%0A++++++++%09CREATE+TABLE+table1+%28col1_0+BOOL%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A577%3A+%0A++++++++%0A++++++++CREATE+TYPE+greeting+AS+ENUM+%28%27hello%27%2C+%27howdy%27%2C+%27hi%27%2C+%27good+day%27%2C+%27morning%27%29%3B%0A++++++++%3B%0A++++rsg_test.go%3A579%3A+%0A++++rsg_test.go%3A580%3A+--+test+log+scope+end+--%0Atest+logs+left+over+in%3A+%2Fgo%2Fsrc%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fartifacts%2FlogTestRandomSyntaxSQLSmith460792454%0A---+FAIL%3A+TestRandomSyntaxSQLSmith+%28300.69s%29%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0ATo+reproduce%2C+try%3A%0A%0A%60%60%60bash%0Amake+test+TESTS%3DTestRandomSyntaxSQLSmith+PKG%3D.%2Fpkg%2Fsql%2Ftests+2%3E%261%0A%60%60%60%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2ATestRandomSyntaxSQLSmith.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=sql%2Ftests%3A+TestRandomSyntaxSQLSmith+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-and-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-and-related-issue.txt index a9d7a30a13ad..beb27187e6f1 100644 --- a/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-and-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-and-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] createComment owner=cockroachdb repo=cockroach issue=30: @@ -24,11 +23,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*kv/splits/nodes=3/quiesce=true.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-issue.txt b/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-issue.txt index ca959ad4f59b..ebf319f6c088 100644 --- a/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/with-artifacts-matching-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" label:branch-release-0.1: [github.Issue{Number:30, Title:"boom", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.1"}]}] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" -label:branch-release-0.1: [] createComment owner=cockroachdb repo=cockroach issue=30: @@ -18,11 +17,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*kv/splits/nodes=3/quiesce=true.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=%3Ccomment%3E \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/with-artifacts-no-issue.txt b/pkg/cmd/internal/issues/testdata/post/with-artifacts-no-issue.txt index a06464e58d69..6c51edac735b 100644 --- a/pkg/cmd/internal/issues/testdata/post/with-artifacts-no-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/with-artifacts-no-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" -label:branch-release-0.1: [] getLatestTag: result v3.3.0 @@ -23,11 +22,11 @@ Parameters in this failure: - GOFLAGS=race

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*kv/splits/nodes=3/quiesce=true.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+failed \ No newline at end of file diff --git a/pkg/cmd/internal/issues/testdata/post/with-artifacts-related-issue.txt b/pkg/cmd/internal/issues/testdata/post/with-artifacts-related-issue.txt index 62c9b6c0858f..39c9a7830738 100644 --- a/pkg/cmd/internal/issues/testdata/post/with-artifacts-related-issue.txt +++ b/pkg/cmd/internal/issues/testdata/post/with-artifacts-related-issue.txt @@ -1,4 +1,3 @@ -listCommits owner=cockroachdb repo=cockroach github.CommitsListOptions{SHA:"", Path:"", Author:"bran", Since:time.Time{wall:, ext:}, Until:time.Time{wall:, ext:}, ListOptions:github.ListOptions{Page:0, PerPage:1}} searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" label:branch-release-0.1: [] searchIssue repo:"cockroach" user:"cockroachdb" is:issue is:open in:title label:"C-test-failure" sort:created-desc "storage: kv/splits/nodes=3/quiesce=true failed" -label:branch-release-0.1: [github.Issue{Number:31, Title:"boom related", Labels:[github.Label{URL:"fake", Name:"C-test-failure"} github.Label{URL:"fake", Name:"O-robot"} github.Label{URL:"fake", Name:"release-0.2"}]}] getLatestTag: result v3.3.0 @@ -29,11 +28,11 @@ Parameters in this failure: - #31 boom related [C-test-failure O-robot release-0.2]

-/cc hodor +/cc @nights-watch [This test on roachdash](https://roachdash.crdb.dev/?filter=status:open%20t:.*kv/splits/nodes=3/quiesce=true.*&sort=title+created&display=lastcommented+project) | [Improve this report!](https://github.com/cockroachdb/cockroach/tree/master/pkg/cmd/internal/issues) -Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+hodor%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+failed \ No newline at end of file +Rendered: https://github.com/cockroachdb/cockroach/issues/new?body=storage.kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+%5Bfailed%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3DbuildLog%29+with+%5Bartifacts%5D%28https%3A%2F%2Fteamcity.example.com%2FviewLog.html%3FbuildId%3D8008135%26tab%3Dartifacts%23%2Fkv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue%29+on+release-0.1+%40+%5Babcd123%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Fcommits%2Fabcd123%29%3A%0A%0A%0A%60%60%60%0AThe+test+failed+on+branch%3Dmaster%2C+cloud%3Dgce%3A%0A%60%60%60%0A%3Cdetails%3E%3Csummary%3EHelp%3C%2Fsummary%3E%0A%3Cp%3E%0AParameters+in+this+failure%3A%0A%0A-+TAGS%3Ddeadlock%0A%0A-+GOFLAGS%3Drace%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%3Cdetails%3E%3Csummary%3ESame+failure+on+other+branches%3C%2Fsummary%3E%0A%3Cp%3E%0A%0A-+%2331+boom+related+%5BC-test-failure+O-robot+release-0.2%5D%0A%3C%2Fp%3E%0A%3C%2Fdetails%3E%0A%2Fcc+%40nights-watch%0A%3Csub%3E%0A%0A%5BThis+test+on+roachdash%5D%28https%3A%2F%2Froachdash.crdb.dev%2F%3Ffilter%3Dstatus%3Aopen%2520t%3A.%2Akv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue.%2A%26sort%3Dtitle%2Bcreated%26display%3Dlastcommented%2Bproject%29+%7C+%5BImprove+this+report%21%5D%28https%3A%2F%2Fgithub.com%2Fcockroachdb%2Fcockroach%2Ftree%2Fmaster%2Fpkg%2Fcmd%2Finternal%2Fissues%29%0A%3C%2Fsub%3E%0A&title=storage%3A+kv%2Fsplits%2Fnodes%3D3%2Fquiesce%3Dtrue+failed \ No newline at end of file From a45418ff2b358accdd38edbc23965cc874e5f0c2 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Wed, 27 Oct 2021 11:21:57 -0400 Subject: [PATCH 124/205] roachtest: fix up `mockgen` usage w/ Bazel We were pointing to the checked-in generated files in the Bazel build here; instead, use `gomock`. Also clean up the `EXISTING_GO_GENERATE_COMMENTS` in `build/bazelutil/check.sh`. Release note: None --- BUILD.bazel | 5 +- build/bazelutil/check.sh | 16 +++---- pkg/cmd/roachtest/BUILD.bazel | 2 +- pkg/cmd/roachtest/prometheus/BUILD.bazel | 30 +++++++++--- .../roachtest/prometheus/mock_generated.go | 46 +++++++++---------- pkg/cmd/roachtest/prometheus/prometheus.go | 12 ++--- .../roachtest/prometheus/prometheus_test.go | 10 ++-- pkg/cmd/roachtest/tests/BUILD.bazel | 30 +++++++++--- pkg/cmd/roachtest/tests/drt.go | 5 +- pkg/cmd/roachtest/tests/drt_generated.go | 28 +++++------ pkg/cmd/roachtest/tests/drt_test.go | 4 +- 11 files changed, 111 insertions(+), 77 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 09bf0f4087de..18fe230a77e3 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -50,6 +50,8 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # tell gazelle to resolve to this second target instead. See # pkg/kv/kvclient/rangefeed/BUILD.bazel for an annotated example. # +# gazelle:resolve go github.com/cockroachdb/cockroach/pkg/cmd/roachtest/prometheus //pkg/cmd/roachtest/prometheus:with-mocks +# gazelle:resolve go github.com/cockroachdb/cockroach/pkg/cmd/roachtest/tests //pkg/cmd/roachtest/tests:with-mocks # gazelle:resolve go github.com/cockroachdb/cockroach/pkg/roachpb //pkg/roachpb:with-mocks # gazelle:resolve go github.com/cockroachdb/cockroach/pkg/kv/kvclient/kvcoord //pkg/kv/kvclient/kvcoord:with-mocks # gazelle:resolve go github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed //pkg/kv/kvclient/rangefeed:with-mocks @@ -57,7 +59,6 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # See pkg/roachpb/gen/BUILD.bazel for more details. # -# gazelle:resolve go github.com/cockroachdb/cockroach/pkg/roachpb //pkg/roachpb:with-mocks # gazelle:resolve proto go roachpb/api.proto //pkg/roachpb:with-mocks # gazelle:resolve proto go roachpb/app_stats.proto //pkg/roachpb:with-mocks # gazelle:resolve proto go roachpb/data.proto //pkg/roachpb:with-mocks @@ -117,6 +118,8 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # gazelle:exclude pkg/testutils/**/testdata/** # gazelle:exclude pkg/security/securitytest/embedded.go # gazelle:exclude pkg/cmd/roachprod/vm/aws/embedded.go +# gazelle:exclude pkg/cmd/roachtest/prometheus/mock_generated.go +# gazelle:exclude pkg/cmd/roachtest/tests/drt_generated.go # gazelle:exclude pkg/**/*_string.go # gazelle:exclude pkg/geo/wkt/wkt_generated.go # gazelle:exclude pkg/sql/schemachanger/scop/backfill_visitor_generated.go diff --git a/build/bazelutil/check.sh b/build/bazelutil/check.sh index 324f84df5485..ddd7efc5c331 100755 --- a/build/bazelutil/check.sh +++ b/build/bazelutil/check.sh @@ -3,24 +3,20 @@ set -euo pipefail EXISTING_GO_GENERATE_COMMENTS=" -pkg/ccl/sqlproxyccl/throttler/service.go://go:generate mockgen -package=throttler -destination=mocks_generated.go -source=service.go . Service -pkg/ccl/sqlproxyccl/denylist/service.go://go:generate mockgen -package=denylist -destination=mocks_generated.go -source=service.go . Service -pkg/ccl/sqlproxyccl/tenant/directory.go://go:generate mockgen -package=tenant -destination=mocks_generated.go . DirectoryClient,Directory_WatchEndpointsClient pkg/cmd/roachprod/vm/aws/config.go://go:generate go-bindata -mode 0600 -modtime 1400000000 -pkg aws -o embedded.go config.json old.json pkg/cmd/roachprod/vm/aws/config.go://go:generate gofmt -s -w embedded.go pkg/cmd/roachprod/vm/aws/config.go://go:generate goimports -w embedded.go pkg/cmd/roachprod/vm/aws/config.go://go:generate terraformgen -o terraform/main.tf -pkg/geo/wkt/wkt.go://go:generate sh generate.sh +pkg/cmd/roachtest/prometheus/prometheus.go://go:generate mockgen -package=prometheus -destination=mock_generated.go -source=prometheus.go . Cluster +pkg/cmd/roachtest/tests/drt.go://go:generate mockgen -source drt.go -package tests -destination drt_generated.go +pkg/kv/kvclient/kvcoord/transport.go://go:generate mockgen -package=kvcoord -destination=mocks_generated.go . Transport +pkg/kv/kvclient/rangecache/range_cache.go://go:generate mockgen -package=rangecache -destination=mocks_generated.go . RangeDescriptorDB +pkg/kv/kvclient/rangefeed/rangefeed.go://go:generate mockgen -package=rangefeed -source rangefeed.go -destination=mocks_generated.go . pkg/kv/kvserver/concurrency/lock_table.go://go:generate ../../../util/interval/generic/gen.sh *lockState concurrency pkg/kv/kvserver/spanlatch/manager.go://go:generate ../../../util/interval/generic/gen.sh *latch spanlatch +pkg/roachpb/api.go://go:generate mockgen -package=roachpb -destination=mocks_generated.go . InternalClient,Internal_RangeFeedClient pkg/roachpb/batch.go://go:generate go run -tags gen-batch gen/main.go pkg/security/certmgr/cert.go://go:generate mockgen -package=certmgr -destination=mocks_generated.go -source=cert.go . Cert -pkg/cmd/roachtest/prometheus/prometheus.go://go:generate mockgen -package=prometheus -destination=mock_generated.go -source=prometheus.go . cluster -pkg/kv/kvclient/rangecache/range_cache.go://go:generate mockgen -package=rangecache -destination=mocks_generated.go . RangeDescriptorDB -pkg/kv/kvclient/rangefeed/rangefeed.go://go:generate mockgen -package=rangefeed -source rangefeed.go -destination=mocks_generated.go . -pkg/kv/kvclient/kvcoord/transport.go://go:generate mockgen -package=kvcoord -destination=mocks_generated.go . Transport -pkg/roachpb/api.go://go:generate mockgen -package=roachpb -destination=mocks_generated.go . InternalClient,Internal_RangeFeedClient -pkg/cmd/roachtest/tests/drt.go://go:generate mockgen -source drt.go -package tests -destination drt_generated.go pkg/security/securitytest/securitytest.go://go:generate go-bindata -mode 0600 -modtime 1400000000 -pkg securitytest -o embedded.go -ignore README.md -ignore regenerate.sh test_certs pkg/security/securitytest/securitytest.go://go:generate gofmt -s -w embedded.go pkg/security/securitytest/securitytest.go://go:generate goimports -w embedded.go diff --git a/pkg/cmd/roachtest/BUILD.bazel b/pkg/cmd/roachtest/BUILD.bazel index f4bc62309ac5..89d5f70342e4 100644 --- a/pkg/cmd/roachtest/BUILD.bazel +++ b/pkg/cmd/roachtest/BUILD.bazel @@ -23,7 +23,7 @@ go_library( "//pkg/cmd/roachtest/registry", "//pkg/cmd/roachtest/spec", "//pkg/cmd/roachtest/test", - "//pkg/cmd/roachtest/tests", + "//pkg/cmd/roachtest/tests:with-mocks", "//pkg/internal/team", "//pkg/testutils/skip", "//pkg/util/contextutil", diff --git a/pkg/cmd/roachtest/prometheus/BUILD.bazel b/pkg/cmd/roachtest/prometheus/BUILD.bazel index 90f6a1a564e6..faf44c9fdfa9 100644 --- a/pkg/cmd/roachtest/prometheus/BUILD.bazel +++ b/pkg/cmd/roachtest/prometheus/BUILD.bazel @@ -1,17 +1,25 @@ +load("@bazel_gomock//:gomock.bzl", "gomock") load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( - name = "prometheus", - srcs = [ - "mock_generated.go", - "prometheus.go", - ], + name = "with-mocks", + srcs = [":prometheus_mocks"], + embed = [":prometheus"], importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/prometheus", visibility = ["//visibility:public"], + deps = [ + "@com_github_golang_mock//gomock", + ], +) + +go_library( + name = "prometheus", + srcs = ["prometheus.go"], + importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/prometheus", + visibility = ["//visibility:private"], deps = [ "//pkg/cmd/roachtest/logger", "//pkg/cmd/roachtest/option", - "@com_github_golang_mock//gomock", "@in_gopkg_yaml_v2//:yaml_v2", ], ) @@ -19,10 +27,18 @@ go_library( go_test( name = "prometheus_test", srcs = ["prometheus_test.go"], - embed = [":prometheus"], + embed = [":with-mocks"], # keep deps = [ "//pkg/cmd/roachtest/option", "@com_github_golang_mock//gomock", "@com_github_stretchr_testify//require", ], ) + +gomock( + name = "prometheus_mocks", + out = "mock_generated.go", + interfaces = ["Cluster"], + library = ":prometheus", + package = "prometheus", +) diff --git a/pkg/cmd/roachtest/prometheus/mock_generated.go b/pkg/cmd/roachtest/prometheus/mock_generated.go index 8ead0e937cb1..8e9c6f2d9163 100644 --- a/pkg/cmd/roachtest/prometheus/mock_generated.go +++ b/pkg/cmd/roachtest/prometheus/mock_generated.go @@ -14,31 +14,31 @@ import ( gomock "github.com/golang/mock/gomock" ) -// Mockcluster is a mock of cluster interface. -type Mockcluster struct { +// MockCluster is a mock of Cluster interface. +type MockCluster struct { ctrl *gomock.Controller - recorder *MockclusterMockRecorder + recorder *MockClusterMockRecorder } -// MockclusterMockRecorder is the mock recorder for Mockcluster. -type MockclusterMockRecorder struct { - mock *Mockcluster +// MockClusterMockRecorder is the mock recorder for MockCluster. +type MockClusterMockRecorder struct { + mock *MockCluster } -// NewMockcluster creates a new mock instance. -func NewMockcluster(ctrl *gomock.Controller) *Mockcluster { - mock := &Mockcluster{ctrl: ctrl} - mock.recorder = &MockclusterMockRecorder{mock} +// NewMockCluster creates a new mock instance. +func NewMockCluster(ctrl *gomock.Controller) *MockCluster { + mock := &MockCluster{ctrl: ctrl} + mock.recorder = &MockClusterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *Mockcluster) EXPECT() *MockclusterMockRecorder { +func (m *MockCluster) EXPECT() *MockClusterMockRecorder { return m.recorder } // ExternalIP mocks base method. -func (m *Mockcluster) ExternalIP(arg0 context.Context, arg1 option.NodeListOption) ([]string, error) { +func (m *MockCluster) ExternalIP(arg0 context.Context, arg1 option.NodeListOption) ([]string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ExternalIP", arg0, arg1) ret0, _ := ret[0].([]string) @@ -47,13 +47,13 @@ func (m *Mockcluster) ExternalIP(arg0 context.Context, arg1 option.NodeListOptio } // ExternalIP indicates an expected call of ExternalIP. -func (mr *MockclusterMockRecorder) ExternalIP(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockClusterMockRecorder) ExternalIP(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExternalIP", reflect.TypeOf((*Mockcluster)(nil).ExternalIP), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExternalIP", reflect.TypeOf((*MockCluster)(nil).ExternalIP), arg0, arg1) } // Get mocks base method. -func (m *Mockcluster) Get(ctx context.Context, l *logger.Logger, src, dest string, opts ...option.Option) error { +func (m *MockCluster) Get(ctx context.Context, l *logger.Logger, src, dest string, opts ...option.Option) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, l, src, dest} for _, a := range opts { @@ -65,14 +65,14 @@ func (m *Mockcluster) Get(ctx context.Context, l *logger.Logger, src, dest strin } // Get indicates an expected call of Get. -func (mr *MockclusterMockRecorder) Get(ctx, l, src, dest interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockClusterMockRecorder) Get(ctx, l, src, dest interface{}, opts ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, l, src, dest}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*Mockcluster)(nil).Get), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCluster)(nil).Get), varargs...) } // PutString mocks base method. -func (m *Mockcluster) PutString(ctx context.Context, content, dest string, mode os.FileMode, opts ...option.Option) error { +func (m *MockCluster) PutString(ctx context.Context, content, dest string, mode os.FileMode, opts ...option.Option) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, content, dest, mode} for _, a := range opts { @@ -84,14 +84,14 @@ func (m *Mockcluster) PutString(ctx context.Context, content, dest string, mode } // PutString indicates an expected call of PutString. -func (mr *MockclusterMockRecorder) PutString(ctx, content, dest, mode interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockClusterMockRecorder) PutString(ctx, content, dest, mode interface{}, opts ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, content, dest, mode}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutString", reflect.TypeOf((*Mockcluster)(nil).PutString), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutString", reflect.TypeOf((*MockCluster)(nil).PutString), varargs...) } // RunE mocks base method. -func (m *Mockcluster) RunE(ctx context.Context, node option.NodeListOption, args ...string) error { +func (m *MockCluster) RunE(ctx context.Context, node option.NodeListOption, args ...string) error { m.ctrl.T.Helper() varargs := []interface{}{ctx, node} for _, a := range args { @@ -103,8 +103,8 @@ func (m *Mockcluster) RunE(ctx context.Context, node option.NodeListOption, args } // RunE indicates an expected call of RunE. -func (mr *MockclusterMockRecorder) RunE(ctx, node interface{}, args ...interface{}) *gomock.Call { +func (mr *MockClusterMockRecorder) RunE(ctx, node interface{}, args ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{ctx, node}, args...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunE", reflect.TypeOf((*Mockcluster)(nil).RunE), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunE", reflect.TypeOf((*MockCluster)(nil).RunE), varargs...) } diff --git a/pkg/cmd/roachtest/prometheus/prometheus.go b/pkg/cmd/roachtest/prometheus/prometheus.go index 7453bbfcdcc0..5063f4922359 100644 --- a/pkg/cmd/roachtest/prometheus/prometheus.go +++ b/pkg/cmd/roachtest/prometheus/prometheus.go @@ -8,7 +8,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -//go:generate mockgen -package=prometheus -destination=mock_generated.go -source=prometheus.go . cluster +//go:generate mockgen -package=prometheus -destination=mock_generated.go -source=prometheus.go . Cluster package prometheus @@ -42,10 +42,10 @@ type Config struct { ScrapeConfigs []ScrapeConfig } -// cluster is a subset of roachtest.Cluster. +// Cluster is a subset of roachtest.Cluster. // It is abstracted to prevent a circular dependency on roachtest, as Cluster // requires the test interface. -type cluster interface { +type Cluster interface { ExternalIP(context.Context, option.NodeListOption) ([]string, error) Get(ctx context.Context, l *logger.Logger, src, dest string, opts ...option.Option) error RunE(ctx context.Context, node option.NodeListOption, args ...string) error @@ -63,7 +63,7 @@ type Prometheus struct { func Init( ctx context.Context, cfg Config, - c cluster, + c Cluster, repeatFunc func(context.Context, option.NodeListOption, string, ...string) error, ) (*Prometheus, error) { if err := c.RunE( @@ -118,7 +118,7 @@ sudo systemd-run --unit prometheus --same-dir \ // Snapshot takes a snapshot of prometheus and stores the snapshot in the given localPath func (pm *Prometheus) Snapshot( - ctx context.Context, c cluster, l *logger.Logger, localPath string, + ctx context.Context, c Cluster, l *logger.Logger, localPath string, ) error { if err := c.RunE( ctx, @@ -148,7 +148,7 @@ const ( ) // makeYAMLConfig creates a prometheus YAML config for the server to use. -func makeYAMLConfig(ctx context.Context, c cluster, scrapeConfigs []ScrapeConfig) (string, error) { +func makeYAMLConfig(ctx context.Context, c Cluster, scrapeConfigs []ScrapeConfig) (string, error) { type yamlStaticConfig struct { Targets []string } diff --git a/pkg/cmd/roachtest/prometheus/prometheus_test.go b/pkg/cmd/roachtest/prometheus/prometheus_test.go index 6da1a42afa90..1bfc9828510b 100644 --- a/pkg/cmd/roachtest/prometheus/prometheus_test.go +++ b/pkg/cmd/roachtest/prometheus/prometheus_test.go @@ -24,15 +24,15 @@ func TestMakeYAMLConfig(t *testing.T) { testCases := []struct { desc string - mockCluster func(ctrl *gomock.Controller) cluster + mockCluster func(ctrl *gomock.Controller) Cluster scrapeConfigs []ScrapeConfig expected string }{ { desc: "multiple scrape nodes", - mockCluster: func(ctrl *gomock.Controller) cluster { - c := NewMockcluster(ctrl) + mockCluster: func(ctrl *gomock.Controller) Cluster { + c := NewMockCluster(ctrl) c.EXPECT(). ExternalIP(ctx, []int{1}). Return([]string{"127.0.0.1"}, nil) @@ -91,8 +91,8 @@ scrape_configs: }, { desc: "using make commands", - mockCluster: func(ctrl *gomock.Controller) cluster { - c := NewMockcluster(ctrl) + mockCluster: func(ctrl *gomock.Controller) Cluster { + c := NewMockCluster(ctrl) c.EXPECT(). ExternalIP(ctx, []int{3, 4, 5}). Return([]string{"127.0.0.3", "127.0.0.4", "127.0.0.5"}, nil) diff --git a/pkg/cmd/roachtest/tests/BUILD.bazel b/pkg/cmd/roachtest/tests/BUILD.bazel index 8d6e0069f40d..644618f9b0f6 100644 --- a/pkg/cmd/roachtest/tests/BUILD.bazel +++ b/pkg/cmd/roachtest/tests/BUILD.bazel @@ -1,5 +1,17 @@ +load("@bazel_gomock//:gomock.bzl", "gomock") load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +go_library( + name = "with-mocks", + srcs = [":mocks_drt"], + embed = [":tests"], + importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/tests", + visibility = ["//visibility:public"], + deps = [ + "@com_github_golang_mock//gomock", + ], +) + go_library( name = "tests", srcs = [ @@ -32,7 +44,6 @@ go_library( "django_blocklist.go", "drop.go", "drt.go", - "drt_generated.go", "encryption.go", "engine_switch.go", "event_log.go", @@ -131,7 +142,7 @@ go_library( "ycsb.go", ], importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/tests", - visibility = ["//visibility:public"], + visibility = ["//visibility:private"], deps = [ "//pkg/base", "//pkg/ccl/changefeedccl/cdctest", @@ -143,7 +154,7 @@ go_library( "//pkg/cmd/roachtest/cluster", "//pkg/cmd/roachtest/logger", "//pkg/cmd/roachtest/option", - "//pkg/cmd/roachtest/prometheus", + "//pkg/cmd/roachtest/prometheus:with-mocks", "//pkg/cmd/roachtest/registry", "//pkg/cmd/roachtest/spec", "//pkg/cmd/roachtest/test", @@ -185,7 +196,6 @@ go_library( "@com_github_cockroachdb_ttycolor//:ttycolor", "@com_github_codahale_hdrhistogram//:hdrhistogram", "@com_github_dustin_go_humanize//:go-humanize", - "@com_github_golang_mock//gomock", "@com_github_google_go_cmp//cmp", "@com_github_jackc_pgtype//:pgtype", "@com_github_kr_pretty//:pretty", @@ -209,11 +219,11 @@ go_test( "tpcc_test.go", "util_load_group_test.go", ], - embed = [":tests"], + embed = [":with-mocks"], # keep deps = [ "//pkg/cmd/roachtest/logger", "//pkg/cmd/roachtest/option", - "//pkg/cmd/roachtest/prometheus", + "//pkg/cmd/roachtest/prometheus:with-mocks", "//pkg/cmd/roachtest/spec", "//pkg/testutils/skip", "//pkg/util/version", @@ -224,3 +234,11 @@ go_test( "@org_golang_x_oauth2//:oauth2", ], ) + +gomock( + name = "mocks_drt", + out = "drt_generated.go", + interfaces = ["PromClient"], + library = ":tests", + package = "tests", +) diff --git a/pkg/cmd/roachtest/tests/drt.go b/pkg/cmd/roachtest/tests/drt.go index 661193efb9a4..168678b0ca39 100644 --- a/pkg/cmd/roachtest/tests/drt.go +++ b/pkg/cmd/roachtest/tests/drt.go @@ -24,7 +24,8 @@ import ( "github.com/prometheus/common/model" ) -type promClient interface { +// PromClient is an interface allowing queries against Prometheus. +type PromClient interface { Query(ctx context.Context, query string, ts time.Time) (model.Value, promv1.Warnings, error) } @@ -33,7 +34,7 @@ type tpccChaosEventProcessor struct { workloadNodeIP string ops []string ch chan ChaosEvent - promClient promClient + promClient PromClient errs []error // allowZeroSuccessDuringUptime allows 0 successes during an uptime event. diff --git a/pkg/cmd/roachtest/tests/drt_generated.go b/pkg/cmd/roachtest/tests/drt_generated.go index cf816d5ad9c6..19e433a76f97 100644 --- a/pkg/cmd/roachtest/tests/drt_generated.go +++ b/pkg/cmd/roachtest/tests/drt_generated.go @@ -14,31 +14,31 @@ import ( model "github.com/prometheus/common/model" ) -// MockpromClient is a mock of promClient interface. -type MockpromClient struct { +// MockPromClient is a mock of PromClient interface. +type MockPromClient struct { ctrl *gomock.Controller - recorder *MockpromClientMockRecorder + recorder *MockPromClientMockRecorder } -// MockpromClientMockRecorder is the mock recorder for MockpromClient. -type MockpromClientMockRecorder struct { - mock *MockpromClient +// MockPromClientMockRecorder is the mock recorder for MockPromClient. +type MockPromClientMockRecorder struct { + mock *MockPromClient } -// NewMockpromClient creates a new mock instance. -func NewMockpromClient(ctrl *gomock.Controller) *MockpromClient { - mock := &MockpromClient{ctrl: ctrl} - mock.recorder = &MockpromClientMockRecorder{mock} +// NewMockPromClient creates a new mock instance. +func NewMockPromClient(ctrl *gomock.Controller) *MockPromClient { + mock := &MockPromClient{ctrl: ctrl} + mock.recorder = &MockPromClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockpromClient) EXPECT() *MockpromClientMockRecorder { +func (m *MockPromClient) EXPECT() *MockPromClientMockRecorder { return m.recorder } // Query mocks base method. -func (m *MockpromClient) Query(ctx context.Context, query string, ts time.Time) (model.Value, v1.Warnings, error) { +func (m *MockPromClient) Query(ctx context.Context, query string, ts time.Time) (model.Value, v1.Warnings, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Query", ctx, query, ts) ret0, _ := ret[0].(model.Value) @@ -48,7 +48,7 @@ func (m *MockpromClient) Query(ctx context.Context, query string, ts time.Time) } // Query indicates an expected call of Query. -func (mr *MockpromClientMockRecorder) Query(ctx, query, ts interface{}) *gomock.Call { +func (mr *MockPromClientMockRecorder) Query(ctx, query, ts interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockpromClient)(nil).Query), ctx, query, ts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockPromClient)(nil).Query), ctx, query, ts) } diff --git a/pkg/cmd/roachtest/tests/drt_test.go b/pkg/cmd/roachtest/tests/drt_test.go index ef58e886727e..e90180426b99 100644 --- a/pkg/cmd/roachtest/tests/drt_test.go +++ b/pkg/cmd/roachtest/tests/drt_test.go @@ -518,8 +518,8 @@ func TestTPCCChaosEventProcessor(t *testing.T) { allowZeroSuccessDuringUptime: tc.allowZeroSuccessDuringUptime, maxErrorsDuringUptime: tc.maxErrorsDuringUptime, - promClient: func(ctrl *gomock.Controller) promClient { - c := NewMockpromClient(ctrl) + promClient: func(ctrl *gomock.Controller) PromClient { + c := NewMockPromClient(ctrl) e := c.EXPECT() for _, m := range tc.mockPromQueries { e.Query(ctx, m.q, m.t).Return( From cc35e3678468d84614359e1cd9e9c0e1fa1c11e6 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 16:13:41 +0200 Subject: [PATCH 125/205] kvserver: remove deprecated fields in ChangeReplicasTrigger These became obsolete long before we introduced long-running migration (we switched to the new method of doing things in 19.2). In the meantime, all ranges have migrated into the applied state and in the process, have conveniently flushed out any triggers still using the old format from their logs. So we get to "just do this"; thanks, long-running migrations. Release note: None --- pkg/kv/kvserver/client_replica_gc_test.go | 2 +- .../replica_application_state_machine.go | 9 +- .../replica_application_state_machine_test.go | 227 ++++---- pkg/roachpb/data.go | 40 +- pkg/roachpb/data.pb.go | 514 ++++++------------ pkg/roachpb/data.proto | 24 +- 6 files changed, 287 insertions(+), 529 deletions(-) diff --git a/pkg/kv/kvserver/client_replica_gc_test.go b/pkg/kv/kvserver/client_replica_gc_test.go index 47c816b12800..a0fb40a8a6ee 100644 --- a/pkg/kv/kvserver/client_replica_gc_test.go +++ b/pkg/kv/kvserver/client_replica_gc_test.go @@ -54,7 +54,7 @@ func TestReplicaGCQueueDropReplicaDirect(t *testing.T) { return nil } crt := et.InternalCommitTrigger.GetChangeReplicasTrigger() - if crt == nil || crt.DeprecatedChangeType != roachpb.REMOVE_VOTER { + if crt == nil || len(crt.InternalRemovedReplicas) == 0 { return nil } testutils.SucceedsSoon(t, func() error { diff --git a/pkg/kv/kvserver/replica_application_state_machine.go b/pkg/kv/kvserver/replica_application_state_machine.go index 576db511cf47..65c4505d68d2 100644 --- a/pkg/kv/kvserver/replica_application_state_machine.go +++ b/pkg/kv/kvserver/replica_application_state_machine.go @@ -560,7 +560,7 @@ func (b *replicaAppBatch) stageWriteBatch(ctx context.Context, cmd *replicatedCm func changeRemovesStore( desc *roachpb.RangeDescriptor, change *kvserverpb.ChangeReplicas, storeID roachpb.StoreID, ) (removesStore bool) { - curReplica, existsInDesc := desc.GetReplicaDescriptor(storeID) + _, existsInDesc := desc.GetReplicaDescriptor(storeID) // NB: if we're catching up from a preemptive snapshot then we won't // exist in the current descriptor and we can't be removed. if !existsInDesc { @@ -570,13 +570,6 @@ func changeRemovesStore( // NB: We don't use change.Removed() because it will include replicas being // transitioned to VOTER_OUTGOING. - // In 19.1 and before we used DeprecatedUpdatedReplicas instead of providing - // a new range descriptor. Check first if this is 19.1 or earlier command which - // uses DeprecatedChangeType and DeprecatedReplica - if change.Desc == nil { - return change.DeprecatedChangeType == roachpb.REMOVE_VOTER && change.DeprecatedReplica.ReplicaID == curReplica.ReplicaID - } - // In 19.2 and beyond we supply the new range descriptor in the change. // We know we're removed if we do not appear in the new descriptor. _, existsInChange := change.Desc.GetReplicaDescriptor(storeID) return !existsInChange diff --git a/pkg/kv/kvserver/replica_application_state_machine_test.go b/pkg/kv/kvserver/replica_application_state_machine_test.go index 51d5dae93a84..388f75035202 100644 --- a/pkg/kv/kvserver/replica_application_state_machine_test.go +++ b/pkg/kv/kvserver/replica_application_state_machine_test.go @@ -33,139 +33,116 @@ func TestReplicaStateMachineChangeReplicas(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) testutils.RunTrueAndFalse(t, "add replica", func(t *testing.T, add bool) { - testutils.RunTrueAndFalse(t, "deprecated", func(t *testing.T, deprecated bool) { - tc := testContext{} - ctx := context.Background() - stopper := stop.NewStopper() - defer stopper.Stop(ctx) - tc.Start(t, stopper) - - // Lock the replica for the entire test. - r := tc.repl - r.raftMu.Lock() - defer r.raftMu.Unlock() - sm := r.getStateMachine() - - desc := r.Desc() - replDesc, ok := desc.GetReplicaDescriptor(r.store.StoreID()) - require.True(t, ok) + tc := testContext{} + ctx := context.Background() + stopper := stop.NewStopper() + defer stopper.Stop(ctx) + tc.Start(t, stopper) + + // Lock the replica for the entire test. + r := tc.repl + r.raftMu.Lock() + defer r.raftMu.Unlock() + sm := r.getStateMachine() + + desc := r.Desc() + replDesc, ok := desc.GetReplicaDescriptor(r.store.StoreID()) + require.True(t, ok) + + newDesc := *desc + newDesc.InternalReplicas = append([]roachpb.ReplicaDescriptor(nil), desc.InternalReplicas...) + var trigger roachpb.ChangeReplicasTrigger + var confChange raftpb.ConfChange + if add { + // Add a new replica to the Range. + addedReplDesc := newDesc.AddReplica(replDesc.NodeID+1, replDesc.StoreID+1, roachpb.VOTER_FULL) + + trigger = roachpb.ChangeReplicasTrigger{ + Desc: &newDesc, + InternalAddedReplicas: []roachpb.ReplicaDescriptor{addedReplDesc}, + } - newDesc := *desc - newDesc.InternalReplicas = append([]roachpb.ReplicaDescriptor(nil), desc.InternalReplicas...) - var trigger roachpb.ChangeReplicasTrigger - var confChange raftpb.ConfChange - if add { - // Add a new replica to the Range. - addedReplDesc := newDesc.AddReplica(replDesc.NodeID+1, replDesc.StoreID+1, roachpb.VOTER_FULL) - - if deprecated { - trigger = roachpb.ChangeReplicasTrigger{ - DeprecatedChangeType: roachpb.ADD_VOTER, - DeprecatedReplica: addedReplDesc, - DeprecatedUpdatedReplicas: []roachpb.ReplicaDescriptor{ - replDesc, - addedReplDesc, - }, - DeprecatedNextReplicaID: addedReplDesc.ReplicaID + 1, - } - } else { - trigger = roachpb.ChangeReplicasTrigger{ - Desc: &newDesc, - InternalAddedReplicas: []roachpb.ReplicaDescriptor{addedReplDesc}, - } - } - - confChange = raftpb.ConfChange{ - Type: raftpb.ConfChangeAddNode, - NodeID: uint64(addedReplDesc.ReplicaID), - } - } else { - // Remove ourselves from the Range. - removedReplDesc, ok := newDesc.RemoveReplica(replDesc.NodeID, replDesc.StoreID) - require.True(t, ok) - - if deprecated { - trigger = roachpb.ChangeReplicasTrigger{ - DeprecatedChangeType: roachpb.REMOVE_VOTER, - DeprecatedReplica: removedReplDesc, - DeprecatedUpdatedReplicas: []roachpb.ReplicaDescriptor{}, - DeprecatedNextReplicaID: replDesc.ReplicaID + 1, - } - } else { - trigger = roachpb.ChangeReplicasTrigger{ - Desc: &newDesc, - InternalRemovedReplicas: []roachpb.ReplicaDescriptor{removedReplDesc}, - } - } - - confChange = raftpb.ConfChange{ - Type: raftpb.ConfChangeRemoveNode, - NodeID: uint64(removedReplDesc.ReplicaID), - } + confChange = raftpb.ConfChange{ + Type: raftpb.ConfChangeAddNode, + NodeID: uint64(addedReplDesc.ReplicaID), } + } else { + // Remove ourselves from the Range. + removedReplDesc, ok := newDesc.RemoveReplica(replDesc.NodeID, replDesc.StoreID) + require.True(t, ok) - // Create a new application batch. - b := sm.NewBatch(false /* ephemeral */).(*replicaAppBatch) - defer b.Close() + trigger = roachpb.ChangeReplicasTrigger{ + Desc: &newDesc, + InternalRemovedReplicas: []roachpb.ReplicaDescriptor{removedReplDesc}, + } - // Stage a command with the ChangeReplicas trigger. - cmd := &replicatedCmd{ - ctx: ctx, - ent: &raftpb.Entry{ - Index: r.mu.state.RaftAppliedIndex + 1, - Type: raftpb.EntryConfChange, - }, - decodedRaftEntry: decodedRaftEntry{ - idKey: makeIDKey(), - raftCmd: kvserverpb.RaftCommand{ - ProposerLeaseSequence: r.mu.state.Lease.Sequence, - MaxLeaseIndex: r.mu.state.LeaseAppliedIndex + 1, - ReplicatedEvalResult: kvserverpb.ReplicatedEvalResult{ - State: &kvserverpb.ReplicaState{Desc: &newDesc}, - ChangeReplicas: &kvserverpb.ChangeReplicas{ChangeReplicasTrigger: trigger}, - WriteTimestamp: r.mu.state.GCThreshold.Add(1, 0), - }, - }, - confChange: &decodedConfChange{ - ConfChangeI: confChange, + confChange = raftpb.ConfChange{ + Type: raftpb.ConfChangeRemoveNode, + NodeID: uint64(removedReplDesc.ReplicaID), + } + } + + // Create a new application batch. + b := sm.NewBatch(false /* ephemeral */).(*replicaAppBatch) + defer b.Close() + + // Stage a command with the ChangeReplicas trigger. + cmd := &replicatedCmd{ + ctx: ctx, + ent: &raftpb.Entry{ + Index: r.mu.state.RaftAppliedIndex + 1, + Type: raftpb.EntryConfChange, + }, + decodedRaftEntry: decodedRaftEntry{ + idKey: makeIDKey(), + raftCmd: kvserverpb.RaftCommand{ + ProposerLeaseSequence: r.mu.state.Lease.Sequence, + MaxLeaseIndex: r.mu.state.LeaseAppliedIndex + 1, + ReplicatedEvalResult: kvserverpb.ReplicatedEvalResult{ + State: &kvserverpb.ReplicaState{Desc: &newDesc}, + ChangeReplicas: &kvserverpb.ChangeReplicas{ChangeReplicasTrigger: trigger}, + WriteTimestamp: r.mu.state.GCThreshold.Add(1, 0), }, }, - } - - checkedCmd, err := b.Stage(cmd) + confChange: &decodedConfChange{ + ConfChangeI: confChange, + }, + }, + } + + checkedCmd, err := b.Stage(cmd) + require.NoError(t, err) + require.Equal(t, !add, b.changeRemovesReplica) + require.Equal(t, b.state.RaftAppliedIndex, cmd.ent.Index) + require.Equal(t, b.state.LeaseAppliedIndex, cmd.raftCmd.MaxLeaseIndex) + + // Check the replica's destroy status. + reason, _ := r.IsDestroyed() + if add { + require.Equal(t, destroyReasonAlive, reason) + } else { + require.Equal(t, destroyReasonRemoved, reason) + } + + // Apply the batch to the StateMachine. + err = b.ApplyToStateMachine(ctx) + require.NoError(t, err) + + // Apply the side effects of the command to the StateMachine. + _, err = sm.ApplySideEffects(checkedCmd) + if add { require.NoError(t, err) - require.Equal(t, !add, b.changeRemovesReplica) - require.Equal(t, b.state.RaftAppliedIndex, cmd.ent.Index) - require.Equal(t, b.state.LeaseAppliedIndex, cmd.raftCmd.MaxLeaseIndex) - - // Check the replica's destroy status. - reason, _ := r.IsDestroyed() - if add { - require.Equal(t, destroyReasonAlive, reason) - } else { - require.Equal(t, destroyReasonRemoved, reason) - } + } else { + require.Equal(t, apply.ErrRemoved, err) + } - // Apply the batch to the StateMachine. - err = b.ApplyToStateMachine(ctx) + // Check whether the Replica still exists in the Store. + _, err = tc.store.GetReplica(r.RangeID) + if add { require.NoError(t, err) - - // Apply the side effects of the command to the StateMachine. - _, err = sm.ApplySideEffects(checkedCmd) - if add { - require.NoError(t, err) - } else { - require.Equal(t, apply.ErrRemoved, err) - } - - // Check whether the Replica still exists in the Store. - _, err = tc.store.GetReplica(r.RangeID) - if add { - require.NoError(t, err) - } else { - require.Error(t, err) - require.IsType(t, &roachpb.RangeNotFoundError{}, err) - } - }) + } else { + require.Error(t, err) + require.IsType(t, &roachpb.RangeNotFoundError{}, err) + } }) } diff --git a/pkg/roachpb/data.go b/pkg/roachpb/data.go index f1c4e060ab11..383a29a92ce0 100644 --- a/pkg/roachpb/data.go +++ b/pkg/roachpb/data.go @@ -1558,18 +1558,12 @@ func writeTooOldRetryTimestamp(err *WriteTooOldError) hlc.Timestamp { // Replicas returns all of the replicas present in the descriptor after this // trigger applies. func (crt ChangeReplicasTrigger) Replicas() []ReplicaDescriptor { - if crt.Desc != nil { - return crt.Desc.Replicas().Descriptors() - } - return crt.DeprecatedUpdatedReplicas + return crt.Desc.Replicas().Descriptors() } // NextReplicaID returns the next replica id to use after this trigger applies. func (crt ChangeReplicasTrigger) NextReplicaID() ReplicaID { - if crt.Desc != nil { - return crt.Desc.NextReplicaID - } - return crt.DeprecatedNextReplicaID + return crt.Desc.NextReplicaID } // ConfChange returns the configuration change described by the trigger. @@ -1772,17 +1766,12 @@ func (crt ChangeReplicasTrigger) SafeFormat(w redact.SafePrinter, _ rune) { var nextReplicaID ReplicaID var afterReplicas []ReplicaDescriptor added, removed := crt.Added(), crt.Removed() - if crt.Desc != nil { - nextReplicaID = crt.Desc.NextReplicaID - // NB: we don't want to mutate InternalReplicas, so we don't call - // .Replicas() - // - // TODO(tbg): revisit after #39489 is merged. - afterReplicas = crt.Desc.InternalReplicas - } else { - nextReplicaID = crt.DeprecatedNextReplicaID - afterReplicas = crt.DeprecatedUpdatedReplicas - } + nextReplicaID = crt.Desc.NextReplicaID + // NB: we don't want to mutate InternalReplicas, so we don't call + // .Replicas() + // + // TODO(tbg): revisit after #39489 is merged. + afterReplicas = crt.Desc.InternalReplicas cc, err := crt.ConfChange(nil) if err != nil { w.Printf("", err) @@ -1837,18 +1826,8 @@ func confChangesToRedactableString(ccs []raftpb.ConfChangeSingle) redact.Redacta }) } -func (crt ChangeReplicasTrigger) legacy() (ReplicaDescriptor, bool) { - if len(crt.InternalAddedReplicas)+len(crt.InternalRemovedReplicas) == 0 && crt.DeprecatedReplica.ReplicaID != 0 { - return crt.DeprecatedReplica, true - } - return ReplicaDescriptor{}, false -} - // Added returns the replicas added by this change (if there are any). func (crt ChangeReplicasTrigger) Added() []ReplicaDescriptor { - if rDesc, ok := crt.legacy(); ok && crt.DeprecatedChangeType == ADD_VOTER { - return []ReplicaDescriptor{rDesc} - } return crt.InternalAddedReplicas } @@ -1857,9 +1836,6 @@ func (crt ChangeReplicasTrigger) Added() []ReplicaDescriptor { // transitioning to VOTER_{OUTGOING,DEMOTING} (from VOTER_FULL). The subsequent trigger // leaving the joint configuration has an empty Removed(). func (crt ChangeReplicasTrigger) Removed() []ReplicaDescriptor { - if rDesc, ok := crt.legacy(); ok && crt.DeprecatedChangeType == REMOVE_VOTER { - return []ReplicaDescriptor{rDesc} - } return crt.InternalRemovedReplicas } diff --git a/pkg/roachpb/data.pb.go b/pkg/roachpb/data.pb.go index c360a5a322ce..68acb3dc93ce 100644 --- a/pkg/roachpb/data.pb.go +++ b/pkg/roachpb/data.pb.go @@ -573,28 +573,12 @@ var xxx_messageInfo_MergeTrigger proto.InternalMessageInfo // Removal() only). This joint configuration is left via another // ChangeReplicasTrigger which does not specify any additions nor removals. type ChangeReplicasTrigger struct { - // TODO(tbg): remove once we know that no trigger using this will ever be - // applied (this will require something like #39182). - // - // TODO(tbg): when removing this, also rename internal_x_replicas to just - // x_replicas and remove the getter. - DeprecatedChangeType ReplicaChangeType `protobuf:"varint,1,opt,name=deprecated_change_type,json=deprecatedChangeType,proto3,enum=cockroach.roachpb.ReplicaChangeType" json:"deprecated_change_type,omitempty"` - // The replica being modified. - // TODO(tbg): remove once we know that no trigger using this will ever be - // applied (this will require something like #39182). - DeprecatedReplica ReplicaDescriptor `protobuf:"bytes,2,opt,name=deprecated_replica,json=deprecatedReplica,proto3" json:"deprecated_replica"` - // The new replica list with this change applied. - DeprecatedUpdatedReplicas []ReplicaDescriptor `protobuf:"bytes,3,rep,name=deprecated_updated_replicas,json=deprecatedUpdatedReplicas,proto3" json:"deprecated_updated_replicas"` - // The next replica id to use with this change applied. - DeprecatedNextReplicaID ReplicaID `protobuf:"varint,4,opt,name=deprecated_next_replica_id,json=deprecatedNextReplicaId,proto3,casttype=ReplicaID" json:"deprecated_next_replica_id,omitempty"` - // The updated range descriptor. If desc is non-nil, then it overrides - // updated_replicas and next_replica_id. This incremental addition is needed - // to maintain backwards compatibility. - // TODO(jeffreyxiao): Remove deprecated_updated_replicas and - // deprecated_next_replica_id in 20.1. + // The updated range descriptor. Desc *RangeDescriptor `protobuf:"bytes,5,opt,name=desc,proto3" json:"desc,omitempty"` // The new replicas added to the range descriptor in this change, exactly as // they appear in the updated range descriptor. + // + // TODO(tbg): rename internal_x_replicas to just x_replicas and remove the getter. InternalAddedReplicas []ReplicaDescriptor `protobuf:"bytes,6,rep,name=internal_added_replicas,json=internalAddedReplicas,proto3" json:"internal_added_replicas"` // The replicas whose removal is being initiated in this change. If the // replica is still present as an outgoing voter in the updated descriptor @@ -1581,178 +1565,173 @@ func init() { func init() { proto.RegisterFile("roachpb/data.proto", fileDescriptor_6493e3a32c32e46b) } var fileDescriptor_6493e3a32c32e46b = []byte{ - // 2735 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0xcb, 0x6f, 0xe3, 0xd6, - 0xb9, 0x37, 0x2d, 0xca, 0xa2, 0x3e, 0x4b, 0x32, 0x7d, 0xc6, 0xf6, 0x68, 0x3c, 0xf7, 0x5a, 0x73, - 0x95, 0x7b, 0x6f, 0xa6, 0x83, 0x46, 0x42, 0x9d, 0x34, 0x08, 0xa6, 0x53, 0xa0, 0x7a, 0x8d, 0x23, - 0x8d, 0x1e, 0x0e, 0x45, 0x4f, 0xe2, 0x24, 0x05, 0x4b, 0x91, 0xc7, 0x32, 0x6b, 0x8a, 0xd4, 0x90, - 0x94, 0x63, 0x65, 0xd7, 0x4d, 0x11, 0x64, 0xd1, 0x16, 0x5d, 0x75, 0x19, 0xa0, 0xbb, 0x76, 0xd1, - 0x2e, 0x8b, 0xfe, 0x05, 0x59, 0x74, 0x91, 0x4d, 0x91, 0xb4, 0x05, 0x84, 0xd6, 0xd9, 0x64, 0xd1, - 0xbf, 0xc0, 0x68, 0x81, 0xe2, 0x9c, 0x43, 0x8a, 0xb4, 0x47, 0x76, 0xe5, 0x4c, 0x9a, 0x66, 0xd1, - 0x8d, 0x4d, 0x7e, 0x8f, 0xdf, 0x77, 0x1e, 0xdf, 0x8b, 0x9f, 0x00, 0x39, 0xb6, 0xaa, 0x1d, 0x0e, - 0x7b, 0x45, 0x5d, 0xf5, 0xd4, 0xc2, 0xd0, 0xb1, 0x3d, 0x1b, 0xad, 0x6a, 0xb6, 0x76, 0x44, 0xe9, - 0x05, 0x9f, 0xbb, 0x79, 0xef, 0xe8, 0xb8, 0x78, 0x74, 0xec, 0x62, 0xe7, 0x18, 0x3b, 0x45, 0xcd, - 0xb6, 0xb4, 0x91, 0xe3, 0x60, 0x4b, 0x1b, 0x17, 0x4d, 0x5b, 0x3b, 0xa2, 0x7f, 0x0c, 0xab, 0xcf, - 0xd4, 0xcf, 0xcb, 0x3a, 0x58, 0xd5, 0xdd, 0xd1, 0x60, 0xa0, 0x3a, 0xe3, 0xa2, 0xe3, 0x0e, 0x7b, - 0x45, 0xff, 0xc5, 0x97, 0xdd, 0x08, 0xcc, 0x0f, 0xb0, 0xa7, 0x86, 0x4b, 0xd8, 0xbc, 0xed, 0x7a, - 0xb6, 0xa3, 0xf6, 0x71, 0x11, 0x5b, 0x7d, 0xc3, 0xc2, 0x44, 0xe0, 0x58, 0xd3, 0x7c, 0xe6, 0x7f, - 0xcd, 0x64, 0xbe, 0xe8, 0x73, 0xb3, 0x23, 0xcf, 0x30, 0x8b, 0x87, 0xa6, 0x56, 0xf4, 0x8c, 0x01, - 0x76, 0x3d, 0x75, 0x30, 0xf4, 0x39, 0x6b, 0x7d, 0xbb, 0x6f, 0xd3, 0xc7, 0x22, 0x79, 0x62, 0xd4, - 0xfc, 0x3e, 0xf0, 0xdd, 0xa1, 0x6a, 0xa1, 0x5b, 0x10, 0x3b, 0xc2, 0xe3, 0x6c, 0xec, 0x0e, 0x77, - 0x37, 0x55, 0x4e, 0x9c, 0x4d, 0x72, 0xb1, 0x47, 0x78, 0x2c, 0x11, 0x1a, 0xba, 0x03, 0x09, 0x6c, - 0xe9, 0x0a, 0x61, 0xf3, 0xe7, 0xd9, 0x4b, 0xd8, 0xd2, 0x1f, 0xe1, 0xf1, 0x7d, 0xe1, 0x67, 0x1f, - 0xe4, 0x16, 0x7e, 0xf3, 0x41, 0x8e, 0x6b, 0xf0, 0x02, 0x27, 0x2e, 0x36, 0x78, 0x61, 0x51, 0x8c, - 0xe5, 0xfb, 0x10, 0x7f, 0xac, 0x9a, 0x23, 0x8c, 0x6e, 0x43, 0xd2, 0x51, 0xdf, 0x51, 0x7a, 0x63, - 0x0f, 0xbb, 0x59, 0x8e, 0x40, 0x48, 0x82, 0xa3, 0xbe, 0x53, 0x26, 0xef, 0xa8, 0x04, 0xc9, 0xe9, - 0x4a, 0xb3, 0x8b, 0x77, 0xb8, 0xbb, 0xcb, 0xdb, 0xff, 0x5d, 0x08, 0xaf, 0x80, 0x6c, 0xa7, 0x70, - 0x68, 0x6a, 0x05, 0x39, 0x10, 0x2a, 0xf3, 0x1f, 0x4e, 0x72, 0x0b, 0x52, 0xa8, 0x95, 0x7f, 0x0b, - 0x84, 0x47, 0x78, 0xcc, 0x6c, 0xf9, 0xfb, 0xe0, 0x66, 0xec, 0xe3, 0x25, 0x88, 0x1f, 0x13, 0x19, - 0xdf, 0x4a, 0xb6, 0xf0, 0xd4, 0x45, 0x17, 0x28, 0x86, 0x6f, 0x80, 0x09, 0xe7, 0x3f, 0xe6, 0x00, - 0xba, 0x9e, 0xed, 0xe0, 0xba, 0x8e, 0x2d, 0x0f, 0xf5, 0x01, 0x34, 0x73, 0xe4, 0x7a, 0xd8, 0x51, - 0x0c, 0xdd, 0x37, 0xf3, 0x2a, 0x91, 0xff, 0xe3, 0x24, 0xf7, 0x62, 0xdf, 0xf0, 0x0e, 0x47, 0xbd, - 0x82, 0x66, 0x0f, 0x8a, 0x53, 0x6c, 0xbd, 0x17, 0x3e, 0x17, 0x87, 0x47, 0xfd, 0x22, 0xbd, 0xa0, - 0xd1, 0xc8, 0xd0, 0x0b, 0x7b, 0x7b, 0xf5, 0xea, 0xe9, 0x24, 0x97, 0xac, 0x30, 0xc0, 0x7a, 0x55, - 0x4a, 0xfa, 0xd8, 0x75, 0x1d, 0xbd, 0x00, 0x09, 0xcb, 0xd6, 0x31, 0xb1, 0x42, 0xd6, 0x1b, 0x2f, - 0xaf, 0x9d, 0x4e, 0x72, 0x4b, 0x6d, 0x5b, 0xc7, 0xf5, 0xea, 0xd9, 0xf4, 0x49, 0x5a, 0x22, 0x42, - 0x75, 0x1d, 0x7d, 0x03, 0x04, 0xe2, 0x17, 0x54, 0x3e, 0x46, 0xe5, 0x37, 0x4e, 0x27, 0xb9, 0x04, - 0x5b, 0x39, 0x51, 0x08, 0x1e, 0xa5, 0x84, 0xcb, 0x76, 0x93, 0xff, 0x05, 0x07, 0xa9, 0xee, 0xd0, - 0x34, 0x3c, 0xd9, 0x31, 0xfa, 0x7d, 0xec, 0xa0, 0x1a, 0x24, 0x4d, 0x7c, 0xe0, 0x29, 0x3a, 0x76, - 0x35, 0xba, 0xb5, 0xe5, 0xed, 0xfc, 0x8c, 0x43, 0x92, 0x54, 0xab, 0x8f, 0xab, 0xd8, 0xd5, 0x1c, - 0x63, 0xe8, 0xd9, 0x8e, 0x7f, 0x5c, 0x02, 0x51, 0x25, 0x54, 0xb4, 0x03, 0xe0, 0x18, 0xfd, 0x43, - 0x1f, 0x67, 0xf1, 0x9a, 0x38, 0x49, 0xaa, 0x4b, 0xc8, 0xf7, 0xf9, 0xcf, 0x98, 0x4b, 0xc5, 0x44, - 0x3e, 0xff, 0x7b, 0x1e, 0x52, 0x2d, 0xec, 0xf4, 0xf1, 0x57, 0x74, 0xb1, 0xa8, 0x0f, 0x22, 0x03, - 0x22, 0xd1, 0xa8, 0xb8, 0x9e, 0xea, 0xb9, 0x34, 0x5c, 0x96, 0xb7, 0xff, 0x2f, 0x02, 0xe7, 0xc7, - 0x6e, 0x21, 0x88, 0xdd, 0x42, 0xeb, 0x71, 0xa5, 0xd2, 0x25, 0xc2, 0xe5, 0x0d, 0x82, 0x78, 0x3a, - 0xc9, 0x65, 0x24, 0x02, 0x33, 0xa5, 0x4b, 0x19, 0x0a, 0xdb, 0x3a, 0xd6, 0x34, 0xfa, 0x8e, 0x7e, - 0xc8, 0x41, 0xea, 0xc0, 0xc1, 0xf8, 0x5d, 0x4c, 0xac, 0x38, 0x5e, 0x36, 0x3e, 0x4f, 0xd0, 0x54, - 0x09, 0xfa, 0xd9, 0x24, 0xf7, 0x60, 0x7e, 0x1f, 0x25, 0x00, 0x15, 0x92, 0xdc, 0xa6, 0x28, 0xd2, - 0x32, 0x33, 0xdc, 0x25, 0x76, 0xd1, 0x3e, 0x6c, 0xb0, 0x1d, 0x6b, 0xa6, 0xed, 0x62, 0x5d, 0x09, - 0xc3, 0x78, 0x69, 0xfe, 0x30, 0x5e, 0xa3, 0x10, 0x15, 0x8a, 0x30, 0xe5, 0xa1, 0xb7, 0x01, 0x31, - 0x68, 0x92, 0x40, 0x15, 0x3f, 0x69, 0x66, 0x13, 0x14, 0xb6, 0x10, 0x81, 0x3d, 0x3a, 0x2e, 0x04, - 0xb9, 0xb6, 0x10, 0xc9, 0xb5, 0x05, 0x09, 0xab, 0x7a, 0x97, 0x3d, 0x4b, 0xec, 0x5a, 0x22, 0x94, - 0x73, 0x7e, 0xf5, 0xcb, 0x38, 0xac, 0x57, 0x0e, 0xc9, 0xe5, 0x4a, 0x78, 0x68, 0x1a, 0x9a, 0xea, - 0x06, 0x0e, 0xf6, 0x26, 0x6c, 0xe8, 0x78, 0xe8, 0x60, 0x4d, 0xf5, 0xb0, 0xae, 0x68, 0x54, 0x46, - 0xf1, 0xc6, 0x43, 0x4c, 0xbd, 0x2d, 0xb3, 0xfd, 0xbf, 0xb3, 0xbc, 0x84, 0x61, 0x30, 0x40, 0x79, - 0x3c, 0xc4, 0xd2, 0x5a, 0x88, 0x11, 0x52, 0xd1, 0x3e, 0xa0, 0x08, 0xb6, 0xc3, 0xb4, 0x7c, 0xef, - 0xbb, 0x02, 0xf7, 0x29, 0xff, 0x5b, 0x0d, 0x51, 0x7c, 0x11, 0xf4, 0x7d, 0xb8, 0x1d, 0x81, 0x1e, - 0x0d, 0xf5, 0xa8, 0x09, 0x37, 0x1b, 0xbb, 0x13, 0xbb, 0xa6, 0x8d, 0x5b, 0x21, 0xdc, 0x1e, 0x43, - 0x0b, 0x4e, 0x0a, 0x61, 0xd8, 0x8c, 0xd8, 0xb2, 0xf0, 0x89, 0x17, 0x18, 0x22, 0x69, 0x88, 0xa7, - 0x69, 0xe8, 0xee, 0xe9, 0x24, 0x77, 0xb3, 0x3a, 0x95, 0x6a, 0xe3, 0x13, 0xcf, 0xd7, 0xa7, 0x69, - 0x29, 0x39, 0x7d, 0x91, 0x6e, 0xea, 0x33, 0xa5, 0x74, 0xf4, 0x32, 0xf0, 0x34, 0x3a, 0xe3, 0xf3, - 0x46, 0xa7, 0x44, 0xe5, 0x51, 0x0f, 0x6e, 0x1a, 0x96, 0x87, 0x1d, 0x4b, 0x35, 0x15, 0x55, 0xd7, - 0xa3, 0xc7, 0xb0, 0x74, 0xed, 0x63, 0x58, 0x0f, 0xa0, 0x4a, 0x04, 0x69, 0x7a, 0x04, 0x07, 0x70, - 0x6b, 0x6a, 0xc3, 0xc1, 0x03, 0xfb, 0x38, 0x6a, 0x25, 0x71, 0x6d, 0x2b, 0xd3, 0x05, 0x4b, 0x0c, - 0x2b, 0xb0, 0x73, 0x9f, 0x27, 0x25, 0x36, 0xff, 0x3e, 0x07, 0x37, 0x5a, 0xb6, 0x6e, 0x1c, 0x18, - 0x58, 0x27, 0x65, 0x3b, 0xf0, 0xd5, 0xaf, 0x03, 0x72, 0xc7, 0xae, 0x87, 0x07, 0x8a, 0x66, 0x5b, - 0x07, 0x46, 0x5f, 0x71, 0x87, 0xaa, 0x45, 0xfd, 0x54, 0x90, 0x44, 0xc6, 0xa9, 0x50, 0x06, 0xad, - 0xf5, 0x35, 0x40, 0xb4, 0xb4, 0x98, 0xc6, 0x31, 0xb6, 0xb0, 0xeb, 0x32, 0x69, 0xe6, 0x7d, 0x37, - 0x67, 0x2c, 0x96, 0x28, 0x49, 0x22, 0x51, 0x69, 0xfa, 0x1a, 0x84, 0x92, 0x7f, 0x0c, 0x62, 0xd7, - 0x33, 0xb4, 0xa3, 0x71, 0x39, 0x2c, 0x21, 0x65, 0x00, 0x97, 0xd2, 0x94, 0x9e, 0xe1, 0xf9, 0x69, - 0x79, 0xbe, 0x72, 0xee, 0x06, 0x50, 0xf9, 0xdf, 0xc6, 0x60, 0xbd, 0xee, 0x1f, 0x43, 0xc5, 0x1e, - 0x0c, 0x42, 0xf4, 0x2a, 0xa4, 0x5d, 0x52, 0xb0, 0x14, 0x8f, 0x11, 0x7c, 0x03, 0xb9, 0x99, 0x6b, - 0x0e, 0x0b, 0x9b, 0x94, 0x72, 0xa3, 0x65, 0xae, 0x0a, 0xe9, 0x01, 0xa9, 0x24, 0x53, 0x94, 0xc5, - 0x4b, 0x51, 0xa2, 0x15, 0x47, 0x4a, 0x0d, 0xa2, 0xf5, 0xe7, 0x7b, 0x70, 0xd3, 0xcf, 0x09, 0xc1, - 0x75, 0x4f, 0xf1, 0x62, 0x14, 0xef, 0xee, 0x0c, 0xbc, 0x99, 0x99, 0x46, 0x5a, 0xd7, 0x2e, 0x49, - 0x40, 0xeb, 0x03, 0xff, 0xae, 0xe9, 0x0d, 0x4d, 0xf1, 0x59, 0x59, 0xf9, 0xff, 0x59, 0xeb, 0x7d, - 0xda, 0x37, 0xa4, 0x1b, 0x83, 0x19, 0x0e, 0xf3, 0x1a, 0xa0, 0xf0, 0x9e, 0xa6, 0xc0, 0x2c, 0xc0, - 0x9e, 0x9b, 0x75, 0x9c, 0x17, 0x2e, 0x5a, 0x12, 0xdd, 0x0b, 0x94, 0xfb, 0xfc, 0x7b, 0x1f, 0xe4, - 0xb8, 0xfc, 0xef, 0x38, 0x58, 0xed, 0xf4, 0x68, 0x4a, 0x8e, 0xe4, 0xf3, 0x48, 0x33, 0xc3, 0xcd, - 0xd1, 0xcc, 0xfc, 0x80, 0xbb, 0x76, 0x53, 0xf8, 0xc5, 0xd4, 0xb7, 0xd0, 0xea, 0x7d, 0x9e, 0xf4, - 0xb3, 0xf9, 0xb3, 0x25, 0x58, 0x96, 0x1d, 0xd5, 0x72, 0x55, 0xcd, 0x33, 0x6c, 0x0b, 0x95, 0x80, - 0x27, 0xbd, 0xba, 0xef, 0x78, 0xcf, 0x5d, 0x55, 0xd9, 0xe5, 0x13, 0xab, 0x85, 0x3d, 0xb5, 0x2c, - 0x90, 0x95, 0x7d, 0x34, 0xc9, 0x71, 0x12, 0x55, 0x45, 0x08, 0x78, 0x4b, 0x1d, 0xb0, 0x2e, 0x34, - 0x29, 0xd1, 0x67, 0xf4, 0x00, 0x96, 0x48, 0xc7, 0x30, 0x62, 0x2d, 0xc3, 0xec, 0xda, 0x12, 0x59, - 0x46, 0x97, 0xca, 0x4a, 0xbe, 0x0e, 0x6a, 0x40, 0xc6, 0x54, 0x5d, 0x4f, 0x39, 0xc4, 0xaa, 0xe3, - 0xf5, 0xb0, 0x3a, 0x67, 0x4b, 0xc0, 0x02, 0x2f, 0x4d, 0x54, 0x5f, 0x0d, 0x34, 0xd1, 0x4b, 0xb0, - 0xa1, 0xd1, 0x98, 0x0b, 0xcb, 0xb9, 0x72, 0x60, 0x9c, 0x60, 0x3d, 0x2b, 0xd2, 0x6c, 0xb2, 0xc6, - 0xb8, 0x53, 0x94, 0x87, 0x84, 0x47, 0x56, 0x40, 0x2b, 0x75, 0x78, 0x69, 0x2b, 0xd7, 0x58, 0x01, - 0x51, 0x0d, 0x7d, 0xe5, 0xbb, 0x90, 0xed, 0x9b, 0x76, 0x4f, 0x35, 0x95, 0x91, 0xa5, 0x61, 0xc7, - 0x53, 0x0d, 0xcb, 0x1b, 0x2b, 0xa6, 0x31, 0x30, 0x3c, 0xbf, 0x03, 0x98, 0x0b, 0x75, 0x83, 0x81, - 0xec, 0x85, 0x18, 0x4d, 0x02, 0x81, 0xde, 0x82, 0x1b, 0xb6, 0xef, 0x9f, 0xe1, 0x72, 0xdd, 0xac, - 0x70, 0x69, 0xaa, 0x7e, 0xca, 0x9b, 0x7d, 0x03, 0xc8, 0xbe, 0xc8, 0x70, 0x51, 0x1e, 0xd2, 0xef, - 0x38, 0x86, 0x87, 0x15, 0xcf, 0xb6, 0x15, 0xdb, 0xd4, 0xb3, 0x29, 0x7a, 0x68, 0xcb, 0x94, 0x28, - 0xdb, 0x76, 0xc7, 0xd4, 0xd1, 0x03, 0x00, 0xe2, 0x74, 0x34, 0xa4, 0xdd, 0xec, 0x32, 0xb5, 0x7b, - 0x59, 0xd6, 0x0d, 0x92, 0x23, 0x51, 0x20, 0xef, 0x2e, 0x7a, 0x0d, 0x44, 0xc3, 0x52, 0x0e, 0x4c, - 0xda, 0x1d, 0x51, 0x58, 0x37, 0xbb, 0x4a, 0x31, 0xfe, 0x67, 0x16, 0x06, 0x7e, 0x32, 0xc2, 0x96, - 0x86, 0xf5, 0xd7, 0x89, 0xa4, 0x8f, 0x96, 0x31, 0xac, 0x87, 0x54, 0x9f, 0x12, 0x5d, 0x64, 0xc3, - 0x8a, 0xd1, 0xb7, 0x6c, 0x87, 0xa4, 0x19, 0xfc, 0xc4, 0x1a, 0x0d, 0xdc, 0x2c, 0xa2, 0x88, 0x85, - 0xab, 0xdc, 0xbb, 0xce, 0x54, 0xba, 0xf8, 0x49, 0x7b, 0x34, 0xa0, 0xf5, 0x37, 0xec, 0x60, 0xcf, - 0xf1, 0x5c, 0x29, 0x63, 0x4c, 0xdf, 0x09, 0xfa, 0xb9, 0xcf, 0xc5, 0x98, 0xc8, 0x37, 0x78, 0x61, - 0x49, 0x4c, 0x34, 0x78, 0x21, 0x29, 0x42, 0x83, 0x17, 0xd2, 0x62, 0xa6, 0xc1, 0x0b, 0x19, 0x71, - 0x25, 0xff, 0x6b, 0x1e, 0x56, 0x23, 0x5e, 0x2f, 0x61, 0xcd, 0x76, 0xf4, 0x2f, 0x22, 0x04, 0xbf, - 0x3a, 0xe1, 0xf6, 0x1f, 0x67, 0xe0, 0x7d, 0x47, 0x58, 0x14, 0x63, 0x17, 0xdc, 0x21, 0x21, 0x0a, - 0x0d, 0x5e, 0x10, 0xc4, 0xe4, 0xd4, 0x35, 0x40, 0x5c, 0x6e, 0xf0, 0x42, 0x4a, 0x4c, 0x47, 0xdd, - 0xa4, 0xc1, 0x0b, 0x2b, 0xa2, 0xd8, 0xe0, 0x05, 0x51, 0x5c, 0xcd, 0x4f, 0x38, 0x58, 0x22, 0xbd, - 0x83, 0x45, 0x3e, 0x4f, 0x56, 0x5c, 0xc3, 0xea, 0x9b, 0x58, 0x39, 0xc2, 0xe3, 0xb0, 0x21, 0x5a, - 0xde, 0x7e, 0x7e, 0xc6, 0xd9, 0x30, 0x9d, 0x42, 0x97, 0x2a, 0x3c, 0xc2, 0x63, 0x7a, 0xde, 0xa1, - 0xdb, 0xa4, 0xdd, 0x28, 0x03, 0x7d, 0x0b, 0x62, 0xde, 0x49, 0xd0, 0x31, 0xcd, 0xe5, 0x81, 0xec, - 0xb0, 0x89, 0xd6, 0xe6, 0x03, 0x48, 0x9f, 0x33, 0x73, 0xc5, 0xe8, 0x25, 0x3a, 0x4e, 0x69, 0xf0, - 0x02, 0x2f, 0xc6, 0xf3, 0x7f, 0xe0, 0x60, 0xa5, 0x69, 0x6b, 0x47, 0x25, 0xed, 0xc9, 0xc8, 0x70, - 0x0d, 0x5a, 0x94, 0xbe, 0x09, 0x7c, 0x64, 0x7b, 0x97, 0xba, 0x4f, 0x24, 0x0a, 0xdc, 0x67, 0xdd, - 0x05, 0x92, 0x00, 0xf4, 0x91, 0xa3, 0xf6, 0x0c, 0xd3, 0xf0, 0xd8, 0xda, 0x33, 0xdb, 0xdb, 0x97, - 0x7c, 0x99, 0x45, 0x26, 0x66, 0x05, 0xe2, 0xbc, 0x85, 0xea, 0x54, 0x53, 0x8a, 0xa0, 0xe4, 0x7f, - 0xb5, 0x08, 0x40, 0xf6, 0xc6, 0x3e, 0x33, 0xfe, 0x2d, 0xdb, 0x0a, 0x33, 0x43, 0xec, 0x73, 0x64, - 0x86, 0x19, 0xc1, 0xc3, 0xff, 0x2b, 0x83, 0x27, 0xff, 0x63, 0x0e, 0x32, 0xe7, 0xc3, 0xfa, 0xaa, - 0x01, 0xd8, 0xdb, 0x20, 0xb8, 0xbe, 0xb0, 0x3f, 0x53, 0xfa, 0xce, 0xdc, 0x1d, 0xd3, 0xc5, 0xa1, - 0x23, 0x39, 0xbc, 0x2e, 0x7e, 0x22, 0x4d, 0x11, 0xfd, 0x86, 0xe9, 0xa7, 0x71, 0x88, 0x37, 0xb1, - 0xea, 0x62, 0x34, 0x86, 0x38, 0x9b, 0x4f, 0x70, 0x5f, 0x5e, 0xff, 0xc6, 0x2c, 0xa2, 0x6f, 0x03, - 0xe0, 0x93, 0xa1, 0xe1, 0xa8, 0xe4, 0x8e, 0xe6, 0xea, 0x1f, 0xa5, 0x88, 0x02, 0xaa, 0x42, 0x22, - 0xf8, 0x24, 0x8f, 0x5d, 0xfb, 0x93, 0x3c, 0x50, 0x45, 0x7b, 0x10, 0xf9, 0xa0, 0x65, 0xa3, 0x1a, - 0xf2, 0xd7, 0x35, 0x82, 0xb9, 0xd0, 0x3f, 0x59, 0xd1, 0x7a, 0xa8, 0x4d, 0xe7, 0x2d, 0x5d, 0xaa, - 0x8b, 0x7e, 0xc4, 0xc1, 0xf2, 0xd0, 0xb1, 0x87, 0x6c, 0xe4, 0xe2, 0xce, 0x57, 0x7b, 0xda, 0xa7, - 0x93, 0x1c, 0xec, 0xfa, 0x5a, 0x72, 0xf7, 0x99, 0xcf, 0x19, 0x82, 0x15, 0xc8, 0x2e, 0x5a, 0x83, - 0x38, 0x1e, 0xda, 0xda, 0x21, 0x9d, 0xfa, 0xc4, 0x24, 0xf6, 0x82, 0x5e, 0x88, 0xf8, 0x1a, 0xe9, - 0xda, 0x62, 0xe5, 0xd5, 0xb3, 0x49, 0x2e, 0x4d, 0x5d, 0x23, 0xf0, 0xd8, 0xd0, 0x79, 0x90, 0x04, - 0xa2, 0x1a, 0x66, 0x34, 0x36, 0x66, 0x11, 0x68, 0x04, 0xce, 0xca, 0xd6, 0x14, 0x25, 0x92, 0x01, - 0xe9, 0xa4, 0x65, 0x45, 0x3d, 0x4f, 0x08, 0xdb, 0x8c, 0xfc, 0x9f, 0x38, 0xc8, 0x94, 0x7a, 0xb6, - 0xe3, 0x91, 0x84, 0x51, 0xb3, 0x3c, 0x67, 0x7c, 0x55, 0x98, 0x3c, 0xfb, 0x44, 0x1a, 0xa9, 0x20, - 0x0c, 0x1d, 0xc3, 0x76, 0x82, 0xdc, 0x18, 0x2f, 0xd7, 0xce, 0x26, 0xb9, 0xd2, 0xe7, 0x8e, 0xb4, - 0x5d, 0x1f, 0x4c, 0x9a, 0xc2, 0xde, 0x17, 0xc8, 0xce, 0x3e, 0x23, 0xbb, 0xfb, 0xfb, 0x22, 0xac, - 0x36, 0xb1, 0x7a, 0x20, 0x9f, 0x58, 0x75, 0x6b, 0x38, 0x22, 0x8e, 0xe2, 0x61, 0xf4, 0x32, 0x4b, - 0x83, 0x2c, 0xf8, 0xb6, 0xae, 0x4e, 0x63, 0xd1, 0x0c, 0xf8, 0x3c, 0xac, 0x38, 0xf8, 0xc0, 0xc1, - 0xee, 0xa1, 0x62, 0x58, 0xc7, 0xaa, 0x69, 0xe8, 0xf4, 0xfe, 0x04, 0x29, 0xe3, 0x93, 0xeb, 0x8c, - 0x3a, 0xb3, 0xf9, 0x10, 0x9e, 0xad, 0xf9, 0xd8, 0x86, 0x75, 0xd7, 0xc3, 0xc3, 0xa1, 0x61, 0xf5, - 0x95, 0x01, 0xf9, 0x5e, 0xc4, 0x96, 0xda, 0x33, 0xb1, 0x9e, 0x4d, 0xd2, 0x15, 0xdc, 0x08, 0x98, - 0x2d, 0x5b, 0xc7, 0x35, 0xc6, 0x42, 0x3d, 0x48, 0xb1, 0x21, 0x21, 0x7e, 0xa2, 0x58, 0xa3, 0x41, - 0x16, 0xbe, 0xa0, 0xc4, 0x06, 0x04, 0x95, 0xe5, 0xdd, 0x0b, 0xdd, 0x09, 0x2f, 0xc6, 0x1b, 0xbc, - 0x10, 0x17, 0x97, 0x58, 0xa7, 0x92, 0x7f, 0x3f, 0x3c, 0xff, 0x87, 0x86, 0xa5, 0x9a, 0xcf, 0x76, - 0xfe, 0xaf, 0x40, 0x36, 0x3a, 0x76, 0xb4, 0x07, 0x03, 0xd5, 0x22, 0xff, 0x47, 0x96, 0xc7, 0x5c, - 0x49, 0x8a, 0x8c, 0x25, 0x2b, 0x8c, 0x5d, 0x21, 0x5c, 0x54, 0x86, 0x74, 0x70, 0x73, 0xac, 0x9d, - 0xe4, 0xe7, 0x69, 0x27, 0x53, 0xbe, 0x0e, 0xeb, 0x28, 0xe7, 0xbd, 0xfd, 0xe9, 0x91, 0x4c, 0x8f, - 0x81, 0xb5, 0x6a, 0xf9, 0xbf, 0x71, 0xb0, 0x52, 0x31, 0x0d, 0x6c, 0x79, 0xb4, 0x92, 0xd5, 0xad, - 0x03, 0x1b, 0xbd, 0x0a, 0xeb, 0xfa, 0x34, 0x4d, 0x2a, 0x7d, 0x6c, 0x61, 0x3f, 0x33, 0x73, 0x34, - 0x31, 0xdc, 0x38, 0x9b, 0xe4, 0x56, 0xa8, 0xf4, 0xce, 0x94, 0x25, 0xad, 0x85, 0x1a, 0x21, 0x15, - 0xbd, 0x02, 0x19, 0x93, 0xc4, 0xbe, 0x72, 0xae, 0x8e, 0xcd, 0xcc, 0x2d, 0x69, 0x33, 0xfa, 0x8a, - 0x30, 0xdc, 0xbc, 0x38, 0xa6, 0x56, 0x86, 0xb6, 0x69, 0x68, 0x41, 0xf3, 0xf2, 0xc2, 0x65, 0x63, - 0xc5, 0x0b, 0xb3, 0xe9, 0x5d, 0xaa, 0x24, 0xad, 0x6b, 0xb3, 0xc8, 0xf9, 0xbf, 0x72, 0x90, 0x0c, - 0x37, 0xfe, 0xc0, 0x1f, 0x5c, 0x5e, 0xf7, 0xe7, 0x09, 0x36, 0xbe, 0x7c, 0x09, 0xe2, 0x74, 0x0f, - 0x57, 0xfc, 0x5e, 0x45, 0xb7, 0x1c, 0xfc, 0x5e, 0x45, 0x85, 0xbf, 0xa4, 0x8d, 0xb2, 0x79, 0xe4, - 0xbd, 0x8f, 0x39, 0x48, 0xd2, 0xdf, 0xcc, 0xe8, 0x54, 0x7b, 0x19, 0x12, 0x7b, 0xed, 0x47, 0xed, - 0xce, 0xeb, 0x6d, 0x71, 0x01, 0x25, 0x20, 0x56, 0x6f, 0xcb, 0x22, 0x87, 0x92, 0x10, 0x7f, 0xd8, - 0xec, 0x94, 0x64, 0x71, 0x91, 0x3c, 0x96, 0xf7, 0xe5, 0x5a, 0x57, 0x8c, 0xa1, 0x1b, 0xb0, 0x52, - 0xad, 0x35, 0xeb, 0xad, 0xba, 0x5c, 0xab, 0x2a, 0x8c, 0x28, 0x20, 0x01, 0x78, 0xb9, 0xde, 0xaa, - 0x89, 0x3c, 0x81, 0xaa, 0xd6, 0x2a, 0xf5, 0x56, 0xa9, 0x29, 0xc6, 0xd1, 0x3a, 0xac, 0x86, 0xb2, - 0x01, 0x39, 0x89, 0x52, 0x20, 0x54, 0xf7, 0xa4, 0x92, 0x5c, 0xef, 0xb4, 0xc5, 0x25, 0x04, 0xb0, - 0x44, 0x74, 0xe5, 0x37, 0xc5, 0x14, 0xb1, 0xbd, 0x53, 0xeb, 0x88, 0x69, 0x6a, 0xb0, 0xf3, 0xc6, - 0x76, 0x55, 0xcc, 0x90, 0x47, 0x79, 0x6f, 0xb7, 0x59, 0x13, 0x81, 0x28, 0x96, 0xeb, 0x72, 0x49, - 0x92, 0x4a, 0xfb, 0xe2, 0x32, 0xca, 0x00, 0x10, 0xc5, 0x6e, 0x4d, 0xaa, 0xd7, 0xba, 0xa2, 0x9e, - 0x27, 0x1f, 0x1e, 0x89, 0x7b, 0x1a, 0xac, 0x3e, 0x35, 0xcc, 0x47, 0x69, 0x48, 0x96, 0xaa, 0x55, - 0xe5, 0x71, 0x47, 0xae, 0x49, 0xe2, 0x02, 0x12, 0x21, 0x25, 0xd5, 0x5a, 0x9d, 0xc7, 0x35, 0x9f, - 0xc2, 0xa1, 0x55, 0x48, 0x13, 0x81, 0x76, 0xa7, 0xed, 0x93, 0x16, 0xd1, 0x1a, 0x88, 0xbe, 0x50, - 0x48, 0x8d, 0x6d, 0xf2, 0xef, 0xfd, 0x7c, 0x6b, 0xe1, 0x5e, 0xe7, 0xdc, 0xf7, 0x2d, 0x6b, 0x26, - 0xc9, 0xd6, 0x77, 0x6b, 0xed, 0x6a, 0xbd, 0xbd, 0x23, 0x2e, 0x90, 0x97, 0xae, 0x5c, 0xda, 0x21, - 0x2f, 0x31, 0x62, 0xbe, 0xd2, 0x69, 0xb5, 0xea, 0xb2, 0x5c, 0xab, 0x8a, 0x1c, 0xe1, 0x95, 0xca, - 0x1d, 0x89, 0xbc, 0x2c, 0xfa, 0x80, 0x65, 0x58, 0x9b, 0x55, 0x1b, 0xd1, 0x0a, 0x2c, 0xef, 0x59, - 0xee, 0x10, 0x6b, 0x74, 0x10, 0x28, 0x2e, 0x90, 0x23, 0xa0, 0x96, 0x0f, 0xb0, 0xc3, 0x90, 0x24, - 0x12, 0x28, 0xae, 0x27, 0x2e, 0xde, 0x7b, 0x17, 0x36, 0x2f, 0x77, 0x07, 0xb4, 0x09, 0x1b, 0xcd, - 0xd2, 0x8e, 0x52, 0xde, 0x57, 0x2a, 0xcd, 0xbd, 0xae, 0x5c, 0x93, 0x94, 0x6e, 0x4d, 0x96, 0xd9, - 0x62, 0x6f, 0xc1, 0x7a, 0xb3, 0x56, 0xaa, 0x2a, 0x0f, 0x3b, 0x92, 0xb2, 0xd3, 0xec, 0x94, 0x4b, - 0x4d, 0x45, 0xaa, 0x95, 0xaa, 0x5d, 0x91, 0x43, 0x39, 0xb8, 0xdd, 0x2a, 0xbd, 0xa1, 0x54, 0x9a, - 0x9d, 0x6e, 0xad, 0xaa, 0xd0, 0xf3, 0x96, 0x4b, 0xad, 0x5d, 0x65, 0xb7, 0xd3, 0xac, 0x57, 0xf6, - 0x83, 0xf5, 0x97, 0xbf, 0xf6, 0xe1, 0x5f, 0xb6, 0x16, 0x3e, 0x3c, 0xdd, 0xe2, 0x3e, 0x3a, 0xdd, - 0xe2, 0x3e, 0x39, 0xdd, 0xe2, 0xfe, 0x7c, 0xba, 0xc5, 0xfd, 0xe4, 0xd3, 0xad, 0x85, 0x8f, 0x3e, - 0xdd, 0x5a, 0xf8, 0xe4, 0xd3, 0xad, 0x85, 0x37, 0x13, 0xbe, 0xcb, 0xf6, 0x96, 0xe8, 0xef, 0xd7, - 0x2f, 0xfe, 0x23, 0x00, 0x00, 0xff, 0xff, 0x24, 0xe6, 0x58, 0xa6, 0xc3, 0x1f, 0x00, 0x00, + // 2651 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0x4f, 0x73, 0x23, 0x47, + 0x15, 0xf7, 0x48, 0x23, 0x69, 0xf4, 0xf4, 0xc7, 0xe3, 0x5e, 0xdb, 0xab, 0x6c, 0xc0, 0x5a, 0x14, + 0x20, 0xcb, 0x16, 0x91, 0x0a, 0x27, 0xa4, 0x52, 0xcb, 0x52, 0x85, 0xfe, 0xad, 0x23, 0xad, 0x64, + 0x39, 0xad, 0xf1, 0x26, 0x9b, 0x84, 0x1a, 0x46, 0x33, 0x6d, 0x79, 0xca, 0xa3, 0x19, 0xed, 0xcc, + 0xc8, 0x59, 0xe5, 0xc6, 0x85, 0x4a, 0xe5, 0x00, 0x14, 0x27, 0x8e, 0xa9, 0xe2, 0x40, 0x15, 0x17, + 0x38, 0x52, 0x7c, 0x82, 0x1c, 0x38, 0xe4, 0x42, 0x25, 0x40, 0x95, 0x0a, 0x9c, 0x4b, 0x0e, 0x7c, + 0x02, 0x17, 0x54, 0x51, 0xdd, 0x3d, 0xd2, 0xc8, 0x5e, 0xd9, 0x68, 0xb3, 0x4b, 0xc8, 0x81, 0x8b, + 0x3d, 0xfd, 0xba, 0xdf, 0xef, 0xbd, 0xee, 0x7e, 0xef, 0xd7, 0xaf, 0x5b, 0x80, 0x5c, 0x47, 0xd3, + 0x0f, 0x87, 0xbd, 0x92, 0xa1, 0xf9, 0x5a, 0x71, 0xe8, 0x3a, 0xbe, 0x83, 0xd6, 0x74, 0x47, 0x3f, + 0x62, 0xf2, 0x62, 0xd0, 0x7b, 0xed, 0xe6, 0xd1, 0x71, 0xe9, 0xe8, 0xd8, 0x23, 0xee, 0x31, 0x71, + 0x4b, 0xba, 0x63, 0xeb, 0x23, 0xd7, 0x25, 0xb6, 0x3e, 0x2e, 0x59, 0x8e, 0x7e, 0xc4, 0xfe, 0x98, + 0x76, 0x9f, 0xab, 0x9f, 0x1d, 0xeb, 0x12, 0xcd, 0xf0, 0x46, 0x83, 0x81, 0xe6, 0x8e, 0x4b, 0xae, + 0x37, 0xec, 0x95, 0x82, 0x46, 0x30, 0x76, 0x73, 0x6a, 0x7e, 0x40, 0x7c, 0x2d, 0x74, 0xe1, 0xda, + 0xb3, 0x9e, 0xef, 0xb8, 0x5a, 0x9f, 0x94, 0x88, 0xdd, 0x37, 0x6d, 0x42, 0x07, 0x1c, 0xeb, 0x7a, + 0xd0, 0xf9, 0x95, 0x85, 0x9d, 0x2f, 0x06, 0xbd, 0xb9, 0x91, 0x6f, 0x5a, 0xa5, 0x43, 0x4b, 0x2f, + 0xf9, 0xe6, 0x80, 0x78, 0xbe, 0x36, 0x18, 0x06, 0x3d, 0xeb, 0x7d, 0xa7, 0xef, 0xb0, 0xcf, 0x12, + 0xfd, 0xe2, 0xd2, 0xc2, 0x7d, 0x10, 0xbb, 0x43, 0xcd, 0x46, 0xcf, 0x40, 0xf4, 0x88, 0x8c, 0x73, + 0xd1, 0xeb, 0xc2, 0x8d, 0x74, 0x25, 0x71, 0x3a, 0xc9, 0x47, 0xef, 0x92, 0x31, 0xa6, 0x32, 0x74, + 0x1d, 0x12, 0xc4, 0x36, 0x54, 0xda, 0x2d, 0x9e, 0xed, 0x8e, 0x13, 0xdb, 0xb8, 0x4b, 0xc6, 0xb7, + 0xa4, 0x5f, 0x7e, 0x90, 0x5f, 0xf9, 0xfd, 0x07, 0x79, 0xa1, 0x29, 0x4a, 0x82, 0x1c, 0x69, 0x8a, + 0x52, 0x44, 0x8e, 0x16, 0xfa, 0x10, 0xbb, 0xa7, 0x59, 0x23, 0x82, 0x9e, 0x85, 0xa4, 0xab, 0xbd, + 0xa3, 0xf6, 0xc6, 0x3e, 0xf1, 0x72, 0x02, 0x85, 0xc0, 0x92, 0xab, 0xbd, 0x53, 0xa1, 0x6d, 0x54, + 0x86, 0xe4, 0xcc, 0xd3, 0x5c, 0xe4, 0xba, 0x70, 0x23, 0xb5, 0xfd, 0xd5, 0x62, 0xb8, 0x05, 0x74, + 0x3a, 0xc5, 0x43, 0x4b, 0x2f, 0x2a, 0xd3, 0x41, 0x15, 0xf1, 0xc3, 0x49, 0x7e, 0x05, 0x87, 0x5a, + 0x85, 0xb7, 0x40, 0xba, 0x4b, 0xc6, 0xdc, 0x56, 0x30, 0x0f, 0x61, 0xc1, 0x3c, 0x5e, 0x82, 0xd8, + 0x31, 0x1d, 0x13, 0x58, 0xc9, 0x15, 0x1f, 0xd9, 0xe8, 0x22, 0xc3, 0x08, 0x0c, 0xf0, 0xc1, 0x85, + 0x8f, 0x05, 0x80, 0xae, 0xef, 0xb8, 0xa4, 0x61, 0x10, 0xdb, 0x47, 0x7d, 0x00, 0xdd, 0x1a, 0x79, + 0x3e, 0x71, 0x55, 0xd3, 0x08, 0xcc, 0xbc, 0x4a, 0xc7, 0xff, 0x65, 0x92, 0x7f, 0xb1, 0x6f, 0xfa, + 0x87, 0xa3, 0x5e, 0x51, 0x77, 0x06, 0xa5, 0x19, 0xb6, 0xd1, 0x0b, 0xbf, 0x4b, 0xc3, 0xa3, 0x7e, + 0x89, 0x6d, 0xd0, 0x68, 0x64, 0x1a, 0xc5, 0xfd, 0xfd, 0x46, 0xed, 0x64, 0x92, 0x4f, 0x56, 0x39, + 0x60, 0xa3, 0x86, 0x93, 0x01, 0x76, 0xc3, 0x40, 0x2f, 0x40, 0xc2, 0x76, 0x0c, 0x42, 0xad, 0x50, + 0x7f, 0x63, 0x95, 0xf5, 0x93, 0x49, 0x3e, 0xbe, 0xeb, 0x18, 0xa4, 0x51, 0x3b, 0x9d, 0x7d, 0xe1, + 0x38, 0x1d, 0xd4, 0x30, 0xd0, 0x77, 0x40, 0xa2, 0x71, 0xc1, 0xc6, 0x47, 0xd9, 0xf8, 0xcd, 0x93, + 0x49, 0x3e, 0xc1, 0x3d, 0xa7, 0x0a, 0xd3, 0x4f, 0x9c, 0xf0, 0xf8, 0x6c, 0x0a, 0xbf, 0x11, 0x20, + 0xdd, 0x1d, 0x5a, 0xa6, 0xaf, 0xb8, 0x66, 0xbf, 0x4f, 0x5c, 0x54, 0x87, 0xa4, 0x45, 0x0e, 0x7c, + 0xd5, 0x20, 0x9e, 0xce, 0xa6, 0x96, 0xda, 0x2e, 0x2c, 0x58, 0x24, 0xac, 0xd9, 0x7d, 0x52, 0x23, + 0x9e, 0xee, 0x9a, 0x43, 0xdf, 0x71, 0x83, 0xe5, 0x92, 0xa8, 0x2a, 0x95, 0xa2, 0x1d, 0x00, 0xd7, + 0xec, 0x1f, 0x06, 0x38, 0x91, 0xc7, 0xc4, 0x49, 0x32, 0x5d, 0x2a, 0xbe, 0x25, 0x7e, 0xc6, 0x43, + 0x2a, 0x2a, 0x8b, 0x85, 0x3f, 0x89, 0x90, 0x6e, 0x13, 0xb7, 0x4f, 0xbe, 0xa4, 0xce, 0xa2, 0x3e, + 0xc8, 0x1c, 0x88, 0x66, 0xa3, 0xea, 0xf9, 0x9a, 0xef, 0xb1, 0x74, 0x49, 0x6d, 0x7f, 0x63, 0x0e, + 0x2e, 0xc8, 0xdd, 0xe2, 0x34, 0x77, 0x8b, 0xed, 0x7b, 0xd5, 0x6a, 0x97, 0x0e, 0xae, 0x6c, 0x52, + 0xc4, 0x93, 0x49, 0x3e, 0x8b, 0x29, 0xcc, 0x4c, 0x8e, 0xb3, 0x0c, 0xb6, 0x7d, 0xac, 0xeb, 0xac, + 0x8d, 0x7e, 0x22, 0x40, 0xfa, 0xc0, 0x25, 0xe4, 0x5d, 0x42, 0xad, 0xb8, 0x7e, 0x2e, 0xb6, 0x4c, + 0xd2, 0xd4, 0x28, 0xfa, 0xe9, 0x24, 0x7f, 0x7b, 0xf9, 0x18, 0xa5, 0x00, 0x55, 0x4a, 0x6e, 0x33, + 0x14, 0x9c, 0xe2, 0x86, 0xbb, 0xd4, 0x2e, 0xba, 0x0f, 0x9b, 0x7c, 0xc6, 0xba, 0xe5, 0x78, 0xc4, + 0x50, 0xc3, 0x34, 0x8e, 0x2f, 0x9f, 0xc6, 0xeb, 0x0c, 0xa2, 0xca, 0x10, 0x66, 0x7d, 0xe8, 0x6d, + 0x40, 0x1c, 0x9a, 0x12, 0xa8, 0x1a, 0x90, 0x66, 0x2e, 0xc1, 0x60, 0x8b, 0x73, 0xb0, 0x47, 0xc7, + 0xc5, 0x29, 0xd7, 0x16, 0xe7, 0xb8, 0xb6, 0x88, 0x89, 0x66, 0x74, 0xf9, 0x37, 0xe6, 0xdb, 0x32, + 0x27, 0x39, 0x13, 0x57, 0xbf, 0x8e, 0xc0, 0x46, 0xf5, 0x90, 0x6e, 0x2e, 0x26, 0x43, 0xcb, 0xd4, + 0x35, 0x6f, 0x1a, 0x60, 0x2f, 0x83, 0xc8, 0x62, 0x22, 0xb6, 0x6c, 0x4c, 0x60, 0x36, 0x1e, 0xf5, + 0xe0, 0xaa, 0x69, 0xfb, 0xc4, 0xb5, 0x35, 0x4b, 0xd5, 0x0c, 0x83, 0x18, 0xaa, 0x1b, 0x20, 0xe7, + 0xe2, 0xd7, 0xa3, 0x37, 0x52, 0xdb, 0x5f, 0x5f, 0x04, 0xc5, 0x87, 0x3c, 0x12, 0x60, 0x1b, 0x53, + 0xa8, 0x32, 0x45, 0x9a, 0xba, 0x88, 0x0e, 0xe0, 0x99, 0x99, 0x0d, 0x97, 0x0c, 0x9c, 0xe3, 0x79, + 0x2b, 0x89, 0xc7, 0xb6, 0x32, 0x73, 0x18, 0x73, 0xac, 0xa9, 0x9d, 0x5b, 0x22, 0x25, 0x76, 0x46, + 0xea, 0xb1, 0xc2, 0xfb, 0x02, 0x5c, 0x69, 0x3b, 0x86, 0x79, 0x60, 0x12, 0x83, 0x1e, 0x19, 0xd3, + 0x75, 0xfa, 0x36, 0x20, 0x6f, 0xec, 0xf9, 0x64, 0xa0, 0xea, 0x8e, 0x7d, 0x60, 0xf6, 0x55, 0x6f, + 0xa8, 0xd9, 0x2c, 0x23, 0x25, 0x2c, 0xf3, 0x9e, 0x2a, 0xeb, 0x60, 0xe7, 0x4c, 0x1d, 0x10, 0xa3, + 0x35, 0xcb, 0x3c, 0x26, 0x36, 0xf1, 0x3c, 0x3e, 0x9a, 0xe7, 0xdd, 0xd5, 0x05, 0x2e, 0x53, 0x25, + 0x2c, 0x53, 0x95, 0x56, 0xa0, 0x41, 0x25, 0x85, 0x7b, 0x20, 0x77, 0x7d, 0x53, 0x3f, 0x1a, 0x57, + 0x42, 0xfa, 0xaa, 0x00, 0x78, 0x4c, 0xa6, 0xf6, 0x4c, 0x3f, 0xa0, 0x84, 0xe5, 0x8e, 0x12, 0x6f, + 0x0a, 0x55, 0xf8, 0x43, 0x14, 0x36, 0x1a, 0xc1, 0x62, 0x54, 0x9d, 0xc1, 0x20, 0x44, 0xaf, 0x41, + 0xc6, 0xa3, 0x64, 0xa9, 0xfa, 0x5c, 0x10, 0x18, 0xc8, 0x2f, 0xf4, 0x39, 0x24, 0x55, 0x9c, 0xf6, + 0xe6, 0x29, 0xb6, 0x06, 0x99, 0x01, 0x65, 0xb1, 0x19, 0x4a, 0xe4, 0x42, 0x94, 0x79, 0xb6, 0xc3, + 0xe9, 0xc1, 0x3c, 0xf7, 0xfd, 0x08, 0xae, 0xea, 0x2c, 0x66, 0x67, 0x9b, 0x3e, 0xc3, 0x8b, 0x32, + 0xbc, 0x1b, 0x0b, 0xf0, 0x16, 0x46, 0x39, 0xde, 0xd0, 0x17, 0x06, 0xff, 0x9b, 0xb0, 0x31, 0x08, + 0xf6, 0x9a, 0xed, 0xd0, 0x0c, 0x9f, 0x53, 0xda, 0x37, 0x17, 0xf9, 0xfb, 0x68, 0x6c, 0xe0, 0x2b, + 0x83, 0x05, 0x01, 0xf3, 0x1a, 0xa0, 0x70, 0x9f, 0x66, 0xc0, 0x3c, 0xcd, 0x9e, 0x5b, 0xb4, 0x9c, + 0xe7, 0x36, 0x1a, 0xcb, 0xde, 0x39, 0xc9, 0x2d, 0xf1, 0xbd, 0x0f, 0xf2, 0x42, 0xe1, 0x8f, 0x02, + 0xac, 0x75, 0x7a, 0x8c, 0x0e, 0xe6, 0xb8, 0x64, 0xee, 0x20, 0x15, 0x96, 0x38, 0x48, 0x7f, 0x2c, + 0x3c, 0x76, 0x41, 0xf2, 0x74, 0xb8, 0x35, 0xb4, 0x7a, 0x4b, 0xa4, 0xb5, 0x54, 0xe1, 0x34, 0x0e, + 0x29, 0xc5, 0xd5, 0x6c, 0x4f, 0xd3, 0x7d, 0xd3, 0xb1, 0x51, 0x19, 0x44, 0x5a, 0x27, 0x06, 0x81, + 0xf7, 0xdc, 0x65, 0xa7, 0x8a, 0xf2, 0xd0, 0x6e, 0x13, 0x5f, 0xab, 0x48, 0xd4, 0xb3, 0x8f, 0x26, + 0x79, 0x01, 0x33, 0x55, 0x84, 0x40, 0xb4, 0xb5, 0x01, 0xaf, 0x80, 0x92, 0x98, 0x7d, 0xa3, 0xdb, + 0x10, 0xa7, 0xa7, 0xd5, 0x88, 0x1f, 0x57, 0xd9, 0x85, 0xc4, 0x31, 0xe7, 0x46, 0x97, 0x8d, 0xc5, + 0x81, 0x0e, 0x6a, 0x42, 0xd6, 0xd2, 0x3c, 0x5f, 0x3d, 0x24, 0x9a, 0xeb, 0xf7, 0x88, 0xb6, 0xe4, + 0x71, 0xc4, 0x13, 0x2f, 0x43, 0x55, 0x5f, 0x9d, 0x6a, 0xa2, 0x97, 0x60, 0x53, 0x67, 0x39, 0x17, + 0x1e, 0x25, 0xea, 0x81, 0xf9, 0x90, 0x18, 0x39, 0x99, 0xb1, 0xc9, 0x3a, 0xef, 0x9d, 0xa1, 0xdc, + 0xa1, 0x7d, 0xd4, 0x03, 0x76, 0x4a, 0x84, 0x9b, 0xb6, 0xfa, 0x18, 0x1e, 0x50, 0xd5, 0x30, 0x56, + 0x7e, 0x08, 0xb9, 0xbe, 0xe5, 0xf4, 0x34, 0x4b, 0x1d, 0xd9, 0x3a, 0x71, 0x7d, 0xcd, 0xb4, 0xfd, + 0xb1, 0x6a, 0x99, 0x03, 0xd3, 0x0f, 0x4e, 0x9f, 0xa5, 0x50, 0x37, 0x39, 0xc8, 0x7e, 0x88, 0xd1, + 0xa2, 0x10, 0xe8, 0x2d, 0xb8, 0xe2, 0x04, 0xf1, 0x19, 0xba, 0xeb, 0xe5, 0xa4, 0x0b, 0x09, 0xfb, + 0x91, 0x68, 0x0e, 0x0c, 0x20, 0xe7, 0x7c, 0x87, 0x87, 0x0a, 0x90, 0x79, 0xc7, 0x35, 0x7d, 0xa2, + 0xfa, 0x8e, 0xa3, 0x3a, 0x96, 0x91, 0x4b, 0xb3, 0x45, 0x4b, 0x31, 0xa1, 0xe2, 0x38, 0x1d, 0xcb, + 0x40, 0xb7, 0x01, 0x68, 0xd0, 0xb1, 0x94, 0xf6, 0x72, 0x29, 0x66, 0xf7, 0x22, 0xd6, 0x9d, 0x92, + 0x23, 0x55, 0xa0, 0x6d, 0x0f, 0xbd, 0x06, 0xb2, 0x69, 0xab, 0x07, 0x16, 0x3b, 0x99, 0x19, 0xac, + 0x97, 0x5b, 0x63, 0x18, 0x5f, 0x5b, 0x84, 0x41, 0x1e, 0x8c, 0x88, 0xad, 0x13, 0xe3, 0x75, 0x3a, + 0x32, 0x40, 0xcb, 0x9a, 0xf6, 0x1d, 0xa6, 0xcf, 0x84, 0x1e, 0x72, 0x60, 0xd5, 0xec, 0xdb, 0x8e, + 0x4b, 0x69, 0x86, 0x3c, 0xb0, 0x47, 0x03, 0x2f, 0x87, 0x18, 0x62, 0xf1, 0xb2, 0xf0, 0x6e, 0x70, + 0x95, 0x2e, 0x79, 0xb0, 0x3b, 0x1a, 0xb0, 0x53, 0x38, 0xac, 0x9e, 0xce, 0xf4, 0x79, 0x38, 0x6b, + 0xce, 0xda, 0x14, 0xfd, 0xcc, 0x55, 0x25, 0x2a, 0x8b, 0x4d, 0x51, 0x8a, 0xcb, 0x89, 0xa6, 0x28, + 0x25, 0x65, 0x68, 0x8a, 0x52, 0x46, 0xce, 0x36, 0x45, 0x29, 0x2b, 0xaf, 0x16, 0x7e, 0x27, 0xc2, + 0xda, 0x5c, 0xd4, 0x63, 0xa2, 0x3b, 0xae, 0xf1, 0x34, 0x52, 0xf0, 0xcb, 0x93, 0x6e, 0xff, 0x0f, + 0x06, 0x31, 0x08, 0x84, 0x88, 0x1c, 0x3d, 0x17, 0x0e, 0x09, 0x59, 0x6a, 0x8a, 0x92, 0x24, 0x27, + 0x67, 0xa1, 0x01, 0x72, 0xaa, 0x29, 0x4a, 0x69, 0x39, 0x33, 0x1f, 0x26, 0x4d, 0x51, 0x5a, 0x95, + 0xe5, 0xa6, 0x28, 0xc9, 0xf2, 0x5a, 0x61, 0x22, 0x40, 0x9c, 0xd6, 0x0e, 0x36, 0x2d, 0x8d, 0x57, + 0x3d, 0xd3, 0xee, 0x5b, 0x84, 0xde, 0x9a, 0xc3, 0x82, 0x28, 0xb5, 0xfd, 0xfc, 0x82, 0xb5, 0xe1, + 0x3a, 0xc5, 0x2e, 0x53, 0xb8, 0x4b, 0xc6, 0x6c, 0xbd, 0xc3, 0xb0, 0xc9, 0x78, 0xf3, 0x1d, 0xe8, + 0x7b, 0x10, 0xf5, 0x1f, 0x4e, 0x2b, 0xa6, 0xa5, 0x22, 0x90, 0x2f, 0x36, 0xd5, 0xba, 0x76, 0x1b, + 0x32, 0x67, 0xcc, 0x5c, 0x72, 0xed, 0x9f, 0xbf, 0xca, 0x37, 0x45, 0x49, 0x94, 0x63, 0x85, 0x3f, + 0x0b, 0xb0, 0xda, 0x72, 0xf4, 0xa3, 0xb2, 0xfe, 0x60, 0x64, 0x7a, 0x26, 0x3b, 0x94, 0xbe, 0x0b, + 0xe2, 0xdc, 0xf4, 0x2e, 0x0c, 0x9f, 0xb9, 0x2c, 0xf0, 0x9e, 0x74, 0x16, 0x08, 0x03, 0x18, 0x23, + 0x57, 0xeb, 0x99, 0x96, 0xe9, 0x73, 0xdf, 0xb3, 0xdb, 0xdb, 0x17, 0xdc, 0x0a, 0xe6, 0x5e, 0x6b, + 0x8a, 0x34, 0x78, 0x8b, 0xb5, 0x99, 0x26, 0x9e, 0x43, 0x29, 0xfc, 0x36, 0x02, 0x40, 0xe7, 0xb6, + 0x3f, 0x34, 0x34, 0x9f, 0xfc, 0x4f, 0xa6, 0x15, 0x32, 0x43, 0xf4, 0x73, 0x30, 0xc3, 0x82, 0xe4, + 0x11, 0xff, 0x9b, 0xc9, 0x53, 0xf8, 0x99, 0x00, 0xd9, 0xb3, 0x69, 0x7d, 0xd9, 0xe3, 0xcb, 0xdb, + 0x20, 0x79, 0xc1, 0xe0, 0xe0, 0x3d, 0xe3, 0x07, 0x4b, 0x57, 0x4c, 0xe7, 0x1f, 0xbc, 0xe8, 0xe2, + 0x75, 0xc9, 0x03, 0x3c, 0x43, 0x0c, 0x0a, 0xa6, 0x5f, 0xc4, 0x20, 0xd6, 0x22, 0x9a, 0x47, 0xd0, + 0x18, 0x62, 0xfc, 0x6e, 0x2c, 0x7c, 0x71, 0xf5, 0x1b, 0xb7, 0x88, 0xbe, 0x0f, 0x40, 0x1e, 0x0e, + 0x4d, 0x57, 0xa3, 0x7b, 0xb4, 0x54, 0xfd, 0x88, 0xe7, 0x14, 0x50, 0x0d, 0x12, 0x41, 0x4d, 0x1f, + 0x94, 0xf2, 0x8f, 0x73, 0x8f, 0x9b, 0xaa, 0xa2, 0x7d, 0xb8, 0x6a, 0x90, 0xa1, 0x4b, 0x74, 0xcd, + 0xa7, 0xf1, 0x40, 0x1d, 0xa3, 0x7f, 0x3d, 0x73, 0xfa, 0x26, 0xf1, 0x1f, 0x3c, 0xda, 0x08, 0xb5, + 0xd9, 0x5d, 0xbf, 0xcb, 0x74, 0xd1, 0x4f, 0x05, 0x48, 0x0d, 0x5d, 0x67, 0xc8, 0xaf, 0xfb, 0xde, + 0x72, 0x67, 0xcf, 0xee, 0xc9, 0x24, 0x0f, 0x7b, 0x81, 0x96, 0xd2, 0x7d, 0xe2, 0x75, 0x86, 0xa9, + 0x07, 0x8a, 0x87, 0xd6, 0x21, 0x46, 0x86, 0x8e, 0x7e, 0xc8, 0x5e, 0x1c, 0xa2, 0x98, 0x37, 0xd0, + 0x0b, 0x73, 0xb1, 0x46, 0xab, 0xb6, 0x68, 0x65, 0xed, 0x74, 0x92, 0xcf, 0xb0, 0xd0, 0x98, 0x46, + 0x6c, 0x18, 0x3c, 0x08, 0x83, 0xac, 0x85, 0x8c, 0xa6, 0xfa, 0xe3, 0x21, 0xc9, 0x49, 0x2c, 0x03, + 0x17, 0xb1, 0x35, 0x43, 0x99, 0x63, 0x40, 0x65, 0x3c, 0x24, 0x78, 0x55, 0x3b, 0x2b, 0x08, 0xcb, + 0x8c, 0xc2, 0x5f, 0x05, 0xc8, 0x96, 0x7b, 0x8e, 0xeb, 0x53, 0xc2, 0xa8, 0xdb, 0xbe, 0x3b, 0xbe, + 0x2c, 0x4d, 0x9e, 0xfc, 0x35, 0x14, 0x69, 0x20, 0x0d, 0x5d, 0xd3, 0x71, 0xa7, 0xdc, 0x18, 0xab, + 0xd4, 0x4f, 0x27, 0xf9, 0xf2, 0xe7, 0xce, 0xb4, 0xbd, 0x00, 0x0c, 0xcf, 0x60, 0x6f, 0x49, 0x74, + 0x66, 0x9f, 0xd1, 0xd9, 0xfd, 0x2b, 0x02, 0x6b, 0x2d, 0xa2, 0x1d, 0x28, 0x0f, 0xed, 0x86, 0x3d, + 0x1c, 0xd1, 0x40, 0xf1, 0x09, 0x7a, 0x99, 0xd3, 0x20, 0x4f, 0xbe, 0xad, 0xcb, 0x69, 0x6c, 0x9e, + 0x01, 0x9f, 0x87, 0x55, 0x97, 0x1c, 0xb8, 0xc4, 0x3b, 0x54, 0x4d, 0xfb, 0x58, 0xb3, 0x4c, 0x83, + 0xed, 0x9f, 0x84, 0xb3, 0x81, 0xb8, 0xc1, 0xa5, 0x0b, 0x8b, 0x0f, 0xe9, 0xc9, 0x8a, 0x8f, 0x6d, + 0xd8, 0xf0, 0x7c, 0x32, 0x1c, 0x9a, 0x76, 0x5f, 0x1d, 0xd0, 0xfb, 0x22, 0xb1, 0xb5, 0x9e, 0x45, + 0x8c, 0x5c, 0x92, 0x79, 0x70, 0x65, 0xda, 0xd9, 0x76, 0x0c, 0x52, 0xe7, 0x5d, 0xa8, 0x07, 0x69, + 0xfe, 0x40, 0x45, 0x1e, 0xa8, 0xf6, 0x68, 0x90, 0x83, 0xa7, 0x44, 0x6c, 0x40, 0x51, 0x39, 0xef, + 0x9e, 0xab, 0x4e, 0x44, 0x39, 0xd6, 0x14, 0xa5, 0x98, 0x1c, 0xe7, 0x95, 0x4a, 0xe1, 0xfd, 0x70, + 0xfd, 0xef, 0x98, 0xb6, 0x66, 0x3d, 0xd9, 0xfa, 0xbf, 0x02, 0xb9, 0x39, 0xda, 0xa0, 0xb7, 0x2d, + 0xcd, 0xa6, 0xff, 0x47, 0xb6, 0xcf, 0x43, 0x09, 0x6f, 0x86, 0xfd, 0x55, 0xde, 0x5d, 0xa5, 0xbd, + 0xa8, 0x02, 0x99, 0xe9, 0xce, 0xf1, 0x72, 0x52, 0x5c, 0xa6, 0x9c, 0x4c, 0x07, 0x3a, 0xbc, 0xa2, + 0x5c, 0x76, 0xf7, 0x67, 0x4b, 0x32, 0x5b, 0x06, 0x5e, 0xaa, 0x15, 0xfe, 0x29, 0xc0, 0x6a, 0xd5, + 0x32, 0x89, 0xed, 0xb3, 0x93, 0xac, 0x61, 0x1f, 0x38, 0xe8, 0x55, 0xd8, 0x30, 0x66, 0x34, 0xa9, + 0xf6, 0x89, 0x4d, 0x02, 0x66, 0x16, 0x18, 0x31, 0x5c, 0x39, 0x9d, 0xe4, 0x57, 0xd9, 0xe8, 0x9d, + 0x59, 0x17, 0x5e, 0x0f, 0x35, 0x42, 0x29, 0x7a, 0x05, 0xb2, 0x16, 0xcd, 0x7d, 0xf5, 0xcc, 0x39, + 0xb6, 0x90, 0x5b, 0x32, 0xd6, 0x7c, 0x13, 0x11, 0xb8, 0x7a, 0xfe, 0x89, 0x54, 0x1d, 0x3a, 0x96, + 0xa9, 0x4f, 0x8b, 0x97, 0x17, 0x2e, 0x7a, 0x5c, 0x3c, 0xf7, 0x2e, 0xba, 0xc7, 0x94, 0xf0, 0x86, + 0xbe, 0x48, 0x5c, 0xf8, 0x87, 0x00, 0xc9, 0x70, 0xe2, 0xb7, 0x83, 0xe7, 0xcb, 0xc7, 0x7d, 0x1a, + 0xe7, 0x8f, 0x98, 0x2f, 0x41, 0x8c, 0xcd, 0xe1, 0x92, 0xdf, 0x4a, 0xd8, 0x94, 0xa7, 0xbf, 0x95, + 0xb0, 0xc1, 0x5f, 0xd0, 0x44, 0xf9, 0xab, 0xe4, 0xcd, 0x8f, 0x05, 0x48, 0xb2, 0xdf, 0x6b, 0x28, + 0xe1, 0xa2, 0x14, 0x24, 0xf6, 0x77, 0xef, 0xee, 0x76, 0x5e, 0xdf, 0x95, 0x57, 0x50, 0x02, 0xa2, + 0x8d, 0x5d, 0x45, 0x16, 0x50, 0x12, 0x62, 0x77, 0x5a, 0x9d, 0xb2, 0x22, 0x47, 0xe8, 0x67, 0xe5, + 0xbe, 0x52, 0xef, 0xca, 0x51, 0x74, 0x05, 0x56, 0x6b, 0xf5, 0x56, 0xa3, 0xdd, 0x50, 0xea, 0x35, + 0x95, 0x0b, 0x25, 0x24, 0x81, 0xa8, 0x34, 0xda, 0x75, 0x59, 0xa4, 0x50, 0xb5, 0x7a, 0xb5, 0xd1, + 0x2e, 0xb7, 0xe4, 0x18, 0xda, 0x80, 0xb5, 0x70, 0xec, 0x54, 0x9c, 0x44, 0x69, 0x90, 0x6a, 0xfb, + 0xb8, 0xac, 0x34, 0x3a, 0xbb, 0x72, 0x1c, 0x01, 0xc4, 0xa9, 0xae, 0xf2, 0xa6, 0x9c, 0xa6, 0xb6, + 0x77, 0xea, 0x1d, 0x39, 0xc3, 0x0c, 0x76, 0xde, 0xd8, 0xae, 0xc9, 0x59, 0xfa, 0xa9, 0xec, 0xef, + 0xb5, 0xea, 0x32, 0x50, 0xc5, 0x4a, 0x43, 0x29, 0x63, 0x5c, 0xbe, 0x2f, 0xa7, 0x50, 0x16, 0x80, + 0x2a, 0x76, 0xeb, 0xb8, 0x51, 0xef, 0xca, 0x46, 0x81, 0x5e, 0x3c, 0x12, 0x37, 0x75, 0x58, 0x0b, + 0x4e, 0x78, 0xfe, 0x66, 0xc7, 0x26, 0x98, 0x81, 0x64, 0xb9, 0x56, 0x53, 0xef, 0x75, 0x94, 0x3a, + 0x96, 0x57, 0x90, 0x0c, 0x69, 0x5c, 0x6f, 0x77, 0xee, 0xd5, 0x03, 0x89, 0x80, 0xd6, 0x20, 0x43, + 0x07, 0xec, 0x76, 0x76, 0x03, 0x51, 0x04, 0xad, 0x83, 0x1c, 0x0c, 0x0a, 0xa5, 0xd1, 0x6b, 0xe2, + 0x7b, 0xbf, 0xda, 0x5a, 0xb9, 0xd9, 0x39, 0x73, 0xbf, 0xe5, 0xc5, 0x24, 0x9d, 0xfa, 0x5e, 0x7d, + 0xb7, 0xd6, 0xd8, 0xdd, 0x91, 0x57, 0x68, 0xa3, 0xab, 0x94, 0x77, 0x68, 0x23, 0x4a, 0xcd, 0x57, + 0x3b, 0xed, 0x76, 0x43, 0x51, 0xea, 0x35, 0x59, 0xa0, 0x7d, 0xe5, 0x4a, 0x07, 0xd3, 0x46, 0x24, + 0x00, 0xac, 0xc0, 0xfa, 0xa2, 0xb3, 0x11, 0xad, 0x42, 0x6a, 0xdf, 0xf6, 0x86, 0x44, 0x67, 0x0f, + 0x81, 0xf2, 0x0a, 0x5d, 0x02, 0x66, 0xf9, 0x80, 0xb8, 0x1c, 0x09, 0xd3, 0x44, 0xf1, 0x7c, 0x39, + 0x72, 0xf3, 0x5d, 0xb8, 0x76, 0x71, 0x38, 0xa0, 0x6b, 0xb0, 0xd9, 0x2a, 0xef, 0xa8, 0x95, 0xfb, + 0x6a, 0xb5, 0xb5, 0xdf, 0x55, 0xea, 0x58, 0xed, 0xd6, 0x15, 0x85, 0x3b, 0xfb, 0x0c, 0x6c, 0xb4, + 0xea, 0xe5, 0x9a, 0x7a, 0xa7, 0x83, 0xd5, 0x9d, 0x56, 0xa7, 0x52, 0x6e, 0xa9, 0xb8, 0x5e, 0xae, + 0x75, 0x65, 0x01, 0xe5, 0xe1, 0xd9, 0x76, 0xf9, 0x0d, 0xb5, 0xda, 0xea, 0x74, 0xeb, 0x35, 0x95, + 0xad, 0xb7, 0x52, 0x6e, 0xef, 0xa9, 0x7b, 0x9d, 0x56, 0xa3, 0x7a, 0x7f, 0xea, 0x7f, 0xe5, 0x5b, + 0x1f, 0xfe, 0x7d, 0x6b, 0xe5, 0xc3, 0x93, 0x2d, 0xe1, 0xa3, 0x93, 0x2d, 0xe1, 0x93, 0x93, 0x2d, + 0xe1, 0x6f, 0x27, 0x5b, 0xc2, 0xcf, 0x3f, 0xdd, 0x5a, 0xf9, 0xe8, 0xd3, 0xad, 0x95, 0x4f, 0x3e, + 0xdd, 0x5a, 0x79, 0x33, 0x11, 0x84, 0x6c, 0x2f, 0xce, 0x7e, 0x3b, 0x7d, 0xf1, 0xdf, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xb7, 0xa4, 0xb3, 0xfb, 0x3f, 0x1e, 0x00, 0x00, } func (this *SplitTrigger) Equal(that interface{}) bool { @@ -2199,40 +2178,6 @@ func (m *ChangeReplicasTrigger) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if m.DeprecatedNextReplicaID != 0 { - i = encodeVarintData(dAtA, i, uint64(m.DeprecatedNextReplicaID)) - i-- - dAtA[i] = 0x20 - } - if len(m.DeprecatedUpdatedReplicas) > 0 { - for iNdEx := len(m.DeprecatedUpdatedReplicas) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.DeprecatedUpdatedReplicas[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintData(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - } - { - size, err := m.DeprecatedReplica.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintData(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - if m.DeprecatedChangeType != 0 { - i = encodeVarintData(dAtA, i, uint64(m.DeprecatedChangeType)) - i-- - dAtA[i] = 0x8 - } return len(dAtA) - i, nil } @@ -3632,20 +3577,6 @@ func (m *ChangeReplicasTrigger) Size() (n int) { } var l int _ = l - if m.DeprecatedChangeType != 0 { - n += 1 + sovData(uint64(m.DeprecatedChangeType)) - } - l = m.DeprecatedReplica.Size() - n += 1 + l + sovData(uint64(l)) - if len(m.DeprecatedUpdatedReplicas) > 0 { - for _, e := range m.DeprecatedUpdatedReplicas { - l = e.Size() - n += 1 + l + sovData(uint64(l)) - } - } - if m.DeprecatedNextReplicaID != 0 { - n += 1 + sovData(uint64(m.DeprecatedNextReplicaID)) - } if m.Desc != nil { l = m.Desc.Size() n += 1 + l + sovData(uint64(l)) @@ -4913,111 +4844,6 @@ func (m *ChangeReplicasTrigger) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: ChangeReplicasTrigger: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedChangeType", wireType) - } - m.DeprecatedChangeType = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowData - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DeprecatedChangeType |= ReplicaChangeType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedReplica", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowData - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthData - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthData - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.DeprecatedReplica.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedUpdatedReplicas", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowData - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthData - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthData - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.DeprecatedUpdatedReplicas = append(m.DeprecatedUpdatedReplicas, ReplicaDescriptor{}) - if err := m.DeprecatedUpdatedReplicas[len(m.DeprecatedUpdatedReplicas)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field DeprecatedNextReplicaID", wireType) - } - m.DeprecatedNextReplicaID = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowData - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.DeprecatedNextReplicaID |= ReplicaID(b&0x7F) << shift - if b < 0x80 { - break - } - } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Desc", wireType) diff --git a/pkg/roachpb/data.proto b/pkg/roachpb/data.proto index 2668caa914fd..0ca5ba001dc9 100644 --- a/pkg/roachpb/data.proto +++ b/pkg/roachpb/data.proto @@ -220,28 +220,12 @@ enum ReplicaChangeType { message ChangeReplicasTrigger { option (gogoproto.goproto_stringer) = false; - // TODO(tbg): remove once we know that no trigger using this will ever be - // applied (this will require something like #39182). - // - // TODO(tbg): when removing this, also rename internal_x_replicas to just - // x_replicas and remove the getter. - ReplicaChangeType deprecated_change_type = 1; - // The replica being modified. - // TODO(tbg): remove once we know that no trigger using this will ever be - // applied (this will require something like #39182). - ReplicaDescriptor deprecated_replica = 2 [(gogoproto.nullable) = false]; - // The new replica list with this change applied. - repeated ReplicaDescriptor deprecated_updated_replicas = 3 [(gogoproto.nullable) = false]; - // The next replica id to use with this change applied. - int32 deprecated_next_replica_id = 4 [(gogoproto.customname) = "DeprecatedNextReplicaID", (gogoproto.casttype) = "ReplicaID"]; - // The updated range descriptor. If desc is non-nil, then it overrides - // updated_replicas and next_replica_id. This incremental addition is needed - // to maintain backwards compatibility. - // TODO(jeffreyxiao): Remove deprecated_updated_replicas and - // deprecated_next_replica_id in 20.1. + // The updated range descriptor. RangeDescriptor desc = 5; // The new replicas added to the range descriptor in this change, exactly as // they appear in the updated range descriptor. + // + // TODO(tbg): rename internal_x_replicas to just x_replicas and remove the getter. repeated ReplicaDescriptor internal_added_replicas = 6 [(gogoproto.nullable) = false]; // The replicas whose removal is being initiated in this change. If the // replica is still present as an outgoing voter in the updated descriptor @@ -250,6 +234,8 @@ message ChangeReplicasTrigger { // removed from the descriptor in the course of this change (which is itself // not visible to this trigger). repeated ReplicaDescriptor internal_removed_replicas = 7 [(gogoproto.nullable) = false]; + + reserved 1 to 4; } // ModifiedSpanTrigger indicates that a specific span has been modified. From 24474e057f1ba25435eb848ad81eddcf9876ad7f Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 16:33:54 +0200 Subject: [PATCH 126/205] kvserver: improve comment on raft send buffer size Release note: None --- pkg/kv/kvserver/raft_transport.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/kv/kvserver/raft_transport.go b/pkg/kv/kvserver/raft_transport.go index 77ad3976f054..c7fb40036547 100644 --- a/pkg/kv/kvserver/raft_transport.go +++ b/pkg/kv/kvserver/raft_transport.go @@ -39,11 +39,13 @@ import ( const ( // Outgoing messages are queued per-node on a channel of this size. // - // TODO(peter): The normal send buffer size is larger than we would like. It - // is a temporary patch for the issue discussed in #8630 where - // Store.HandleRaftRequest can block applying a preemptive snapshot for a - // long enough period of time that grpc flow control kicks in and messages - // are dropped on the sending side. + // This buffer was sized many moons ago and is very large. If the + // buffer fills up, we drop raft messages, so we'd be in trouble. + // But as is, the buffer can hold to a lot of memory, especially + // during RESTORE/IMPORT where we're routinely sending out SSTs, + // which weigh in at a few mbs each; an individual raft instance + // will limit how many it has in-flight per-follower, but groups + // don't compete among each other for budget. raftSendBufferSize = 10000 // When no message has been queued for this duration, the corresponding From e4d0b61612b6f8e7d7890810aa5f68e420e109c1 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 16:34:12 +0200 Subject: [PATCH 127/205] kvserver: improve comment on split trigger helper Release note: None --- pkg/kv/kvserver/split_trigger_helper.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/kv/kvserver/split_trigger_helper.go b/pkg/kv/kvserver/split_trigger_helper.go index 0bd0f813a76a..fb719a5a8b13 100644 --- a/pkg/kv/kvserver/split_trigger_helper.go +++ b/pkg/kv/kvserver/split_trigger_helper.go @@ -96,13 +96,8 @@ func maybeDropMsgApp( // away and is going to be GC'ed soon; queue a check to make sure this would // happen ASAP. (The leader will probe this replica only once per heartbeat // interval, so we're not going to queue these checks at some high rate). - // - // New replicas only get created through splits or rebalances, so if we - // don't find a left hand side, it was either garbage collected after having - // been removed from the store (see the above comment), or there wasn't a - // split in the first case and this replica was instead created through an - // up-replication for which the preemptive snapshot had been lost (i.e. - // accidentally GC'ed before the replication change succeeded). + // If we don't find a left hand side, it was garbage collected and won't + // be initializing the right-hand side, so we give up. // // Note that there's a subtle case in which the left hand side is caught up // across the split trigger via a snapshot. In that case, since we're looking From 850099ad6c124a0c7a9caa6b9d9fd672a113ff1d Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 16:35:53 +0200 Subject: [PATCH 128/205] kvserver: remove TODO for removing DisableEagerReplicaRemoval It's used in 7+ nontrivial tests that have nothing to do with preemptive snapshots. Release note: None --- pkg/kv/kvserver/store_split.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/kv/kvserver/store_split.go b/pkg/kv/kvserver/store_split.go index 8c120290365a..810965fae7a4 100644 --- a/pkg/kv/kvserver/store_split.go +++ b/pkg/kv/kvserver/store_split.go @@ -42,9 +42,6 @@ func splitPreApply( // // The exception to that is if the DisableEagerReplicaRemoval testing flag is // enabled. - // - // TODO(ajwerner): rethink DisableEagerReplicaRemoval and remove this in - // 20.1 after there are no more preemptive snapshots. _, hasRightDesc := split.RightDesc.GetReplicaDescriptor(r.StoreID()) _, hasLeftDesc := split.LeftDesc.GetReplicaDescriptor(r.StoreID()) if !hasRightDesc || !hasLeftDesc { From 2a1857f11b93273e2cb4dcf742133000f4e9d0aa Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 16:43:05 +0200 Subject: [PATCH 129/205] kvserver: remove snapshot CanDecline flag Only preemptive snapshots specified CanDecline, and we haven't been using those since 19.2. This also removes the corresponding rejection enum reason `SnapshotResponse_DECLINED`, and retires the `server.declined_reservation_timeout` cluster setting. Release note: None --- pkg/kv/kvserver/client_raft_test.go | 5 +- pkg/kv/kvserver/raft.pb.go | 204 +++++++++++----------------- pkg/kv/kvserver/raft.proto | 11 +- pkg/kv/kvserver/replica_command.go | 11 +- pkg/kv/kvserver/store_pool.go | 28 +--- pkg/kv/kvserver/store_pool_test.go | 31 ++--- pkg/kv/kvserver/store_raft.go | 2 +- pkg/kv/kvserver/store_snapshot.go | 64 ++------- pkg/kv/kvserver/store_test.go | 113 ++------------- pkg/settings/registry.go | 1 + 10 files changed, 128 insertions(+), 342 deletions(-) diff --git a/pkg/kv/kvserver/client_raft_test.go b/pkg/kv/kvserver/client_raft_test.go index a53fcb5600df..a6b5a9be9057 100644 --- a/pkg/kv/kvserver/client_raft_test.go +++ b/pkg/kv/kvserver/client_raft_test.go @@ -1327,9 +1327,8 @@ func TestFailedSnapshotFillsReservation(t *testing.T) { rep2Desc, found := desc.GetReplicaDescriptor(2) require.True(t, found) header := kvserver.SnapshotRequest_Header{ - CanDecline: true, - RangeSize: 100, - State: kvserverpb.ReplicaState{Desc: desc}, + RangeSize: 100, + State: kvserverpb.ReplicaState{Desc: desc}, RaftMessageRequest: kvserver.RaftMessageRequest{ RangeID: rep.RangeID, FromReplica: repDesc, diff --git a/pkg/kv/kvserver/raft.pb.go b/pkg/kv/kvserver/raft.pb.go index f57fc144b047..5135d28b9ca3 100644 --- a/pkg/kv/kvserver/raft.pb.go +++ b/pkg/kv/kvserver/raft.pb.go @@ -127,7 +127,6 @@ const ( SnapshotResponse_ACCEPTED SnapshotResponse_Status = 1 SnapshotResponse_APPLIED SnapshotResponse_Status = 2 SnapshotResponse_ERROR SnapshotResponse_Status = 3 - SnapshotResponse_DECLINED SnapshotResponse_Status = 4 ) var SnapshotResponse_Status_name = map[int32]string{ @@ -135,7 +134,6 @@ var SnapshotResponse_Status_name = map[int32]string{ 1: "ACCEPTED", 2: "APPLIED", 3: "ERROR", - 4: "DECLINED", } var SnapshotResponse_Status_value = map[string]int32{ @@ -143,7 +141,6 @@ var SnapshotResponse_Status_value = map[string]int32{ "ACCEPTED": 1, "APPLIED": 2, "ERROR": 3, - "DECLINED": 4, } func (x SnapshotResponse_Status) String() string { @@ -435,10 +432,6 @@ type SnapshotRequest_Header struct { RaftMessageRequest RaftMessageRequest `protobuf:"bytes,2,opt,name=raft_message_request,json=raftMessageRequest,proto3" json:"raft_message_request"` // The estimated size of the range, to be used in reservation decisions. RangeSize int64 `protobuf:"varint,3,opt,name=range_size,json=rangeSize,proto3" json:"range_size,omitempty"` - // can_decline is set on preemptive snapshots, but not those generated - // by raft because at that point it is better to queue up the stream - // than to cancel it. - CanDecline bool `protobuf:"varint,4,opt,name=can_decline,json=canDecline,proto3" json:"can_decline,omitempty"` // The priority of the snapshot. Priority SnapshotRequest_Priority `protobuf:"varint,6,opt,name=priority,proto3,enum=cockroach.kv.kvserver.SnapshotRequest_Priority" json:"priority,omitempty"` // The strategy of the snapshot. @@ -577,89 +570,87 @@ func init() { func init() { proto.RegisterFile("kv/kvserver/raft.proto", fileDescriptor_acdcf79fd972c844) } var fileDescriptor_acdcf79fd972c844 = []byte{ - // 1302 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4f, 0x6f, 0xdb, 0xc6, - 0x12, 0x17, 0x6d, 0x5a, 0xa2, 0x56, 0x56, 0xcc, 0xb7, 0xcf, 0xcf, 0x8f, 0xcf, 0x78, 0x95, 0x5c, - 0x21, 0x6d, 0xdd, 0x7f, 0x14, 0xe2, 0xa4, 0x3d, 0xf4, 0x52, 0xe8, 0x0f, 0x03, 0xcb, 0x72, 0x6c, - 0x67, 0x65, 0xbb, 0x68, 0x8b, 0x96, 0x58, 0x51, 0x2b, 0x99, 0x90, 0xc4, 0x65, 0x96, 0x2b, 0xb5, - 0xca, 0xa7, 0xe8, 0x47, 0xe8, 0xad, 0xb7, 0x7e, 0x8b, 0x02, 0x3e, 0xe6, 0x98, 0x43, 0x61, 0xb4, - 0xca, 0xb1, 0xe8, 0x17, 0xc8, 0xa9, 0xd8, 0xe5, 0x52, 0x56, 0x9c, 0x38, 0x8d, 0x80, 0x06, 0x68, - 0x4f, 0x9e, 0x1d, 0xcd, 0xfc, 0x66, 0x76, 0xe6, 0x37, 0xb3, 0x34, 0xd8, 0xe8, 0x8f, 0xcb, 0xfd, - 0x71, 0x44, 0xd8, 0x98, 0xb0, 0x32, 0xc3, 0x5d, 0x6e, 0x87, 0x8c, 0x72, 0x0a, 0xff, 0xe3, 0x51, - 0xaf, 0xcf, 0x28, 0xf6, 0xce, 0xec, 0xfe, 0xd8, 0x4e, 0x2c, 0x36, 0xd7, 0xa5, 0x2a, 0x6c, 0x97, - 0x09, 0x63, 0x94, 0x45, 0xb1, 0xf1, 0xe6, 0x46, 0xa2, 0x1d, 0x12, 0x8e, 0x3b, 0x98, 0x63, 0xa5, - 0xb7, 0xe7, 0xc1, 0x07, 0xfe, 0x98, 0x04, 0x24, 0x8a, 0x66, 0x42, 0xd8, 0x9e, 0x89, 0xca, 0xbe, - 0x34, 0x6f, 0x9f, 0x08, 0x61, 0xbb, 0x1c, 0x71, 0xcc, 0x89, 0xb2, 0x29, 0x10, 0xee, 0x75, 0x64, - 0xa6, 0xe5, 0xf1, 0x6d, 0xf9, 0x37, 0x6c, 0xcf, 0x25, 0xbe, 0xb9, 0xde, 0xa3, 0x3d, 0x2a, 0xc5, - 0xb2, 0x90, 0x62, 0x6d, 0xe9, 0x77, 0x1d, 0xe4, 0x11, 0xee, 0xf2, 0x5d, 0x82, 0x19, 0x6f, 0x13, - 0xcc, 0xe1, 0xd7, 0xc0, 0x60, 0x38, 0xe8, 0x11, 0xd7, 0xef, 0x58, 0xda, 0x96, 0xb6, 0xad, 0x57, - 0x6b, 0xd3, 0x8b, 0x62, 0x06, 0x09, 0x5d, 0xa3, 0xfe, 0xf4, 0xa2, 0x78, 0xa7, 0xe7, 0xf3, 0xb3, - 0x51, 0xdb, 0xf6, 0xe8, 0xb0, 0x3c, 0x2b, 0x46, 0xa7, 0x7d, 0x29, 0x97, 0xc3, 0x7e, 0xaf, 0xac, - 0x6e, 0x6e, 0x2b, 0x3f, 0x94, 0x91, 0xa0, 0x8d, 0x0e, 0x8c, 0xc0, 0x5a, 0x97, 0xd1, 0xa1, 0xcb, - 0x48, 0x38, 0xf0, 0x3d, 0x2c, 0xc2, 0x2c, 0x6d, 0x69, 0xdb, 0xf9, 0x6a, 0x73, 0x7a, 0x51, 0xcc, - 0xdf, 0x65, 0x74, 0x88, 0xe2, 0x5f, 0x64, 0xb0, 0x8f, 0x17, 0x0b, 0x96, 0x78, 0xa2, 0x7c, 0x77, - 0x0e, 0xa8, 0x03, 0x87, 0x20, 0xcf, 0xe9, 0x7c, 0xc8, 0x65, 0x19, 0xb2, 0x31, 0xbd, 0x28, 0xe6, - 0x8e, 0xe9, 0x5f, 0x11, 0x30, 0xc7, 0xe9, 0x65, 0x38, 0x08, 0x74, 0x4e, 0xd8, 0xd0, 0xd2, 0x45, - 0xfd, 0x90, 0x94, 0xe1, 0x06, 0x48, 0x7b, 0x74, 0x38, 0xf4, 0xb9, 0xb5, 0x22, 0xb5, 0xea, 0x04, - 0x2d, 0x90, 0x79, 0x30, 0xf2, 0x49, 0xe4, 0x11, 0x2b, 0xbd, 0xa5, 0x6d, 0x1b, 0x28, 0x39, 0xc2, - 0x87, 0xe0, 0xff, 0x03, 0xdc, 0xeb, 0xf9, 0x41, 0xcf, 0xed, 0xd2, 0xc1, 0x80, 0x7e, 0x43, 0x58, - 0xe4, 0xd2, 0xc0, 0x4d, 0xcc, 0x8d, 0xad, 0xe5, 0xed, 0xdc, 0xce, 0x6d, 0xfb, 0x85, 0x8c, 0xb4, - 0x67, 0x14, 0xba, 0xa4, 0x95, 0xbd, 0xaf, 0xc4, 0xaa, 0x7e, 0x7e, 0x51, 0x4c, 0xa1, 0xff, 0x29, - 0xf8, 0xbb, 0x09, 0xfa, 0x61, 0x70, 0x5f, 0xc5, 0x3e, 0x02, 0x6f, 0xbd, 0x2c, 0xb6, 0x8b, 0x3d, - 0x6f, 0xc4, 0x30, 0x27, 0x16, 0x90, 0x39, 0xbf, 0x79, 0x2d, 0x52, 0x45, 0x19, 0xee, 0xe9, 0x46, - 0xc6, 0x34, 0x4a, 0x3f, 0xa4, 0x01, 0x14, 0x7c, 0xbb, 0x47, 0xa2, 0x08, 0xf7, 0x08, 0x22, 0x0f, - 0x46, 0x24, 0x7a, 0xfd, 0xa4, 0xfb, 0x0a, 0xac, 0xc5, 0xf8, 0x11, 0xc7, 0x8c, 0xbb, 0x7d, 0x32, - 0xb1, 0x8c, 0x2d, 0x6d, 0x7b, 0xb5, 0xfa, 0xd1, 0xd3, 0x8b, 0xe2, 0xad, 0xc5, 0xb0, 0x9b, 0x64, - 0x82, 0xf2, 0x12, 0xad, 0x25, 0xc0, 0x9a, 0x64, 0x02, 0xef, 0x81, 0xd5, 0x79, 0x4e, 0x4b, 0x42, - 0xe7, 0x76, 0x6e, 0xce, 0x75, 0xe6, 0x0a, 0x61, 0xea, 0x24, 0xf2, 0x98, 0x1f, 0x72, 0xca, 0x54, - 0x2b, 0x72, 0x73, 0x7c, 0x85, 0x0d, 0x00, 0x2e, 0xd9, 0x2a, 0xa9, 0xba, 0x18, 0x58, 0x76, 0xc6, - 0x45, 0x58, 0x06, 0x99, 0x61, 0x5c, 0x6a, 0x49, 0xc6, 0xdc, 0xce, 0x9a, 0x1d, 0xaf, 0x06, 0x5b, - 0x75, 0x40, 0xb9, 0x24, 0x56, 0xf3, 0x74, 0x5c, 0x59, 0x8c, 0x8e, 0xd9, 0x7f, 0x12, 0x1d, 0xe1, - 0x1e, 0x00, 0x67, 0xc9, 0xce, 0x8b, 0xac, 0xb4, 0xcc, 0xfd, 0xe6, 0x35, 0xb9, 0x3f, 0xb3, 0x20, - 0x55, 0xb2, 0x73, 0xde, 0xb0, 0x05, 0xd6, 0x66, 0x27, 0x97, 0x91, 0x28, 0x8c, 0xac, 0xcc, 0xc2, - 0x80, 0x37, 0x66, 0x10, 0x48, 0x20, 0x94, 0xba, 0xe0, 0xbf, 0xcf, 0x0f, 0x4a, 0x15, 0x73, 0xef, - 0x0c, 0x36, 0x81, 0xc1, 0xe2, 0x73, 0x64, 0x69, 0x32, 0xd0, 0xbb, 0x2f, 0x09, 0x74, 0x05, 0x21, - 0x8e, 0x36, 0x03, 0x28, 0x1d, 0x01, 0xeb, 0x19, 0xab, 0x28, 0xa4, 0x41, 0x44, 0x4e, 0x02, 0x9f, - 0x06, 0xd0, 0x06, 0x2b, 0xf2, 0x3d, 0x93, 0x33, 0x99, 0xdb, 0xb1, 0xae, 0x46, 0x09, 0xdb, 0xb6, - 0x23, 0x7e, 0x47, 0xb1, 0xd9, 0x27, 0xfa, 0xf9, 0xf7, 0x45, 0xad, 0xf4, 0xf3, 0x12, 0xf8, 0xf7, - 0x0b, 0x20, 0x5f, 0xfb, 0x90, 0xff, 0x7d, 0xa7, 0xb0, 0x09, 0x56, 0x46, 0xa2, 0xa0, 0x6a, 0x06, - 0xcb, 0xaf, 0xd2, 0xad, 0xb9, 0x3e, 0x28, 0xc0, 0x18, 0xa3, 0xf4, 0x5b, 0x1a, 0xac, 0xb5, 0x02, - 0x1c, 0x46, 0x67, 0x94, 0x27, 0xfb, 0xd3, 0x01, 0xe9, 0x33, 0x82, 0x3b, 0x24, 0xe9, 0xd4, 0x87, - 0xd7, 0x44, 0xb8, 0xe2, 0x67, 0xef, 0x4a, 0x27, 0xa4, 0x9c, 0xe1, 0xdb, 0xc0, 0xe8, 0x8f, 0xdd, - 0xb6, 0x20, 0x99, 0xac, 0xde, 0x6a, 0x35, 0x27, 0x3a, 0xd4, 0x3c, 0x95, 0xbc, 0x43, 0x99, 0xfe, - 0x38, 0x26, 0x60, 0x11, 0xe4, 0x06, 0xb4, 0xe7, 0x92, 0x80, 0x33, 0x9f, 0x44, 0xd6, 0xf2, 0xd6, - 0xf2, 0xf6, 0x2a, 0x02, 0x03, 0xda, 0x73, 0x62, 0x0d, 0x5c, 0x07, 0x2b, 0x5d, 0x3f, 0xc0, 0x03, - 0x79, 0x61, 0x03, 0xc5, 0x87, 0xcd, 0x1f, 0x75, 0x90, 0x8e, 0x23, 0xc2, 0x06, 0x58, 0x91, 0x1f, - 0x2f, 0x72, 0xc9, 0x5c, 0x9f, 0x6f, 0xc4, 0x29, 0xc3, 0x3d, 0x72, 0x59, 0xe5, 0x96, 0x70, 0x4a, - 0xea, 0x21, 0x11, 0x20, 0x06, 0xeb, 0x62, 0xa5, 0xb9, 0x6a, 0x83, 0xb9, 0x8a, 0xd9, 0xaa, 0xfd, - 0x0b, 0x4f, 0x06, 0x64, 0xcf, 0x3f, 0x4f, 0x6f, 0x00, 0xa0, 0x9e, 0x0f, 0xff, 0x21, 0x91, 0x54, - 0x58, 0x46, 0xd9, 0xf8, 0x09, 0xf0, 0x1f, 0x12, 0x51, 0x0e, 0x0f, 0x07, 0x6e, 0x87, 0x78, 0x03, - 0x3f, 0x20, 0xea, 0xce, 0xc0, 0xc3, 0x41, 0x3d, 0xd6, 0x88, 0x81, 0x0d, 0x99, 0x4f, 0x99, 0xcf, - 0x27, 0xf2, 0x91, 0xbf, 0x71, 0x2d, 0x05, 0xae, 0x36, 0xe8, 0x48, 0xb9, 0xa1, 0x19, 0x80, 0x00, - 0x8b, 0xb8, 0xd8, 0x61, 0xbd, 0x89, 0x95, 0x59, 0x08, 0xac, 0xa5, 0xdc, 0xd0, 0x0c, 0x00, 0x7e, - 0x0a, 0x74, 0x3e, 0x09, 0xc5, 0xf2, 0x16, 0x40, 0xef, 0xbf, 0x22, 0xd0, 0xf1, 0x24, 0x24, 0x48, - 0x3a, 0xc2, 0x13, 0xf0, 0x4e, 0x87, 0x84, 0x8c, 0x78, 0x98, 0x93, 0x8e, 0x3b, 0x0a, 0xd4, 0xbc, - 0x88, 0x03, 0x67, 0xa3, 0x20, 0x96, 0xe2, 0x56, 0x1b, 0xb2, 0x2e, 0x37, 0x2f, 0xcd, 0x4f, 0xe6, - 0xac, 0x8f, 0x13, 0x63, 0xd9, 0xe1, 0x3d, 0xdd, 0xd0, 0xcc, 0xa5, 0xd2, 0x1d, 0x60, 0x24, 0x05, - 0x80, 0x39, 0x90, 0x39, 0x39, 0x68, 0x1e, 0x1c, 0x7e, 0x76, 0x60, 0xa6, 0xe0, 0x2a, 0x30, 0x90, - 0x53, 0x3b, 0x3c, 0x75, 0xd0, 0xe7, 0xa6, 0x06, 0xf3, 0x20, 0x8b, 0x9c, 0x6a, 0x65, 0xbf, 0x72, - 0x50, 0x73, 0xcc, 0xa5, 0x92, 0x05, 0x8c, 0xe4, 0xa6, 0xc2, 0xb0, 0x79, 0xea, 0x56, 0x2b, 0xc7, - 0xb5, 0x5d, 0x33, 0x55, 0xba, 0x05, 0x74, 0x91, 0x3a, 0xdc, 0x00, 0xf0, 0xb4, 0x51, 0x71, 0x5b, - 0x07, 0x95, 0xa3, 0xd6, 0xee, 0xe1, 0xb1, 0x7b, 0xff, 0xc4, 0x39, 0x71, 0xcc, 0x94, 0x88, 0xd1, - 0x38, 0x68, 0x1c, 0x37, 0x2a, 0xfb, 0xa6, 0x56, 0xd2, 0x8d, 0x25, 0x73, 0xa9, 0xf4, 0x93, 0x06, - 0xcc, 0xcb, 0xeb, 0xab, 0x4d, 0x76, 0x17, 0xa4, 0xc5, 0x95, 0x46, 0x91, 0x1c, 0xb7, 0x1b, 0x3b, - 0xf6, 0x9f, 0xd6, 0x2d, 0x76, 0xb4, 0x5b, 0xd2, 0x0b, 0x29, 0x6f, 0xf1, 0xd8, 0x26, 0xaf, 0xb3, - 0x60, 0x6b, 0x76, 0xf6, 0x0c, 0x97, 0x1a, 0x20, 0x1d, 0xdb, 0x3e, 0x77, 0xef, 0x4a, 0xad, 0xe6, - 0x1c, 0x1d, 0x3b, 0x75, 0x53, 0x13, 0x3f, 0x55, 0x8e, 0x8e, 0xf6, 0x1b, 0x4e, 0xdd, 0x5c, 0x82, - 0x59, 0xb0, 0xe2, 0x20, 0x74, 0x88, 0xcc, 0x65, 0x61, 0x55, 0x77, 0x6a, 0xfb, 0x8d, 0x03, 0xa7, - 0x6e, 0xea, 0x7b, 0xba, 0xb1, 0x6c, 0xea, 0xa5, 0x2f, 0xc1, 0xbf, 0x6a, 0x34, 0xe8, 0xd6, 0xce, - 0x04, 0x6b, 0x6b, 0x34, 0xe0, 0xe4, 0x5b, 0x0e, 0x3f, 0x00, 0x40, 0x7c, 0x85, 0xe2, 0xa0, 0x93, - 0xec, 0xe4, 0x6c, 0x35, 0x3f, 0xbd, 0x28, 0x66, 0x6b, 0xb1, 0xb6, 0x51, 0x47, 0x59, 0x65, 0xd0, - 0xe8, 0x88, 0x6c, 0x43, 0x3c, 0x19, 0x50, 0x1c, 0x7f, 0xb1, 0xaf, 0xa2, 0xe4, 0x58, 0x7d, 0xef, - 0xfc, 0xd7, 0x42, 0xea, 0x7c, 0x5a, 0xd0, 0x1e, 0x4d, 0x0b, 0xda, 0xe3, 0x69, 0x41, 0xfb, 0x65, - 0x5a, 0xd0, 0xbe, 0x7b, 0x52, 0x48, 0x3d, 0x7a, 0x52, 0x48, 0x3d, 0x7e, 0x52, 0x48, 0x7d, 0x61, - 0x24, 0x35, 0x69, 0xa7, 0xe5, 0x3f, 0x1e, 0xb7, 0xff, 0x08, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x9b, - 0xc1, 0x73, 0x61, 0x0d, 0x00, 0x00, + // 1280 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0xdf, 0x6e, 0x1a, 0x47, + 0x17, 0x67, 0xcd, 0x02, 0xcb, 0x60, 0xe2, 0xfd, 0xe6, 0xf3, 0xe7, 0x6f, 0x6b, 0xb5, 0xe0, 0xae, + 0xd2, 0xd6, 0xfd, 0xb7, 0x28, 0x4e, 0xda, 0x8b, 0xde, 0x54, 0x80, 0x89, 0x8c, 0x71, 0x6c, 0x32, + 0x60, 0x57, 0x6d, 0xd5, 0xae, 0x16, 0x18, 0x60, 0x05, 0xec, 0x6c, 0x66, 0x07, 0x5a, 0xf2, 0x14, + 0x95, 0xfa, 0x02, 0xbd, 0xeb, 0x53, 0xf4, 0xde, 0x97, 0xb9, 0xcc, 0x45, 0x85, 0x5a, 0x72, 0xdd, + 0x17, 0xc8, 0x55, 0x35, 0xb3, 0xb3, 0x40, 0x9c, 0x38, 0x0d, 0x52, 0x23, 0xb5, 0x57, 0x3e, 0x33, + 0x9c, 0xf3, 0xfb, 0xcd, 0x9c, 0xf3, 0x3b, 0x67, 0xd6, 0x60, 0x67, 0x30, 0x29, 0x0c, 0x26, 0x01, + 0xa6, 0x13, 0x4c, 0x0b, 0xd4, 0xe9, 0x32, 0xcb, 0xa7, 0x84, 0x11, 0xf8, 0xbf, 0x36, 0x69, 0x0f, + 0x28, 0x71, 0xda, 0x7d, 0x6b, 0x30, 0xb1, 0x22, 0x8f, 0xdd, 0x6d, 0xb1, 0xe5, 0xb7, 0x0a, 0x98, + 0x52, 0x42, 0x83, 0xd0, 0x79, 0x77, 0x27, 0xda, 0x1d, 0x61, 0xe6, 0x74, 0x1c, 0xe6, 0xc8, 0x7d, + 0x6b, 0x15, 0x7c, 0xe8, 0x4e, 0xb0, 0x87, 0x83, 0x60, 0x61, 0xf8, 0xad, 0x85, 0x29, 0xfd, 0xcd, + 0x55, 0xff, 0xc8, 0xf0, 0x5b, 0x85, 0x80, 0x39, 0x0c, 0x4b, 0x9f, 0x1c, 0x66, 0xed, 0x8e, 0x38, + 0x69, 0x61, 0x72, 0x5b, 0xfc, 0xf5, 0x5b, 0x2b, 0x07, 0xdf, 0xdd, 0xee, 0x91, 0x1e, 0x11, 0x66, + 0x81, 0x5b, 0xe1, 0xae, 0xf9, 0x87, 0x0a, 0xb2, 0xc8, 0xe9, 0xb2, 0x23, 0xec, 0x50, 0xd6, 0xc2, + 0x0e, 0x83, 0xdf, 0x02, 0x8d, 0x3a, 0x5e, 0x0f, 0xdb, 0x6e, 0xc7, 0x50, 0xf6, 0x94, 0x7d, 0xb5, + 0x54, 0x9e, 0xcf, 0xf2, 0x29, 0xc4, 0xf7, 0xaa, 0x87, 0x4f, 0x67, 0xf9, 0x3b, 0x3d, 0x97, 0xf5, + 0xc7, 0x2d, 0xab, 0x4d, 0x46, 0x85, 0x45, 0x32, 0x3a, 0xad, 0xa5, 0x5d, 0xf0, 0x07, 0xbd, 0x82, + 0xbc, 0xb9, 0x25, 0xe3, 0x50, 0x4a, 0x80, 0x56, 0x3b, 0x30, 0x00, 0x5b, 0x5d, 0x4a, 0x46, 0x36, + 0xc5, 0xfe, 0xd0, 0x6d, 0x3b, 0x9c, 0x66, 0x63, 0x4f, 0xd9, 0xcf, 0x96, 0x6a, 0xf3, 0x59, 0x3e, + 0x7b, 0x97, 0x92, 0x11, 0x0a, 0x7f, 0x11, 0x64, 0x9f, 0xae, 0x47, 0x16, 0x45, 0xa2, 0x6c, 0x77, + 0x05, 0xa8, 0x03, 0x47, 0x20, 0xcb, 0xc8, 0x2a, 0x65, 0x5c, 0x50, 0x56, 0xe7, 0xb3, 0x7c, 0xa6, + 0x49, 0xfe, 0x0e, 0xc2, 0x0c, 0x23, 0x4b, 0x3a, 0x08, 0x54, 0x86, 0xe9, 0xc8, 0x50, 0x79, 0xfe, + 0x90, 0xb0, 0xe1, 0x0e, 0x48, 0xb6, 0xc9, 0x68, 0xe4, 0x32, 0x23, 0x21, 0x76, 0xe5, 0x0a, 0x1a, + 0x20, 0xf5, 0x60, 0xec, 0xe2, 0xa0, 0x8d, 0x8d, 0xe4, 0x9e, 0xb2, 0xaf, 0xa1, 0x68, 0x09, 0x1f, + 0x82, 0x37, 0x87, 0x4e, 0xaf, 0xe7, 0x7a, 0x3d, 0xbb, 0x4b, 0x86, 0x43, 0xf2, 0x1d, 0xa6, 0x81, + 0x4d, 0x3c, 0x3b, 0x72, 0xd7, 0xf6, 0xe2, 0xfb, 0x99, 0x83, 0xdb, 0xd6, 0x0b, 0x15, 0x69, 0x2d, + 0x24, 0xb4, 0x94, 0x95, 0x75, 0x22, 0xcd, 0x92, 0x7a, 0x39, 0xcb, 0xc7, 0xd0, 0x1b, 0x12, 0xfe, + 0x6e, 0x84, 0x7e, 0xe6, 0xdd, 0x97, 0xdc, 0x75, 0xf0, 0xce, 0xcb, 0xb8, 0x6d, 0xa7, 0xdd, 0x1e, + 0x53, 0x87, 0x61, 0x03, 0x88, 0x33, 0xbf, 0x7d, 0x2d, 0x52, 0x51, 0x3a, 0x1e, 0xab, 0x5a, 0x4a, + 0xd7, 0xcc, 0x9f, 0x93, 0x00, 0x72, 0xbd, 0xdd, 0xc3, 0x41, 0xe0, 0xf4, 0x30, 0xc2, 0x0f, 0xc6, + 0x38, 0x78, 0xfd, 0xa2, 0xfb, 0x06, 0x6c, 0x85, 0xf8, 0x01, 0x73, 0x28, 0xb3, 0x07, 0x78, 0x6a, + 0x68, 0x7b, 0xca, 0xfe, 0x66, 0xe9, 0x93, 0xa7, 0xb3, 0xfc, 0xad, 0xf5, 0xb0, 0x6b, 0x78, 0x8a, + 0xb2, 0x02, 0xad, 0xc1, 0xc1, 0x6a, 0x78, 0x0a, 0xef, 0x81, 0xcd, 0x55, 0x4d, 0x0b, 0x41, 0x67, + 0x0e, 0x6e, 0xae, 0x54, 0xe6, 0x8a, 0x60, 0x0e, 0x71, 0xd0, 0xa6, 0xae, 0xcf, 0x08, 0x95, 0xa5, + 0xc8, 0xac, 0xe8, 0x15, 0x56, 0x01, 0x58, 0xaa, 0x55, 0x48, 0x75, 0x3d, 0xb0, 0xf4, 0x42, 0x8b, + 0xb0, 0x00, 0x52, 0xa3, 0x30, 0xd5, 0x42, 0x8c, 0x99, 0x83, 0x2d, 0x2b, 0x1c, 0x0d, 0x96, 0xac, + 0x80, 0x0c, 0x89, 0xbc, 0x56, 0xe5, 0x98, 0x58, 0x4f, 0x8e, 0xe9, 0x7f, 0x93, 0x1c, 0xe1, 0x31, + 0x00, 0xfd, 0x68, 0xe6, 0x05, 0x46, 0x52, 0x9c, 0xfd, 0xe6, 0x35, 0x67, 0x7f, 0x66, 0x40, 0xca, + 0xc3, 0xae, 0x44, 0xc3, 0x06, 0xd8, 0x5a, 0xac, 0x6c, 0x8a, 0x03, 0x3f, 0x30, 0x52, 0x6b, 0x03, + 0xde, 0x58, 0x40, 0x20, 0x8e, 0x60, 0x76, 0xc1, 0xff, 0x9f, 0x6f, 0x94, 0x92, 0xc3, 0xda, 0x7d, + 0x58, 0x03, 0x1a, 0x0d, 0xd7, 0x81, 0xa1, 0x08, 0xa2, 0xf7, 0x5f, 0x42, 0x74, 0x05, 0x21, 0x64, + 0x5b, 0x00, 0x98, 0x75, 0x60, 0x3c, 0xe3, 0x15, 0xf8, 0xc4, 0x0b, 0xf0, 0xb9, 0xe7, 0x12, 0x0f, + 0x5a, 0x20, 0x21, 0xde, 0x33, 0xd1, 0x93, 0x99, 0x03, 0xe3, 0x2a, 0x8b, 0xdf, 0xb2, 0x2a, 0xfc, + 0x77, 0x14, 0xba, 0x7d, 0xa6, 0x5e, 0xfe, 0x94, 0x57, 0xcc, 0x5f, 0x37, 0xc0, 0x7f, 0x5f, 0x00, + 0xf9, 0xda, 0x9b, 0xfc, 0x9f, 0xdb, 0x85, 0x35, 0x90, 0x18, 0xf3, 0x84, 0xca, 0x1e, 0x2c, 0xbc, + 0x4a, 0xb5, 0x56, 0xea, 0x20, 0x01, 0x43, 0x0c, 0xf3, 0x51, 0x12, 0x6c, 0x35, 0x3c, 0xc7, 0x0f, + 0xfa, 0x84, 0x45, 0xf3, 0xb3, 0x02, 0x92, 0x7d, 0xec, 0x74, 0x70, 0x54, 0xa9, 0x8f, 0xaf, 0x61, + 0xb8, 0x12, 0x67, 0x1d, 0x89, 0x20, 0x24, 0x83, 0xe1, 0xbb, 0x40, 0x1b, 0x4c, 0xec, 0x16, 0x17, + 0x99, 0xc8, 0xde, 0x66, 0x29, 0xc3, 0x2b, 0x54, 0xbb, 0x10, 0xba, 0x43, 0xa9, 0xc1, 0x24, 0x14, + 0x60, 0x1e, 0x64, 0x86, 0xa4, 0x67, 0x63, 0x8f, 0x51, 0x17, 0x07, 0x46, 0x7c, 0x2f, 0xbe, 0xbf, + 0x89, 0xc0, 0x90, 0xf4, 0x2a, 0xe1, 0x0e, 0xdc, 0x06, 0x89, 0xae, 0xeb, 0x39, 0x43, 0x71, 0x61, + 0x0d, 0x85, 0x8b, 0xdd, 0x1f, 0x55, 0x90, 0x0c, 0x19, 0x61, 0x15, 0x24, 0xc4, 0xc7, 0x8b, 0x18, + 0x32, 0xd7, 0x9f, 0x37, 0x60, 0x84, 0x3a, 0x3d, 0xbc, 0xcc, 0x72, 0x83, 0x07, 0x45, 0xf9, 0x10, + 0x08, 0xd0, 0x01, 0xdb, 0x7c, 0xa4, 0xd9, 0x72, 0x82, 0xd9, 0x52, 0xd9, 0xb2, 0xfc, 0x6b, 0x77, + 0x06, 0xa4, 0xcf, 0x3f, 0x4f, 0x6f, 0x01, 0x20, 0x9f, 0x0f, 0xf7, 0x21, 0x16, 0x52, 0x88, 0xa3, + 0x74, 0xf8, 0x04, 0xb8, 0x0f, 0x31, 0xef, 0x47, 0x9f, 0xba, 0x84, 0xba, 0x6c, 0x2a, 0xde, 0xf0, + 0x1b, 0xd7, 0x56, 0xf8, 0x6a, 0xfe, 0xeb, 0x32, 0x0c, 0x2d, 0x00, 0x38, 0x58, 0xc0, 0xf8, 0x88, + 0xea, 0x4d, 0x8d, 0xd4, 0x5a, 0x60, 0x0d, 0x19, 0x86, 0x16, 0x00, 0xf0, 0x73, 0xa0, 0xb2, 0xa9, + 0xcf, 0x67, 0x33, 0x07, 0xfa, 0xf0, 0x15, 0x81, 0x9a, 0x53, 0x1f, 0x23, 0x11, 0x08, 0xcf, 0xc1, + 0x7b, 0x1d, 0xec, 0x53, 0xdc, 0x76, 0x18, 0xee, 0xd8, 0x63, 0x4f, 0xb6, 0x03, 0x5f, 0x30, 0x3a, + 0xf6, 0x42, 0x2b, 0xac, 0xa4, 0x26, 0x4a, 0x7d, 0x73, 0xe9, 0x7e, 0xbe, 0xe2, 0xdd, 0x8c, 0x9c, + 0x45, 0x01, 0x8f, 0x55, 0x4d, 0xd1, 0x37, 0x8e, 0x55, 0x4d, 0xd5, 0x13, 0xe6, 0x1d, 0xa0, 0x45, + 0x69, 0x80, 0x19, 0x90, 0x3a, 0x3f, 0xad, 0x9d, 0x9e, 0x7d, 0x71, 0xaa, 0xc7, 0xe0, 0x26, 0xd0, + 0x50, 0xa5, 0x7c, 0x76, 0x51, 0x41, 0x5f, 0xea, 0x0a, 0xcc, 0x82, 0x34, 0xaa, 0x94, 0x8a, 0x27, + 0xc5, 0xd3, 0x72, 0x45, 0xdf, 0x30, 0x0d, 0xa0, 0x45, 0xf7, 0xe5, 0x8e, 0xb5, 0x0b, 0xbb, 0x54, + 0x6c, 0x96, 0x8f, 0xf4, 0x98, 0x79, 0x0b, 0xa8, 0xfc, 0x02, 0x70, 0x07, 0xc0, 0x8b, 0x6a, 0xd1, + 0x6e, 0x9c, 0x16, 0xeb, 0x8d, 0xa3, 0xb3, 0xa6, 0x7d, 0xff, 0xbc, 0x72, 0x5e, 0xd1, 0x63, 0x9c, + 0xa3, 0x7a, 0x5a, 0x6d, 0x56, 0x8b, 0x27, 0xba, 0x62, 0xaa, 0xda, 0x86, 0xbe, 0x61, 0xfe, 0xa2, + 0x00, 0x7d, 0x99, 0x04, 0x39, 0xae, 0xee, 0x82, 0x24, 0xbf, 0xd8, 0x38, 0x10, 0x3d, 0x75, 0xe3, + 0xc0, 0xfa, 0xcb, 0xec, 0x85, 0x81, 0x56, 0x43, 0x44, 0x21, 0x19, 0xcd, 0x5f, 0xd4, 0xe8, 0x09, + 0xe6, 0x92, 0x4c, 0x2f, 0xde, 0x5a, 0xb3, 0x08, 0x92, 0xa1, 0xef, 0x73, 0xf7, 0x2e, 0x96, 0xcb, + 0x95, 0x7a, 0xb3, 0x72, 0xa8, 0x2b, 0xfc, 0xa7, 0x62, 0xbd, 0x7e, 0x52, 0xad, 0x1c, 0xea, 0x1b, + 0x30, 0x0d, 0x12, 0x15, 0x84, 0xce, 0x90, 0x1e, 0x37, 0x79, 0xf2, 0xd4, 0x63, 0x55, 0x8b, 0xeb, + 0xaa, 0xf9, 0x35, 0xf8, 0x4f, 0x99, 0x78, 0xdd, 0x72, 0x9f, 0x4b, 0xb2, 0x4c, 0x3c, 0x86, 0xbf, + 0x67, 0xf0, 0x23, 0x00, 0xf8, 0x27, 0xa6, 0xe3, 0x75, 0xa2, 0x81, 0x9b, 0x2e, 0x65, 0xe7, 0xb3, + 0x7c, 0xba, 0x1c, 0xee, 0x56, 0x0f, 0x51, 0x5a, 0x3a, 0x54, 0x3b, 0xfc, 0x94, 0xbe, 0x33, 0x1d, + 0x12, 0x27, 0xfc, 0x1c, 0xdf, 0x44, 0xd1, 0xb2, 0xf4, 0xc1, 0xe5, 0xef, 0xb9, 0xd8, 0xe5, 0x3c, + 0xa7, 0x3c, 0x9a, 0xe7, 0x94, 0xc7, 0xf3, 0x9c, 0xf2, 0xdb, 0x3c, 0xa7, 0xfc, 0xf0, 0x24, 0x17, + 0x7b, 0xf4, 0x24, 0x17, 0x7b, 0xfc, 0x24, 0x17, 0xfb, 0x4a, 0x8b, 0x72, 0xd1, 0x4a, 0x8a, 0xff, + 0x2a, 0x6e, 0xff, 0x19, 0x00, 0x00, 0xff, 0xff, 0x38, 0x9a, 0x07, 0xc0, 0x3e, 0x0d, 0x00, 0x00, } func (m *RaftHeartbeat) Marshal() (dAtA []byte, err error) { @@ -1117,16 +1108,6 @@ func (m *SnapshotRequest_Header) MarshalToSizedBuffer(dAtA []byte) (int, error) } i-- dAtA[i] = 0x2a - if m.CanDecline { - i-- - if m.CanDecline { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x20 - } if m.RangeSize != 0 { i = encodeVarintRaft(dAtA, i, uint64(m.RangeSize)) i-- @@ -1393,9 +1374,6 @@ func (m *SnapshotRequest_Header) Size() (n int) { if m.RangeSize != 0 { n += 1 + sovRaft(uint64(m.RangeSize)) } - if m.CanDecline { - n += 2 - } l = m.State.Size() n += 1 + l + sovRaft(uint64(l)) if m.Priority != 0 { @@ -2622,26 +2600,6 @@ func (m *SnapshotRequest_Header) Unmarshal(dAtA []byte) error { break } } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CanDecline", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRaft - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.CanDecline = bool(v != 0) case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) diff --git a/pkg/kv/kvserver/raft.proto b/pkg/kv/kvserver/raft.proto index de23d895581b..d74c92f26967 100644 --- a/pkg/kv/kvserver/raft.proto +++ b/pkg/kv/kvserver/raft.proto @@ -153,8 +153,6 @@ message SnapshotRequest { } message Header { - reserved 1; - // The replica state at the time the snapshot was generated. Note // that ReplicaState.Desc differs from the above range_descriptor // field which holds the updated descriptor after the new replica @@ -168,11 +166,6 @@ message SnapshotRequest { // The estimated size of the range, to be used in reservation decisions. int64 range_size = 3; - // can_decline is set on preemptive snapshots, but not those generated - // by raft because at that point it is better to queue up the stream - // than to cancel it. - bool can_decline = 4; - // The priority of the snapshot. Priority priority = 6; @@ -193,6 +186,8 @@ message SnapshotRequest { // // TODO(irfansharif): Remove this in v22.1. bool deprecated_unreplicated_truncated_state = 8; + + reserved 1, 4; } Header header = 1; @@ -214,7 +209,7 @@ message SnapshotResponse { ACCEPTED = 1; APPLIED = 2; ERROR = 3; - DECLINED = 4; + reserved 4; } Status status = 1; string message = 2; diff --git a/pkg/kv/kvserver/replica_command.go b/pkg/kv/kvserver/replica_command.go index cf7a9db9a649..ec5249353ac2 100644 --- a/pkg/kv/kvserver/replica_command.go +++ b/pkg/kv/kvserver/replica_command.go @@ -2467,14 +2467,9 @@ func (r *Replica) sendSnapshot( }, }, RangeSize: r.GetMVCCStats().Total(), - // Recipients currently cannot choose to decline any snapshots. - // In 19.2 and earlier versions pre-emptive snapshots could be declined. - // - // TODO(ajwerner): Consider removing the CanDecline flag. - CanDecline: false, - Priority: priority, - Strategy: SnapshotRequest_KV_BATCH, - Type: snapType, + Priority: priority, + Strategy: SnapshotRequest_KV_BATCH, + Type: snapType, } newBatchFn := func() storage.Batch { return r.store.Engine().NewUnindexedBatch(true /* writeOnly */) diff --git a/pkg/kv/kvserver/store_pool.go b/pkg/kv/kvserver/store_pool.go index 44c00b4e52fd..dc09ea9b35c0 100644 --- a/pkg/kv/kvserver/store_pool.go +++ b/pkg/kv/kvserver/store_pool.go @@ -41,16 +41,6 @@ const ( TestTimeUntilStoreDeadOff = 24 * time.Hour ) -// DeclinedReservationsTimeout specifies a duration during which the local -// replicate queue will not consider stores which have rejected a reservation a -// viable target. -var DeclinedReservationsTimeout = settings.RegisterDurationSetting( - "server.declined_reservation_timeout", - "the amount of time to consider the store throttled for up-replication after a reservation was declined", - 1*time.Second, - settings.NonNegativeDuration, -) - // FailedReservationsTimeout specifies a duration during which the local // replicate queue will not consider stores which have failed a reservation a // viable target. @@ -914,7 +904,6 @@ type throttleReason int const ( _ throttleReason = iota - throttleDeclined throttleFailed ) @@ -929,19 +918,10 @@ func (sp *StorePool) throttle(reason throttleReason, why string, storeID roachpb detail := sp.getStoreDetailLocked(storeID) detail.throttledBecause = why - // If a snapshot is declined, be it due to an error or because it was - // rejected, we mark the store detail as having been declined so it won't - // be considered as a candidate for new replicas until after the configured - // timeout period has passed. + // If a snapshot is declined, we mark the store detail as having been declined + // so it won't be considered as a candidate for new replicas until after the + // configured timeout period has passed. switch reason { - case throttleDeclined: - timeout := DeclinedReservationsTimeout.Get(&sp.st.SV) - detail.throttledUntil = sp.clock.PhysicalTime().Add(timeout) - if log.V(2) { - ctx := sp.AnnotateCtx(context.TODO()) - log.Infof(ctx, "snapshot declined (%s), s%d will be throttled for %s until %s", - why, storeID, timeout, detail.throttledUntil) - } case throttleFailed: timeout := FailedReservationsTimeout.Get(&sp.st.SV) detail.throttledUntil = sp.clock.PhysicalTime().Add(timeout) @@ -950,6 +930,8 @@ func (sp *StorePool) throttle(reason throttleReason, why string, storeID roachpb log.Infof(ctx, "snapshot failed (%s), s%d will be throttled for %s until %s", why, storeID, timeout, detail.throttledUntil) } + default: + log.Warningf(sp.AnnotateCtx(context.TODO()), "unknown throttle reason %v", reason) } } diff --git a/pkg/kv/kvserver/store_pool_test.go b/pkg/kv/kvserver/store_pool_test.go index 57d6ce950588..5362984b52b4 100644 --- a/pkg/kv/kvserver/store_pool_test.go +++ b/pkg/kv/kvserver/store_pool_test.go @@ -835,30 +835,15 @@ func TestStorePoolThrottle(t *testing.T) { sg := gossiputil.NewStoreGossiper(g) sg.GossipStores(uniqueStore, t) - { - expected := sp.clock.Now().GoTime().Add(DeclinedReservationsTimeout.Get(&sp.st.SV)) - sp.throttle(throttleDeclined, "", 1) - - sp.detailsMu.Lock() - detail := sp.getStoreDetailLocked(1) - sp.detailsMu.Unlock() - if !detail.throttledUntil.Equal(expected) { - t.Errorf("expected store to have been throttled to %v, found %v", - expected, detail.throttledUntil) - } - } + expected := sp.clock.Now().GoTime().Add(FailedReservationsTimeout.Get(&sp.st.SV)) + sp.throttle(throttleFailed, "", 1) - { - expected := sp.clock.Now().GoTime().Add(FailedReservationsTimeout.Get(&sp.st.SV)) - sp.throttle(throttleFailed, "", 1) - - sp.detailsMu.Lock() - detail := sp.getStoreDetailLocked(1) - sp.detailsMu.Unlock() - if !detail.throttledUntil.Equal(expected) { - t.Errorf("expected store to have been throttled to %v, found %v", - expected, detail.throttledUntil) - } + sp.detailsMu.Lock() + detail := sp.getStoreDetailLocked(1) + sp.detailsMu.Unlock() + if !detail.throttledUntil.Equal(expected) { + t.Errorf("expected store to have been throttled to %v, found %v", + expected, detail.throttledUntil) } } diff --git a/pkg/kv/kvserver/store_raft.go b/pkg/kv/kvserver/store_raft.go index 4d67bab3b0aa..4e3b0fdddc30 100644 --- a/pkg/kv/kvserver/store_raft.go +++ b/pkg/kv/kvserver/store_raft.go @@ -74,7 +74,7 @@ func (s *Store) HandleSnapshot( if s.IsDraining() { return stream.Send(&SnapshotResponse{ - Status: SnapshotResponse_DECLINED, + Status: SnapshotResponse_ERROR, Message: storeDrainingMsg, }) } diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 68af0e53c8ef..c038479709f3 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -40,9 +40,7 @@ import ( const ( // Messages that provide detail about why a snapshot was rejected. - snapshotStoreTooFullMsg = "store almost out of disk space" - snapshotApplySemBusyMsg = "store busy applying snapshots" - storeDrainingMsg = "store is draining" + storeDrainingMsg = "store is draining" // IntersectingSnapshotMsg is part of the error message returned from // canAcceptSnapshotLocked and is exposed here so testing can rely on it. @@ -500,38 +498,23 @@ func (kvSS *kvBatchSnapshotStrategy) Close(ctx context.Context) { } // reserveSnapshot throttles incoming snapshots. The returned closure is used -// to cleanup the reservation and release its resources. A nil cleanup function -// and a non-empty rejectionMessage indicates the reservation was declined. +// to cleanup the reservation and release its resources. func (s *Store) reserveSnapshot( ctx context.Context, header *SnapshotRequest_Header, -) (_cleanup func(), _rejectionMsg string, _err error) { +) (_cleanup func(), _err error) { tBegin := timeutil.Now() - if header.RangeSize == 0 { - // Empty snapshots are exempt from rate limits because they're so cheap to - // apply. This vastly speeds up rebalancing any empty ranges created by a - // RESTORE or manual SPLIT AT, since it prevents these empty snapshots from - // getting stuck behind large snapshots managed by the replicate queue. - } else if header.CanDecline { - storeDesc, ok := s.cfg.StorePool.getStoreDescriptor(s.StoreID()) - if ok && (!maxCapacityCheck(storeDesc) || header.RangeSize > storeDesc.Capacity.Available) { - return nil, snapshotStoreTooFullMsg, nil - } - select { - case s.snapshotApplySem <- struct{}{}: - case <-ctx.Done(): - return nil, "", ctx.Err() - case <-s.stopper.ShouldQuiesce(): - return nil, "", errors.Errorf("stopped") - default: - return nil, snapshotApplySemBusyMsg, nil - } - } else { + + // Empty snapshots are exempt from rate limits because they're so cheap to + // apply. This vastly speeds up rebalancing any empty ranges created by a + // RESTORE or manual SPLIT AT, since it prevents these empty snapshots from + // getting stuck behind large snapshots managed by the replicate queue. + if header.RangeSize != 0 { select { case s.snapshotApplySem <- struct{}{}: case <-ctx.Done(): - return nil, "", ctx.Err() + return nil, ctx.Err() case <-s.stopper.ShouldQuiesce(): - return nil, "", errors.Errorf("stopped") + return nil, errors.Errorf("stopped") } } @@ -560,7 +543,7 @@ func (s *Store) reserveSnapshot( if header.RangeSize != 0 { <-s.snapshotApplySem } - }, "", nil + }, nil } // canAcceptSnapshotLocked returns (_, nil) if the snapshot can be applied to @@ -716,16 +699,10 @@ func (s *Store) receiveSnapshot( header.Type, storeID, header.State.Desc.Replicas()) } - cleanup, rejectionMsg, err := s.reserveSnapshot(ctx, header) + cleanup, err := s.reserveSnapshot(ctx, header) if err != nil { return err } - if cleanup == nil { - return stream.Send(&SnapshotResponse{ - Status: SnapshotResponse_DECLINED, - Message: rejectionMsg, - }) - } defer cleanup() // The comment on ReplicaPlaceholder motivates and documents @@ -995,7 +972,6 @@ func SendEmptySnapshot( State: state, RaftMessageRequest: req, RangeSize: ms.Total(), - CanDecline: false, Priority: SnapshotRequest_RECOVERY, Strategy: SnapshotRequest_KV_BATCH, Type: SnapshotRequest_VIA_SNAPSHOT_QUEUE, @@ -1056,20 +1032,6 @@ func sendSnapshot( return err } switch resp.Status { - case SnapshotResponse_DECLINED: - if header.CanDecline { - declinedMsg := "reservation rejected" - if len(resp.Message) > 0 { - declinedMsg = resp.Message - } - err := &benignError{errors.Errorf("%s: remote declined %s: %s", to, snap, declinedMsg)} - storePool.throttle(throttleDeclined, err.Error(), to.StoreID) - return err - } - err := errors.Errorf("%s: programming error: remote declined required %s: %s", - to, snap, resp.Message) - storePool.throttle(throttleFailed, err.Error(), to.StoreID) - return err case SnapshotResponse_ERROR: storePool.throttle(throttleFailed, resp.Message, to.StoreID) return errors.Errorf("%s: remote couldn't accept %s with error: %s", diff --git a/pkg/kv/kvserver/store_test.go b/pkg/kv/kvserver/store_test.go index 6e763a990899..65aa6cbf7062 100644 --- a/pkg/kv/kvserver/store_test.go +++ b/pkg/kv/kvserver/store_test.go @@ -2856,16 +2856,15 @@ func (c fakeSnapshotStream) Send(request *SnapshotRequest) error { } type fakeStorePool struct { - declinedThrottles int - failedThrottles int + failedThrottles int } func (sp *fakeStorePool) throttle(reason throttleReason, why string, toStoreID roachpb.StoreID) { switch reason { - case throttleDeclined: - sp.declinedThrottles++ case throttleFailed: sp.failedThrottles++ + default: + panic("unknown reason") } } @@ -2882,7 +2881,6 @@ func TestSendSnapshotThrottling(t *testing.T) { st := cluster.MakeTestingClusterSettings() header := SnapshotRequest_Header{ - CanDecline: true, State: kvserverpb.ReplicaState{ Desc: &roachpb.RangeDescriptor{RangeID: 1}, }, @@ -2903,39 +2901,6 @@ func TestSendSnapshotThrottling(t *testing.T) { } } - // Test that a declined snapshot causes a decline throttle. - { - sp := &fakeStorePool{} - resp := &SnapshotResponse{ - Status: SnapshotResponse_DECLINED, - } - c := fakeSnapshotStream{resp, nil} - err := sendSnapshot(ctx, st, c, sp, header, nil, newBatch, nil) - if sp.declinedThrottles != 1 { - t.Fatalf("expected 1 declined throttle, but found %d", sp.declinedThrottles) - } - if err == nil { - t.Fatalf("expected error, found nil") - } - } - - // Test that a declined but required snapshot causes a fail throttle. - { - sp := &fakeStorePool{} - header.CanDecline = false - resp := &SnapshotResponse{ - Status: SnapshotResponse_DECLINED, - } - c := fakeSnapshotStream{resp, nil} - err := sendSnapshot(ctx, st, c, sp, header, nil, newBatch, nil) - if sp.failedThrottles != 1 { - t.Fatalf("expected 1 failed throttle, but found %d", sp.failedThrottles) - } - if err == nil { - t.Fatalf("expected error, found nil") - } - } - // Test that an errored snapshot causes a fail throttle. { sp := &fakeStorePool{} @@ -2965,27 +2930,21 @@ func TestReserveSnapshotThrottling(t *testing.T) { ctx := context.Background() - cleanupNonEmpty1, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ + cleanupNonEmpty1, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ RangeSize: 1, }) if err != nil { t.Fatal(err) } - if rejectionMsg != "" { - t.Fatalf("expected no rejection message, got %q", rejectionMsg) - } if n := s.ReservationCount(); n != 1 { t.Fatalf("expected 1 reservation, but found %d", n) } // Ensure we allow a concurrent empty snapshot. - cleanupEmpty, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{}) + cleanupEmpty, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{}) if err != nil { t.Fatal(err) } - if rejectionMsg != "" { - t.Fatalf("expected no rejection message, got %q", rejectionMsg) - } // Empty snapshots are not throttled and so do not increase the reservation // count. if n := s.ReservationCount(); n != 1 { @@ -2993,21 +2952,6 @@ func TestReserveSnapshotThrottling(t *testing.T) { } cleanupEmpty() - // Verify that a declinable snapshot will be declined if another is in - // progress. - cleanupNonEmpty2, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ - RangeSize: 1, - CanDecline: true, - }) - if err != nil { - t.Fatal(err) - } - if rejectionMsg != snapshotApplySemBusyMsg { - t.Fatalf("expected rejection message %q, got %q", snapshotApplySemBusyMsg, rejectionMsg) - } - if cleanupNonEmpty2 != nil { - t.Fatalf("got unexpected non-nil cleanup method") - } if n := s.ReservationCount(); n != 1 { t.Fatalf("expected 1 reservation, but found %d", n) } @@ -3023,15 +2967,12 @@ func TestReserveSnapshotThrottling(t *testing.T) { } }() - cleanupNonEmpty3, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ + cleanupNonEmpty3, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ RangeSize: 1, }) if err != nil { t.Fatal(err) } - if rejectionMsg != "" { - t.Fatalf("expected no rejection message, got %q", rejectionMsg) - } atomic.StoreInt32(&boom, 1) cleanupNonEmpty3() @@ -3040,8 +2981,8 @@ func TestReserveSnapshotThrottling(t *testing.T) { } } -// TestReserveSnapshotFullnessLimit verifies that snapshots are rejected when -// the recipient store's disk is near full. +// TestReserveSnapshotFullnessLimit documents that we have no mechanism to +// decline snapshots based on the remaining capacity of the target store. func TestReserveSnapshotFullnessLimit(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -3065,35 +3006,17 @@ func TestReserveSnapshotFullnessLimit(t *testing.T) { s.cfg.StorePool.getStoreDetailLocked(desc.StoreID).desc = desc s.cfg.StorePool.detailsMu.Unlock() - // A declinable snapshot to a nearly full store should be rejected. - cleanupRejected, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ - RangeSize: 1, - CanDecline: true, - }) - if err != nil { - t.Fatal(err) - } - if rejectionMsg != snapshotStoreTooFullMsg { - t.Fatalf("expected rejection message %q, got %q", snapshotStoreTooFullMsg, rejectionMsg) - } - if cleanupRejected != nil { - t.Fatalf("got unexpected non-nil cleanup method") - } if n := s.ReservationCount(); n != 0 { t.Fatalf("expected 0 reservations, but found %d", n) } - // But a non-declinable snapshot should be allowed. - cleanupAccepted, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ - RangeSize: 1, - CanDecline: false, + // A snapshot should be allowed. + cleanupAccepted, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ + RangeSize: 1, }) if err != nil { t.Fatal(err) } - if rejectionMsg != "" { - t.Fatalf("expected no rejection message, got %q", rejectionMsg) - } if n := s.ReservationCount(); n != 1 { t.Fatalf("expected 1 reservation, but found %d", n) } @@ -3107,20 +3030,6 @@ func TestReserveSnapshotFullnessLimit(t *testing.T) { s.cfg.StorePool.getStoreDetailLocked(desc.StoreID).desc = desc s.cfg.StorePool.detailsMu.Unlock() - // A declinable snapshot to a nearly full store should be rejected. - cleanupRejected2, rejectionMsg, err := s.reserveSnapshot(ctx, &SnapshotRequest_Header{ - RangeSize: desc.Capacity.Available + 1, - CanDecline: true, - }) - if err != nil { - t.Fatal(err) - } - if rejectionMsg != snapshotStoreTooFullMsg { - t.Fatalf("expected rejection message %q, got %q", snapshotStoreTooFullMsg, rejectionMsg) - } - if cleanupRejected2 != nil { - t.Fatalf("got unexpected non-nil cleanup method") - } if n := s.ReservationCount(); n != 0 { t.Fatalf("expected 0 reservations, but found %d", n) } diff --git a/pkg/settings/registry.go b/pkg/settings/registry.go index af0b864af94e..71fd0076f69d 100644 --- a/pkg/settings/registry.go +++ b/pkg/settings/registry.go @@ -110,6 +110,7 @@ var retiredSettings = map[string]struct{}{ "trace.datadog.agent": {}, "trace.datadog.project": {}, "sql.defaults.interleaved_tables.enabled": {}, + "server.declined_reservation_timeout": {}, } // register adds a setting to the registry. From ba71c0e78c1666a88d82dfe058360bf65541391a Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 17:00:20 +0200 Subject: [PATCH 130/205] kvserver: reflect absence of preemptive snaps in more comments Release note: None --- pkg/kv/kvserver/allocator_test.go | 4 ++- pkg/kv/kvserver/client_raft_test.go | 34 ++++++++----------- pkg/kv/kvserver/raft_log_queue.go | 27 +++++++-------- pkg/kv/kvserver/replica.go | 9 +---- .../replica_application_state_machine.go | 8 +---- pkg/kv/kvserver/replica_raft.go | 8 +++-- pkg/kv/kvserver/store_raft.go | 10 +++--- pkg/kv/kvserver/store_snapshot.go | 8 ++--- 8 files changed, 47 insertions(+), 61 deletions(-) diff --git a/pkg/kv/kvserver/allocator_test.go b/pkg/kv/kvserver/allocator_test.go index bf05f79a7adc..03c83f6e96c5 100644 --- a/pkg/kv/kvserver/allocator_test.go +++ b/pkg/kv/kvserver/allocator_test.go @@ -7219,7 +7219,9 @@ func (ts *testStore) rebalance(ots *testStore, bytes int64) { if ts.Capacity.RangeCount == 0 || (ts.Capacity.Capacity-ts.Capacity.Available) < bytes { return } - // Mimic a real Store's behavior of rejecting preemptive snapshots when full. + // Mimic a real Store's behavior of not considering target stores that are + // almost out of disk. (In a real allocator this is, for example, in + // rankedCandidateListFor{Allocation,Rebalancing}). if !maxCapacityCheck(ots.StoreDescriptor) { log.Infof(context.Background(), "s%d too full to accept snapshot from s%d: %v", ots.StoreID, ts.StoreID, ots.Capacity) diff --git a/pkg/kv/kvserver/client_raft_test.go b/pkg/kv/kvserver/client_raft_test.go index a6b5a9be9057..ba4b419ee64b 100644 --- a/pkg/kv/kvserver/client_raft_test.go +++ b/pkg/kv/kvserver/client_raft_test.go @@ -643,9 +643,8 @@ func TestRaftLogSizeAfterTruncation(t *testing.T) { assert.NoError(t, assertCorrectRaftLogSize()) } -// TestSnapshotAfterTruncation tests that Raft will properly send a -// non-preemptive snapshot when a node is brought up and the log has been -// truncated. +// TestSnapshotAfterTruncation tests that Raft will properly send a snapshot +// when a node is brought up and the log has been truncated. func TestSnapshotAfterTruncation(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -708,8 +707,8 @@ func TestSnapshotAfterTruncation(t *testing.T) { tc.WaitForValues(t, key, []int64{incA, incA, incA}) // Now kill one store, increment the key on the other stores and truncate - // their logs to make sure that when store 1 comes back up it will require a - // non-preemptive snapshot from Raft. + // their logs to make sure that when store 1 comes back up it will require + // a snapshot from Raft. tc.StopServer(stoppedStore) incArgs = incrementArgs(key, incB) @@ -1350,8 +1349,8 @@ func TestFailedSnapshotFillsReservation(t *testing.T) { } // TestConcurrentRaftSnapshots tests that snapshots still work correctly when -// Raft requests multiple non-preemptive snapshots at the same time. This -// situation occurs when two replicas need snapshots at the same time. +// Raft requests multiple snapshots at the same time. This situation occurs when +// two replicas need snapshots at the same time. func TestConcurrentRaftSnapshots(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1409,7 +1408,7 @@ func TestConcurrentRaftSnapshots(t *testing.T) { // Now kill stores 1 + 2, increment the key on the other stores and // truncate their logs to make sure that when store 1 + 2 comes back up - // they will require a non-preemptive snapshot from Raft. + // they will require a snapshot from Raft. tc.StopServer(1) tc.StopServer(2) @@ -2800,13 +2799,11 @@ func TestRaftRemoveRace(t *testing.T) { numServers := 10 if util.RaceEnabled { - // In race builds, running 10 nodes needs more than 1 full CPU - // (due to background gossip and heartbeat overhead), so it can't - // keep up when run under stress with one process per CPU. Run a - // reduced version of this test in race builds. This isn't as - // likely to reproduce the preemptive-snapshot race described in - // the previous comment, but will still have a chance to do so, or - // to find other races. + // In race builds, running 10 nodes needs more than 1 full CPU (due to + // background gossip and heartbeat overhead), so it can't keep up when run + // under stress with one process per CPU. Run a reduced version of this test + // in race builds. This isn't as likely to reproduce the races but will + // still have a chance to do so. numServers = 3 } @@ -2819,10 +2816,9 @@ func TestRaftRemoveRace(t *testing.T) { key := tc.ScratchRange(t) desc := tc.LookupRangeOrFatal(t, key) - // Up-replicate to a bunch of nodes which stresses a condition where a - // replica created via a preemptive snapshot receives a message for a - // previous incarnation of the replica (i.e. has a smaller replica ID) that - // existed on the same store. + // Cyclically up-replicate to a bunch of nodes which stresses a condition + // where replicas receive messages for a previous or later incarnation of the + // replica. targets := make([]roachpb.ReplicationTarget, len(tc.Servers)-1) for i := 1; i < len(tc.Servers); i++ { targets[i-1] = tc.Target(i) diff --git a/pkg/kv/kvserver/raft_log_queue.go b/pkg/kv/kvserver/raft_log_queue.go index c7b3c4094235..9d096ac50c88 100644 --- a/pkg/kv/kvserver/raft_log_queue.go +++ b/pkg/kv/kvserver/raft_log_queue.go @@ -103,22 +103,20 @@ func newRaftLogQueue(store *Store, db *kv.DB) *raftLogQueue { // there is at least a little bit of log to truncate (think a hundred records or // ~100kb of data). If followers fall behind, are offline, or are waiting for a // snapshot, a second strategy is needed to make sure that the Raft log is -// eventually truncated: when the raft log size exceeds a limit (4mb at time of -// writing), truncations become willing and able to cut off followers as long as -// a quorum has acked the truncation index. The quota pool ensures that the delta -// between "acked by quorum" and "acked by all" is bounded, while Raft limits the -// size of the uncommitted, i.e. not "acked by quorum", part of the log; thus -// the "quorum" truncation strategy bounds the absolute size of the log on all -// followers. +// eventually truncated: when the raft log size exceeds a limit, truncations +// become willing and able to cut off followers as long as a quorum has acked +// the truncation index. The quota pool ensures that the delta between "acked by +// quorum" and "acked by all" is bounded, while Raft limits the size of the +// uncommitted, i.e. not "acked by quorum", part of the log; thus the "quorum" +// truncation strategy bounds the absolute size of the log on all followers. // // Exceptions are made for replicas for which information is missing ("probing // state") as long as they are known to have been online recently, and for -// in-flight snapshots (in particular preemptive snapshots) which are not -// adequately reflected in the Raft status and would otherwise be cut off with -// regularity. Probing live followers should only remain in this state for a -// short moment and so we deny a log truncation outright (as there's no safe -// index to truncate to); for snapshots, we can still truncate, but not past -// the snapshot's index. +// in-flight snapshots which are not adequately reflected in the Raft status and +// would otherwise be cut off with regularity. Probing live followers should +// only remain in this state for a short moment and so we deny a log truncation +// outright (as there's no safe index to truncate to); for snapshots, we can +// still truncate, but not past the snapshot's index. // // A challenge for log truncation is to deal with sideloaded log entries, that // is, entries which contain SSTables for direct ingestion into the storage @@ -247,8 +245,7 @@ func updateRaftProgressFromActivity( } pr.RecentActive = replicaActive(replicaID) // Override this field for safety since we don't use it. Instead, we use - // pendingSnapshotIndex from above which is also populated for preemptive - // snapshots. + // pendingSnapshotIndex from above. // // NOTE: We don't rely on PendingSnapshot because PendingSnapshot is // initialized by the leader when it realizes the follower needs a snapshot, diff --git a/pkg/kv/kvserver/replica.go b/pkg/kv/kvserver/replica.go index 798f5a9f59b0..54686b17966c 100644 --- a/pkg/kv/kvserver/replica.go +++ b/pkg/kv/kvserver/replica.go @@ -1507,14 +1507,7 @@ func (r *Replica) shouldWaitForPendingMergeRLocked( // older replica ID including its hard state which may have been synthesized // with votes as the newer replica ID. This case tends to be handled safely // in practice because the replica should only be receiving messages as the -// newer replica ID after it has been added to the range. Prior to learner -// replicas we would only add a store to a range after we've successfully -// applied a pre-emptive snapshot. If the store were to split between the -// preemptive snapshot and the addition then the addition would fail due to -// the conditional put logic. If the store were to then enable learners then -// we're still okay because we won't promote a learner unless we succeed in -// sending a learner snapshot. If we fail to send the replica never becomes -// a voter then its votes don't matter and are safe to discard. +// newer replica ID after it has been added to the range as a learner. // // Despite the safety due to the change replicas protocol explained above // it'd be good to know for sure that a replica ID for a range on a store diff --git a/pkg/kv/kvserver/replica_application_state_machine.go b/pkg/kv/kvserver/replica_application_state_machine.go index 65c4505d68d2..e0259aed8f13 100644 --- a/pkg/kv/kvserver/replica_application_state_machine.go +++ b/pkg/kv/kvserver/replica_application_state_machine.go @@ -653,13 +653,7 @@ func (b *replicaAppBatch) runPreApplyTriggersAfterStagingWriteBatch( // Merges require the subsumed range to be atomically deleted when the // merge transaction commits. - // If our range currently has a non-zero replica ID then we know we're - // safe to commit this merge because of the invariants provided to us - // by the merge protocol. Namely if this committed we know that if the - // command committed then all of the replicas in the range descriptor - // are collocated when this command commits. If we do not have a non-zero - // replica ID then the logic in Stage should detect that and destroy our - // preemptive snapshot so we shouldn't ever get here. + // An initialized replica is always contained in its descriptor. rhsRepl, err := b.r.store.GetReplica(merge.RightDesc.RangeID) if err != nil { return wrapWithNonDeterministicFailure(err, "unable to get replica for merge") diff --git a/pkg/kv/kvserver/replica_raft.go b/pkg/kv/kvserver/replica_raft.go index 8ced609a4edd..771f6315c3cc 100644 --- a/pkg/kv/kvserver/replica_raft.go +++ b/pkg/kv/kvserver/replica_raft.go @@ -1615,8 +1615,12 @@ func shouldCampaignOnWake( // being quiescent) and campaigns for raft leadership if appropriate. func (r *Replica) maybeCampaignOnWakeLocked(ctx context.Context) { // Raft panics if a node that is not currently a member of the - // group tries to campaign. That happens primarily when we apply - // preemptive snapshots. + // group tries to campaign. This method should never be called + // otherwise and in fact the Replica should never be in such a + // state but better not take any chances. For example, if this + // method were to be called on an uninitialized replica (which + // has no state and thus an empty raft config), this might cause + // problems. if _, currentMember := r.mu.state.Desc.GetReplicaDescriptorByID(r.mu.replicaID); !currentMember { return } diff --git a/pkg/kv/kvserver/store_raft.go b/pkg/kv/kvserver/store_raft.go index 4e3b0fdddc30..aa33add6a552 100644 --- a/pkg/kv/kvserver/store_raft.go +++ b/pkg/kv/kvserver/store_raft.go @@ -264,11 +264,11 @@ func (s *Store) processRaftRequestWithReplica( return nil } -// processRaftSnapshotRequest processes the incoming non-preemptive snapshot -// Raft request on the request's specified replica. The function makes sure to -// handle any updated Raft Ready state. It also adds and later removes the -// (potentially) necessary placeholder to protect against concurrent access to -// the keyspace encompassed by the snapshot but not yet guarded by the replica. +// processRaftSnapshotRequest processes the incoming snapshot Raft request on +// the request's specified replica. The function makes sure to handle any +// updated Raft Ready state. It also adds and later removes the (potentially) +// necessary placeholder to protect against concurrent access to the keyspace +// encompassed by the snapshot but not yet guarded by the replica. // // If (and only if) no error is returned, the placeholder (if any) in inSnap // will have been removed. diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index c038479709f3..03e143e48c7a 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -592,8 +592,7 @@ func (s *Store) canAcceptSnapshotLocked( // // NB: The snapshot must be intended for this replica as // withReplicaForRequest ensures that requests with a non-zero replica - // id are passed to a replica with a matching id. Given this is not a - // preemptive snapshot we know that its id must be non-zero. + // id are passed to a replica with a matching id. return nil, nil } @@ -802,8 +801,9 @@ func validatePositive(v int64) error { return nil } -// rebalanceSnapshotRate is the rate at which preemptive snapshots can be sent. -// This includes snapshots generated for upreplication or for rebalancing. +// rebalanceSnapshotRate is the rate at which snapshots can be sent in the +// context of up-replication or rebalancing (i.e. any snapshot that was not +// requested by raft itself, to which `kv.snapshot_recovery.max_rate` applies). var rebalanceSnapshotRate = settings.RegisterByteSizeSetting( "kv.snapshot_rebalance.max_rate", "the rate limit (bytes/sec) to use for rebalance and upreplication snapshots", From 55ef8283a6ced432878f22788ec826dad84a99ad Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 17:03:00 +0200 Subject: [PATCH 131/205] kvserver: remove IsPreemptive() Release note: None --- pkg/kv/kvserver/raft.go | 8 -------- pkg/kv/kvserver/store_raft.go | 4 ---- pkg/kv/kvserver/store_snapshot.go | 8 -------- 3 files changed, 20 deletions(-) diff --git a/pkg/kv/kvserver/raft.go b/pkg/kv/kvserver/raft.go index 027cab267414..8705d3048520 100644 --- a/pkg/kv/kvserver/raft.go +++ b/pkg/kv/kvserver/raft.go @@ -235,14 +235,6 @@ func (m *RaftMessageRequest) release() { raftMessageRequestPool.Put(m) } -// IsPreemptive returns whether this is a preemptive snapshot or a Raft -// snapshot. -func (h *SnapshotRequest_Header) IsPreemptive() bool { - // Preemptive snapshots are addressed to replica ID 0. No other requests to - // replica ID 0 are allowed. - return h.RaftMessageRequest.ToReplica.ReplicaID == 0 -} - // traceEntries records the provided event for all proposals corresponding // to the entries contained in ents. The vmodule level for raft must be at // least 1. diff --git a/pkg/kv/kvserver/store_raft.go b/pkg/kv/kvserver/store_raft.go index aa33add6a552..c1c06cc8f491 100644 --- a/pkg/kv/kvserver/store_raft.go +++ b/pkg/kv/kvserver/store_raft.go @@ -275,10 +275,6 @@ func (s *Store) processRaftRequestWithReplica( func (s *Store) processRaftSnapshotRequest( ctx context.Context, snapHeader *SnapshotRequest_Header, inSnap IncomingSnapshot, ) *roachpb.Error { - if snapHeader.IsPreemptive() { - return roachpb.NewError(errors.AssertionFailedf(`expected a raft or learner snapshot`)) - } - return s.withReplicaForRequest(ctx, &snapHeader.RaftMessageRequest, func( ctx context.Context, r *Replica, ) (pErr *roachpb.Error) { diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 03e143e48c7a..779e6cf01642 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -556,10 +556,6 @@ func (s *Store) reserveSnapshot( func (s *Store) canAcceptSnapshotLocked( ctx context.Context, snapHeader *SnapshotRequest_Header, ) (*ReplicaPlaceholder, error) { - if snapHeader.IsPreemptive() { - return nil, errors.AssertionFailedf(`expected a raft or learner snapshot`) - } - // TODO(tbg): see the comment on desc.Generation for what seems to be a much // saner way to handle overlap via generational semantics. desc := *snapHeader.State.Desc @@ -686,10 +682,6 @@ func (s *Store) receiveSnapshot( } } - if header.IsPreemptive() { - return errors.AssertionFailedf(`expected a raft or learner snapshot`) - } - // Defensive check that any snapshot contains this store in the descriptor. storeID := s.StoreID() if _, ok := header.State.Desc.GetReplicaDescriptor(storeID); !ok { From 57c1906f716317aaf9daa584b3c7bbb096dc8628 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 17:06:15 +0200 Subject: [PATCH 132/205] kvserver: bump snapshotReservationWaitWarnThreshold This was set before both the default range size and snapshot rate limit changed. Re-do the math and adjust the constant. Release note: None --- pkg/kv/kvserver/store_snapshot.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 779e6cf01642..80a222a79f51 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -518,12 +518,10 @@ func (s *Store) reserveSnapshot( } } - // The choice here is essentially arbitrary, but with a default range size of 64mb and the - // Raft snapshot rate limiting of 8mb/s, we expect to spend less than 8s per snapshot. - // Preemptive snapshots are limited to 2mb/s (by default), so they can take up to 4x longer, - // but an average range is closer to 32mb, so we expect ~16s for larger preemptive snapshots, + // The choice here is essentially arbitrary, but with a default range size of 128mb-512mb and the + // Raft snapshot rate limiting of 32mb/s, we expect to spend less than 16s per snapshot. // which is what we want to log. - const snapshotReservationWaitWarnThreshold = 13 * time.Second + const snapshotReservationWaitWarnThreshold = 32 * time.Second if elapsed := timeutil.Since(tBegin); elapsed > snapshotReservationWaitWarnThreshold { replDesc, _ := header.State.Desc.GetReplicaDescriptor(s.StoreID()) log.Infof( From 916f88d427130b7b476d619f8360bcb63582efe7 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 17:09:05 +0200 Subject: [PATCH 133/205] kvserver: add TODO about removing snapshot log sending code. I'll get to this, but the mission at hand is removing residual mentions of preemptive snapshots. Release note: None --- pkg/kv/kvserver/replica_sideload.go | 10 +++++++--- pkg/kv/kvserver/store_snapshot.go | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/kv/kvserver/replica_sideload.go b/pkg/kv/kvserver/replica_sideload.go index 0c7967dbd958..b3b6ac136c71 100644 --- a/pkg/kv/kvserver/replica_sideload.go +++ b/pkg/kv/kvserver/replica_sideload.go @@ -178,9 +178,13 @@ func maybeInlineSideloadedRaftCommand( } if len(command.ReplicatedEvalResult.AddSSTable.Data) > 0 { - // The entry we started out with was already "fat". This happens when - // the entry reached us through a preemptive snapshot (when we didn't - // have a ReplicaID yet). + // The entry we started out with was already "fat". This should never + // occur since it would imply that a) the entry was not properly + // sideloaded during append or b) the entry reached us through a + // snapshot, but as of #70464, snapshots are guaranteed to not + // contain any log entries. (So if we hit this, it is going to + // be as a result of log entries that are very old, written + // when sending the log with snapshots was still possible). log.Event(ctx, "entry already inlined") return &ent, nil } diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 80a222a79f51..5fe889549dd8 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -396,6 +396,11 @@ func (kvSS *kvBatchSnapshotStrategy) Send( // SSTables directly to the snapshot. Probably the better long-term // solution, but let's see if it ever becomes relevant. Snapshots with // inlined proposals are hopefully the exception. + // + // TODO(tbg): this code is obsolete because as of the PR linked below, + // our snapshots will never contain log entries. Trim down this code. + // + // https://github.com/cockroachdb/cockroach/pull/70464 { for i, ent := range logEntries { if !sniffSideloadedRaftCommand(ent.Data) { From 741c97c71a0ee1be9d91ca3633b325c15217878f Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 17:15:34 +0200 Subject: [PATCH 134/205] kvserver: assert that local descriptor "always" contains the replica This is an invariant that was established in [#40892]. We now check it more aggressively. There is an awkward exception thanks to uninitialized replicas. [#40892]: https://github.com/cockroachdb/cockroach/pull/40892 Release note: None fixupassert --- pkg/kv/kvserver/replica.go | 35 +++++++++++++++++++ .../replica_application_state_machine.go | 7 ---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/pkg/kv/kvserver/replica.go b/pkg/kv/kvserver/replica.go index 54686b17966c..5561ae9a4c22 100644 --- a/pkg/kv/kvserver/replica.go +++ b/pkg/kv/kvserver/replica.go @@ -420,6 +420,9 @@ type Replica struct { // The replica's Raft group "node". internalRaftGroup *raft.RawNode // The ID of the replica within the Raft group. This value may never be 0. + // It will not change over the lifetime of this replica. If addressed under + // a newer replicaID, the replica immediately replicaGCs itself to make + // way for the newer incarnation. replicaID roachpb.ReplicaID // The minimum allowed ID for this replica. Initialized from // RangeTombstone.NextReplicaID. @@ -1217,6 +1220,38 @@ func (r *Replica) assertStateRaftMuLockedReplicaMuRLocked( log.Fatalf(ctx, "denormalized start key %s diverged from %s", r.startKey, r.mu.state.Desc.StartKey) } } + // A replica is always contained in its descriptor. This is an invariant. When + // the replica applies a ChangeReplicasTrigger that removes it, it will + // eagerly replicaGC itself. Similarly, snapshots that don't contain the + // recipient are refused. In fact, a stronger invariant holds - replicas + // will never change replicaID in-place. When a replica receives a raft + // message addressing it through a higher replicaID, the replica is + // immediately garbage collected as well. + // + // Unfortunately, the invariant does not hold when the descriptor is + // uninitialized, as we are hitting this code during instantiation phase of + // replicas where they can briefly be in an inconsistent state. These calls + // generally go through tryGetOrCreateReplica and first create a replica from + // an uninitialized descriptor that they then populate if on-disk state is + // present. This is all complex and we would be better off if we made sure + // that a Replica is always initialized (i.e. replace uninitialized replicas + // with a different type, similar to ReplicaPlaceholder). + // + // The invariant is also violated in some tests that set the + // DisableEagerReplicaRemoval testing knob, for example in + // TestStoreReplicaGCAfterMerge. + // + // See: + // https://github.com/cockroachdb/cockroach/pull/40892 + if !r.store.TestingKnobs().DisableEagerReplicaRemoval && r.mu.state.Desc.IsInitialized() { + replDesc, ok := r.mu.state.Desc.GetReplicaDescriptor(r.store.StoreID()) + if !ok { + log.Fatalf(ctx, "%+v does not contain local store s%d", r.mu.state.Desc, r.store.StoreID()) + } + if replDesc.ReplicaID != r.mu.replicaID { + log.Fatalf(ctx, "replica's replicaID %d diverges from descriptor %+v", r.mu.replicaID, r.mu.state.Desc) + } + } } // TODO(nvanbenschoten): move the following 5 methods to replica_send.go. diff --git a/pkg/kv/kvserver/replica_application_state_machine.go b/pkg/kv/kvserver/replica_application_state_machine.go index e0259aed8f13..225425da3945 100644 --- a/pkg/kv/kvserver/replica_application_state_machine.go +++ b/pkg/kv/kvserver/replica_application_state_machine.go @@ -560,13 +560,6 @@ func (b *replicaAppBatch) stageWriteBatch(ctx context.Context, cmd *replicatedCm func changeRemovesStore( desc *roachpb.RangeDescriptor, change *kvserverpb.ChangeReplicas, storeID roachpb.StoreID, ) (removesStore bool) { - _, existsInDesc := desc.GetReplicaDescriptor(storeID) - // NB: if we're catching up from a preemptive snapshot then we won't - // exist in the current descriptor and we can't be removed. - if !existsInDesc { - return false - } - // NB: We don't use change.Removed() because it will include replicas being // transitioned to VOTER_OUTGOING. From 80a9eba9426a2c67588da76217256d8ddaf9c004 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Fri, 29 Oct 2021 17:25:22 +0200 Subject: [PATCH 135/205] kvserver: remove env-defaults for snapshot rate limits The cluster settings should be used instead. Neither env var was referenced from our codebase, i.e. they are not used in roachtests. Also, update a TODO. Release note: None --- pkg/kv/kvserver/store_snapshot.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 5fe889549dd8..50db492f0edd 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -25,7 +25,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/storage" enginepb "github.com/cockroachdb/cockroach/pkg/storage/enginepb" - "github.com/cockroachdb/cockroach/pkg/util/envutil" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" "github.com/cockroachdb/cockroach/pkg/util/log" @@ -802,20 +801,23 @@ func validatePositive(v int64) error { var rebalanceSnapshotRate = settings.RegisterByteSizeSetting( "kv.snapshot_rebalance.max_rate", "the rate limit (bytes/sec) to use for rebalance and upreplication snapshots", - envutil.EnvOrDefaultBytes("COCKROACH_PREEMPTIVE_SNAPSHOT_RATE", 32<<20), + 32<<20, // 32mb/s validatePositive, ).WithPublic().WithSystemOnly() // recoverySnapshotRate is the rate at which Raft-initiated spanshots can be // sent. Ideally, one would never see a Raft-initiated snapshot; we'd like all -// the snapshots to be preemptive. However, it has proved unfeasible to -// completely get rid of them. +// replicas to start out as learners or via splits, and to never be cut off from +// the log. However, it has proved unfeasible to completely get rid of them. +// // TODO(tbg): The existence of this rate, separate from rebalanceSnapshotRate, -// does not make a whole lot of sense. +// does not make a whole lot of sense. Both sources of snapshots compete thanks +// to a semaphore at the receiver, and so the slower one ultimately determines +// the pace at which things can move along. var recoverySnapshotRate = settings.RegisterByteSizeSetting( "kv.snapshot_recovery.max_rate", "the rate limit (bytes/sec) to use for recovery snapshots", - envutil.EnvOrDefaultBytes("COCKROACH_RAFT_SNAPSHOT_RATE", 32<<20), + 32<<20, // 32mb/s validatePositive, ).WithPublic().WithSystemOnly() From c12a5a383c6749446823307fa611ce1cc787571d Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Fri, 29 Oct 2021 17:01:10 -0400 Subject: [PATCH 136/205] dev: plumb --cpus= down when --stress is specified Helps with not bricking your machine temporarily. Release note: None --- pkg/cmd/dev/test.go | 48 +++++++++++++++---------- pkg/cmd/dev/testdata/recording/test.txt | 17 +++++++-- pkg/cmd/dev/testdata/test.txt | 9 +++-- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/pkg/cmd/dev/test.go b/pkg/cmd/dev/test.go index 738608203ce3..1b81f2955be5 100644 --- a/pkg/cmd/dev/test.go +++ b/pkg/cmd/dev/test.go @@ -69,16 +69,18 @@ func makeTestCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Comm func (d *dev) test(cmd *cobra.Command, commandLine []string) error { pkgs, additionalBazelArgs := splitArgsAtDash(cmd, commandLine) ctx := cmd.Context() - stress := mustGetFlagBool(cmd, stressFlag) - stressArgs := mustGetFlagString(cmd, stressArgsFlag) - race := mustGetFlagBool(cmd, raceFlag) - filter := mustGetFlagString(cmd, filterFlag) - timeout := mustGetFlagDuration(cmd, timeoutFlag) - short := mustGetFlagBool(cmd, shortFlag) - ignoreCache := mustGetFlagBool(cmd, ignoreCacheFlag) - verbose := mustGetFlagBool(cmd, vFlag) - rewriteArg := mustGetFlagString(cmd, rewriteArgFlag) - rewrite := mustGetFlagString(cmd, rewriteFlag) + var ( + filter = mustGetFlagString(cmd, filterFlag) + ignoreCache = mustGetFlagBool(cmd, ignoreCacheFlag) + race = mustGetFlagBool(cmd, raceFlag) + rewrite = mustGetFlagString(cmd, rewriteFlag) + rewriteArg = mustGetFlagString(cmd, rewriteArgFlag) + short = mustGetFlagBool(cmd, shortFlag) + stress = mustGetFlagBool(cmd, stressFlag) + stressArgs = mustGetFlagString(cmd, stressArgsFlag) + timeout = mustGetFlagDuration(cmd, timeoutFlag) + verbose = mustGetFlagBool(cmd, vFlag) + ) if rewriteArg != "" && rewrite == "" { rewrite = "-rewrite" } @@ -175,12 +177,17 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { args = append(args, fmt.Sprintf("--sandbox_writable_path=%s", filepath.Join(workspace, dir))) } } + if timeout > 0 && !stress { + args = append(args, fmt.Sprintf("--test_timeout=%d", int(timeout.Seconds()))) + + // If stress is specified, we'll pad the timeout below. + } + if stress { + var stressCmdArgs []string if timeout > 0 { - args = append(args, "--run_under", - fmt.Sprintf("%s -maxtime=%s %s", stressTarget, timeout, stressArgs)) - - // The timeout should be higher than the stress duration, lets + stressCmdArgs = append(stressCmdArgs, fmt.Sprintf("-maxtime=%s", timeout)) + // The bazel timeout should be higher than the stress duration, lets // generously give it an extra minute. args = append(args, fmt.Sprintf("--test_timeout=%d", int((timeout+time.Minute).Seconds()))) } else { @@ -189,13 +196,16 @@ func (d *dev) test(cmd *cobra.Command, commandLine []string) error { // we want the bazel timeout to be longer, so lets just set it to // 24h. // - // [1]: Through --stress-arg=-maxtime or if nothing is specified, a - // -maxtime of 0 that's taken as "run forever") - args = append(args, "--run_under", fmt.Sprintf("%s %s", stressTarget, stressArgs)) + // [1]: Through --stress-arg=-maxtime or if nothing is specified, + // -maxtime=0 is taken as "run forever". args = append(args, fmt.Sprintf("--test_timeout=%.0f", 24*time.Hour.Seconds())) } - } else if timeout > 0 { - args = append(args, fmt.Sprintf("--test_timeout=%d", int(timeout.Seconds()))) + if numCPUs > 0 { + stressCmdArgs = append(stressCmdArgs, fmt.Sprintf("-p=%d", numCPUs)) + } + stressCmdArgs = append(stressCmdArgs, stressArgs) + args = append(args, "--run_under", + fmt.Sprintf("%s %s", stressTarget, strings.Join(stressCmdArgs, " "))) } if filter != "" { diff --git a/pkg/cmd/dev/testdata/recording/test.txt b/pkg/cmd/dev/testdata/recording/test.txt index 51ea3d0f69ab..3d70eed4d432 100644 --- a/pkg/cmd/dev/testdata/recording/test.txt +++ b/pkg/cmd/dev/testdata/recording/test.txt @@ -84,7 +84,7 @@ bazel query 'kind(go_test, //pkg/util/tracing:all)' ---- //pkg/util/tracing:tracing_test -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress ' --test_timeout=86400 '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --test_timeout=86400 --run_under '@com_github_cockroachdb_stress//:stress ' '--test_filter=TestStartChild*' --test_output streamed ---- ---- //pkg/util/tracing:tracing_test PASSED in 12.3s @@ -97,7 +97,20 @@ bazel query 'kind(go_test, //pkg/util/tracing:all)' ---- //pkg/util/tracing:tracing_test -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' --test_timeout=70 '--test_filter=TestStartChild*' --test_output streamed +bazel test --local_cpu_resources=12 --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --test_timeout=86400 --run_under '@com_github_cockroachdb_stress//:stress -p=12 ' '--test_filter=TestStartChild*' --test_output streamed +---- +---- +//pkg/util/tracing:tracing_test PASSED in 12.3s + +Executed 1 out of 1 test: 1 test passes. +---- +---- + +bazel query 'kind(go_test, //pkg/util/tracing:all)' +---- +//pkg/util/tracing:tracing_test + +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --test_timeout=70 --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' '--test_filter=TestStartChild*' --test_output streamed ---- ---- ==================== Test output for //pkg/util/tracing:tracing_test: diff --git a/pkg/cmd/dev/testdata/test.txt b/pkg/cmd/dev/testdata/test.txt index 1fb3685efab0..b5535fb4d6a2 100644 --- a/pkg/cmd/dev/testdata/test.txt +++ b/pkg/cmd/dev/testdata/test.txt @@ -31,12 +31,17 @@ bazel test //pkg/util/tracing:tracing_test --nocache_test_results '--test_filter dev test --stress pkg/util/tracing --filter TestStartChild* ---- bazel query 'kind(go_test, //pkg/util/tracing:all)' -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress ' --test_timeout=86400 '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --test_timeout=86400 --run_under '@com_github_cockroachdb_stress//:stress ' '--test_filter=TestStartChild*' --test_output streamed + +dev test --stress pkg/util/tracing --filter TestStartChild* --cpus=12 +---- +bazel query 'kind(go_test, //pkg/util/tracing:all)' +bazel test --local_cpu_resources=12 --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --test_timeout=86400 --run_under '@com_github_cockroachdb_stress//:stress -p=12 ' '--test_filter=TestStartChild*' --test_output streamed dev test --stress pkg/util/tracing --filter TestStartChild* --timeout=10s -v ---- bazel query 'kind(go_test, //pkg/util/tracing:all)' -bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' --test_timeout=70 '--test_filter=TestStartChild*' --test_output streamed +bazel test --test_sharding_strategy=disabled //pkg/util/tracing:tracing_test --test_timeout=70 --run_under '@com_github_cockroachdb_stress//:stress -maxtime=10s ' '--test_filter=TestStartChild*' --test_output streamed dev test //pkg/testutils --timeout=10s ---- From eba7c0e69673ca235d72b260dcd6765711e69995 Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Fri, 29 Oct 2021 21:18:34 -0400 Subject: [PATCH 137/205] sql: clarify !IsCanonical path wrt scan.ExactPrefix Passing scan.ExactPrefix with other indexes is a little confusing, make it clear that for non-canonical scans exactPrefix will always be zero. Release note: None --- pkg/sql/opt/ordering/interesting_orderings.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/sql/opt/ordering/interesting_orderings.go b/pkg/sql/opt/ordering/interesting_orderings.go index 550191797986..27ff0f3a7737 100644 --- a/pkg/sql/opt/ordering/interesting_orderings.go +++ b/pkg/sql/opt/ordering/interesting_orderings.go @@ -108,7 +108,8 @@ func interestingOrderingsForScan(scan *memo.ScanExpr) props.OrderingSet { // the table's indexes. Add orderings for all of them. ord = make(props.OrderingSet, 0, tab.IndexCount()) for i := 0; i < tab.IndexCount(); i++ { - addIndexOrdering(i, &scan.Relational().FuncDeps, scan.ExactPrefix) + // IsCanonical implies no constraints so exactPrefix is 0. + addIndexOrdering(i, &scan.Relational().FuncDeps, 0) } } else { // This scan is not canonical, so we can only use the ordering implied by From b08c31d5c34c35ae34e048b55f49b5af29904474 Mon Sep 17 00:00:00 2001 From: Yevgeniy Miretskiy Date: Sat, 30 Oct 2021 08:27:40 -0400 Subject: [PATCH 138/205] changefeedccl: Move metrics scope library under changefeedccl package. Move metrics scope library out of cdcutil directly under changefeedccl. Metrics scope is not much of a library since it's specific to changefeedccl. Note: this is a move only refactoring, but it is structured as 2 changes (one to add new files, and one to remove cdcutils files) so that it can be easily backported. Release Notes: None --- pkg/ccl/changefeedccl/BUILD.bazel | 3 + pkg/ccl/changefeedccl/metrics_scope.go | 77 +++++++++++++++++++++ pkg/ccl/changefeedccl/metrics_scope_test.go | 57 +++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 pkg/ccl/changefeedccl/metrics_scope.go create mode 100644 pkg/ccl/changefeedccl/metrics_scope_test.go diff --git a/pkg/ccl/changefeedccl/BUILD.bazel b/pkg/ccl/changefeedccl/BUILD.bazel index 14bbc91e56f3..ad6d298d400f 100644 --- a/pkg/ccl/changefeedccl/BUILD.bazel +++ b/pkg/ccl/changefeedccl/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "doc.go", "encoder.go", "metrics.go", + "metrics_scope.go", "name.go", "rowfetcher_cache.go", "schema_registry.go", @@ -122,6 +123,7 @@ go_test( "helpers_tenant_shim_test.go", "helpers_test.go", "main_test.go", + "metrics_scope_test.go", "name_test.go", "nemeses_test.go", "schema_registry_test.go", @@ -200,6 +202,7 @@ go_test( "//pkg/util/json", "//pkg/util/leaktest", "//pkg/util/log", + "//pkg/util/metric", "//pkg/util/mon", "//pkg/util/protoutil", "//pkg/util/randutil", diff --git a/pkg/ccl/changefeedccl/metrics_scope.go b/pkg/ccl/changefeedccl/metrics_scope.go new file mode 100644 index 000000000000..3feeaf2db71a --- /dev/null +++ b/pkg/ccl/changefeedccl/metrics_scope.go @@ -0,0 +1,77 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package changefeedccl + +import ( + "fmt" + + "github.com/cockroachdb/cockroach/pkg/util/metric" +) + +// maxSLIScopes is a static limit on the number of SLI scopes -- that is the number +// of SLI metrics we will keep track of. +// The limit is static due to metric.Registry limitations. +const maxSLIScopes = 8 + +// SLIMetrics is the list of SLI related metrics for changefeeds. +type SLIMetrics struct { + ErrorRetries *metric.Counter + // TODO(yevgeniy): Add more SLI related metrics. +} + +// MetricStruct implements metric.Struct interface +func (*SLIMetrics) MetricStruct() {} + +func makeSLIMetrics(prefix string) *SLIMetrics { + withPrefix := func(meta metric.Metadata) metric.Metadata { + meta.Name = fmt.Sprintf("%s.%s", prefix, meta.Name) + return meta + } + + return &SLIMetrics{ + ErrorRetries: metric.NewCounter(withPrefix(metric.Metadata{ + Name: "error_retries", + Help: "Total retryable errors encountered this SLI", + Measurement: "Errors", + Unit: metric.Unit_COUNT, + })), + } +} + +// SLIScopes represents a set of SLI related metrics for a particular "scope". +type SLIScopes struct { + Scopes [maxSLIScopes]*SLIMetrics // Exported so that we can register w/ metrics registry. + names map[string]*SLIMetrics +} + +// MetricStruct implements metric.Struct interface +func (*SLIScopes) MetricStruct() {} + +// CreateSLIScopes creates changefeed specific SLI scope: a metric.Struct containing +// SLI specific metrics for each scope. +// The scopes are statically named "tier", and each metric name +// contained in SLIMetrics will be prefixed by "changefeed.tier Date: Sat, 30 Oct 2021 08:31:47 -0400 Subject: [PATCH 139/205] changefeedccl: Remove metrics scope library. Remove metrics scope library since it has been moved under changefeedccl. Note: this is a move only refactoring, but it is structured as 2 changes (one to add new files, and one to remove cdcutils files) so that it can be easily backported. Release Notes: None --- pkg/ccl/changefeedccl/cdcutils/BUILD.bazel | 11 +-- .../changefeedccl/cdcutils/metrics_scope.go | 77 ------------------- .../cdcutils/metrics_scope_test.go | 57 -------------- 3 files changed, 2 insertions(+), 143 deletions(-) delete mode 100644 pkg/ccl/changefeedccl/cdcutils/metrics_scope.go delete mode 100644 pkg/ccl/changefeedccl/cdcutils/metrics_scope_test.go diff --git a/pkg/ccl/changefeedccl/cdcutils/BUILD.bazel b/pkg/ccl/changefeedccl/cdcutils/BUILD.bazel index a9dfffd76a83..993c43479b4e 100644 --- a/pkg/ccl/changefeedccl/cdcutils/BUILD.bazel +++ b/pkg/ccl/changefeedccl/cdcutils/BUILD.bazel @@ -2,10 +2,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "cdcutils", - srcs = [ - "metrics_scope.go", - "throttle.go", - ], + srcs = ["throttle.go"], importpath = "github.com/cockroachdb/cockroach/pkg/ccl/changefeedccl/cdcutils", visibility = ["//visibility:public"], deps = [ @@ -21,17 +18,13 @@ go_library( go_test( name = "cdcutils_test", - srcs = [ - "metrics_scope_test.go", - "throttle_test.go", - ], + srcs = ["throttle_test.go"], embed = [":cdcutils"], deps = [ "//pkg/ccl/changefeedccl/changefeedbase", "//pkg/settings/cluster", "//pkg/util/leaktest", "//pkg/util/log", - "//pkg/util/metric", "@com_github_stretchr_testify//require", ], ) diff --git a/pkg/ccl/changefeedccl/cdcutils/metrics_scope.go b/pkg/ccl/changefeedccl/cdcutils/metrics_scope.go deleted file mode 100644 index a69430dbe54a..000000000000 --- a/pkg/ccl/changefeedccl/cdcutils/metrics_scope.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2021 The Cockroach Authors. -// -// Licensed as a CockroachDB Enterprise file under the Cockroach Community -// License (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt - -package cdcutils - -import ( - "fmt" - - "github.com/cockroachdb/cockroach/pkg/util/metric" -) - -// maxSLIScopes is a static limit on the number of SLI scopes -- that is the number -// of SLI metrics we will keep track of. -// The limit is static due to metric.Registry limitations. -const maxSLIScopes = 8 - -// SLIMetrics is the list of SLI related metrics for changefeeds. -type SLIMetrics struct { - ErrorRetries *metric.Counter - // TODO(yevgeniy): Add more SLI related metrics. -} - -// MetricStruct implements metric.Struct interface -func (*SLIMetrics) MetricStruct() {} - -func makeSLIMetrics(prefix string) *SLIMetrics { - withPrefix := func(meta metric.Metadata) metric.Metadata { - meta.Name = fmt.Sprintf("%s.%s", prefix, meta.Name) - return meta - } - - return &SLIMetrics{ - ErrorRetries: metric.NewCounter(withPrefix(metric.Metadata{ - Name: "error_retries", - Help: "Total retryable errors encountered this SLI", - Measurement: "Errors", - Unit: metric.Unit_COUNT, - })), - } -} - -// SLIScopes represents a set of SLI related metrics for a particular "scope". -type SLIScopes struct { - Scopes [maxSLIScopes]*SLIMetrics // Exported so that we can register w/ metrics registry. - names map[string]*SLIMetrics -} - -// MetricStruct implements metric.Struct interface -func (*SLIScopes) MetricStruct() {} - -// CreateSLIScopes creates changefeed specific SLI scope: a metric.Struct containing -// SLI specific metrics for each scope. -// The scopes are statically named "tier", and each metric name -// contained in SLIMetrics will be prefixed by "changefeed.tier Date: Sun, 31 Oct 2021 22:29:45 -0400 Subject: [PATCH 140/205] authors: add Chengxiong Ruan to authors Release note: None --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 1e44990f393a..11c1878f20f4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -101,6 +101,7 @@ Celia La Céline O'Neil Charlie Vieth chengwei <252684445@qq.com> +Chengxiong Ruan Chris Seto <@cockroachlabs.com> Christian Meunier Christopher Routh christopherrouth From 1e7cd0b37cd5866f187415a09939d4d4d9b1d508 Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Mon, 1 Nov 2021 11:58:42 +0000 Subject: [PATCH 141/205] roachtest: fix import roachtest This change fixes the import roachtest to switch to the `csv` database before creating the table and importing into it. This broke when we switched the roachtest to use IMPORT INTO from IMPORT CREATE USING in https://github.com/cockroachdb/cockroach/pull/72097. Fixes: #68037 Release note: None --- pkg/cmd/roachtest/tests/import.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/cmd/roachtest/tests/import.go b/pkg/cmd/roachtest/tests/import.go index 768ba1731a54..6935485908f9 100644 --- a/pkg/cmd/roachtest/tests/import.go +++ b/pkg/cmd/roachtest/tests/import.go @@ -217,6 +217,9 @@ func registerImportTPCH(r registry.Registry) { if _, err := conn.Exec(`CREATE DATABASE csv;`); err != nil { t.Fatal(err) } + if _, err := conn.Exec(`USE csv;`); err != nil { + t.Fatal(err) + } if _, err := conn.Exec( `SET CLUSTER SETTING kv.bulk_ingest.max_index_buffer_size = '2gb'`, ); err != nil && !strings.Contains(err.Error(), "unknown cluster setting") { From df752499192d03b058737d0d768d07a14c0a7ff3 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Thu, 28 Oct 2021 10:40:45 -0500 Subject: [PATCH 142/205] bazel: glob-exclude all `*_generated.go` files from Gazelle This will help ensure that Gazelle doesn't accidentally reference checked-in generated code. The generated code in `pkg/col/colserde/arrowserde` is made by `flatc`, the FlatBuffers compiler -- I tried to make a `genrule` to generate this code but the code that `flatc` generates now doesn't match up to what's checked in (maybe we used a different version of `flatc` for the original version of the code?) Closes #72096. Release note: None --- BUILD.bazel | 19 +------------------ pkg/col/colserde/arrowserde/BUILD.bazel | 12 +++++++----- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 18fe230a77e3..da1edd0c12c9 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -69,7 +69,6 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # gazelle:resolve proto go roachpb/io-formats.proto //pkg/roachpb:with-mocks # gazelle:resolve proto go roachpb/metadata.proto //pkg/roachpb:with-mocks # gazelle:resolve proto go roachpb/span_config.proto //pkg/roachpb:with-mocks -# gazelle:exclude pkg/roachpb/batch_generated.go # gazelle:exclude pkg/roachpb/batch_generated-gen.go # See pkg/sql/opt/optgen/cmd/langgen/BUILD.bazel for more details. @@ -107,7 +106,7 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # gazelle:exclude **/*.pb.gw.go # gazelle:exclude **/*_interval_btree.go # gazelle:exclude **/*_interval_btree_test.go -# gazelle:exclude **/mocks_generated.go +# gazelle:exclude **/*_generated.go # gazelle:exclude pkg/sql/parser/sql.go # gazelle:exclude pkg/sql/parser/helpmap_test.go # gazelle:exclude pkg/sql/parser/help_messages.go @@ -118,25 +117,9 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # gazelle:exclude pkg/testutils/**/testdata/** # gazelle:exclude pkg/security/securitytest/embedded.go # gazelle:exclude pkg/cmd/roachprod/vm/aws/embedded.go -# gazelle:exclude pkg/cmd/roachtest/prometheus/mock_generated.go -# gazelle:exclude pkg/cmd/roachtest/tests/drt_generated.go # gazelle:exclude pkg/**/*_string.go -# gazelle:exclude pkg/geo/wkt/wkt_generated.go -# gazelle:exclude pkg/sql/schemachanger/scop/backfill_visitor_generated.go -# gazelle:exclude pkg/sql/schemachanger/scop/mutation_visitor_generated.go -# gazelle:exclude pkg/sql/schemachanger/scop/validation_visitor_generated.go -# gazelle:exclude pkg/sql/schemachanger/scpb/elements_generated.go # gazelle:exclude pkg/ui/distccl/distccl_no_bazel.go # gazelle:exclude pkg/ui/distoss/distoss_no_bazel.go -# gazelle:exclude pkg/util/log/channel/channel_generated.go -# gazelle:exclude pkg/util/log/eventpb/eventlog_channels_generated.go -# gazelle:exclude pkg/util/log/eventpb/json_encode_generated.go -# gazelle:exclude pkg/util/log/log_channels_generated.go -# gazelle:exclude pkg/util/log/severity/severity_generated.go -# gazelle:exclude pkg/util/timeutil/lowercase_timezones_generated.go -# -# TODO(irfansharif): Hand excluding these _generated.go/_stringer.go files is -# silly, we should glob exclude everything once we have full coverage. # # Generally useful references: # diff --git a/pkg/col/colserde/arrowserde/BUILD.bazel b/pkg/col/colserde/arrowserde/BUILD.bazel index c428649ff886..6d5a6ce74764 100644 --- a/pkg/col/colserde/arrowserde/BUILD.bazel +++ b/pkg/col/colserde/arrowserde/BUILD.bazel @@ -4,12 +4,14 @@ go_library( name = "arrowserde", srcs = [ "doc.go", - "file_generated.go", - "message_generated.go", - "schema_generated.go", - "tensor_generated.go", + "file_generated.go", # keep + "message_generated.go", # keep + "schema_generated.go", # keep + "tensor_generated.go", # keep ], importpath = "github.com/cockroachdb/cockroach/pkg/col/colserde/arrowserde", visibility = ["//visibility:public"], - deps = ["@com_github_google_flatbuffers//go"], + deps = [ + "@com_github_google_flatbuffers//go", # keep + ], ) From ea0c434a7b4ee7d993ff880b6c97d67378fa5cd0 Mon Sep 17 00:00:00 2001 From: Azhng Date: Thu, 28 Oct 2021 14:03:41 -0400 Subject: [PATCH 143/205] sql: textual explain analyze now shows max mem/disk stats Resolves #66109 Release note (sql change): explain analyze now shows maximum allocated memory and maximum sql temp disk usage for a statement. --- pkg/sql/instrumentation.go | 2 ++ .../exec/execbuilder/testdata/dist_vectorize | 5 +++++ .../exec/execbuilder/testdata/explain_analyze | 6 ++++++ .../testdata/explain_analyze_plans | 21 +++++++++++++++++++ .../testdata/inverted_index_geospatial | 18 ++++++++++++++++ pkg/sql/opt/exec/execbuilder/testdata/prepare | 2 ++ .../exec/execbuilder/testdata/vectorize_local | 8 +++++++ pkg/sql/opt/exec/explain/emit.go | 6 ++++++ pkg/sql/opt/exec/factory.go | 3 +++ 9 files changed, 71 insertions(+) diff --git a/pkg/sql/instrumentation.go b/pkg/sql/instrumentation.go index 38bd125ea23e..48602982b06a 100644 --- a/pkg/sql/instrumentation.go +++ b/pkg/sql/instrumentation.go @@ -587,6 +587,8 @@ func (m execNodeTraceMetadata) annotateExplain( nodeStats.SeekCount.MaybeAdd(stats.KV.NumInterfaceSeeks) nodeStats.InternalSeekCount.MaybeAdd(stats.KV.NumInternalSeeks) nodeStats.VectorizedBatchCount.MaybeAdd(stats.Output.NumBatches) + nodeStats.MaxAllocatedMem.MaybeAdd(stats.Exec.MaxAllocatedMem) + nodeStats.MaxAllocatedDisk.MaybeAdd(stats.Exec.MaxAllocatedDisk) } // If we didn't get statistics for all processors, we don't show the // incomplete results. In the future, we may consider an incomplete flag diff --git a/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize b/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize index 7039aae6ac5d..9cf8da8653ea 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize +++ b/pkg/sql/opt/exec/execbuilder/testdata/dist_vectorize @@ -73,6 +73,7 @@ regions: KV contention time: 0µs KV rows read: 5 KV bytes read: 40 B + estimated max memory allocated: 0 B missing stats table: kv@kv_pkey spans: FULL SCAN @@ -95,6 +96,8 @@ regions: │ nodes: │ regions: │ actual row count: 5 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ equality: (k) = (k) │ left cols are key │ right cols are key @@ -107,6 +110,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 5 │ KV bytes read: 40 B +│ estimated max memory allocated: 0 B │ missing stats │ table: kv@kv_pkey │ spans: FULL SCAN @@ -119,6 +123,7 @@ regions: KV contention time: 0µs KV rows read: 5 KV bytes read: 40 B + estimated max memory allocated: 0 B missing stats table: kw@kw_pkey spans: FULL SCAN diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze b/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze index d07e1b018e34..a1afbd3de4a2 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze @@ -22,6 +22,7 @@ regions: KV contention time: 0µs KV rows read: 0 KV bytes read: 0 B + estimated max memory allocated: 0 B missing stats table: kv@kv_pkey spans: [/2 - ] @@ -49,6 +50,7 @@ regions: KV contention time: 0µs KV rows read: 3 KV bytes read: 24 B + estimated max memory allocated: 0 B missing stats table: kv@kv_pkey spans: [/2 - ] @@ -75,6 +77,8 @@ regions: │ regions: │ actual row count: 2 │ vectorized batch count: 0 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ estimated row count: 990 (missing stats) │ equality: (v) = (a) │ right cols are key @@ -89,6 +93,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 4 │ KV bytes read: 32 B +│ estimated max memory allocated: 0 B │ MVCC step count (ext/int): 0/0 │ MVCC seek count (ext/int): 0/0 │ estimated row count: 1,000 (missing stats) @@ -105,6 +110,7 @@ regions: KV contention time: 0µs KV rows read: 3 KV bytes read: 24 B + estimated max memory allocated: 0 B MVCC step count (ext/int): 0/0 MVCC seek count (ext/int): 0/0 estimated row count: 1,000 (missing stats) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans b/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans index 1359b93065da..b34a30476674 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain_analyze_plans @@ -80,6 +80,8 @@ regions: │ nodes: │ regions: │ actual row count: 5 + │ estimated max memory allocated: 0 B + │ estimated max sql temp disk usage: 0 B │ equality: (k) = (k) │ left cols are key │ right cols are key @@ -92,6 +94,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 5 │ KV bytes read: 40 B + │ estimated max memory allocated: 0 B │ missing stats │ table: kv@kv_pkey │ spans: FULL SCAN @@ -104,6 +107,7 @@ regions: KV contention time: 0µs KV rows read: 5 KV bytes read: 40 B + estimated max memory allocated: 0 B missing stats table: kw@kw_pkey spans: FULL SCAN @@ -127,18 +131,24 @@ regions: │ nodes: │ regions: │ actual row count: 5 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ order: +w │ └── • distinct │ nodes: │ regions: │ actual row count: 5 + │ estimated max memory allocated: 0 B + │ estimated max sql temp disk usage: 0 B │ distinct on: w │ └── • hash join │ nodes: │ regions: │ actual row count: 5 + │ estimated max memory allocated: 0 B + │ estimated max sql temp disk usage: 0 B │ equality: (k) = (w) │ left cols are key │ @@ -150,6 +160,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 5 │ KV bytes read: 40 B + │ estimated max memory allocated: 0 B │ missing stats │ table: kv@kv_pkey │ spans: FULL SCAN @@ -162,6 +173,7 @@ regions: KV contention time: 0µs KV rows read: 5 KV bytes read: 40 B + estimated max memory allocated: 0 B missing stats table: kw@kw_pkey spans: FULL SCAN @@ -185,6 +197,8 @@ regions: │ nodes: │ regions: │ actual row count: 25 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ ├── • ordinality │ │ nodes: @@ -199,6 +213,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 5 │ KV bytes read: 40 B +│ estimated max memory allocated: 0 B │ missing stats │ table: kv@kv_pkey │ spans: FULL SCAN @@ -216,6 +231,7 @@ regions: KV contention time: 0µs KV rows read: 5 KV bytes read: 40 B + estimated max memory allocated: 0 B missing stats table: kv@kv_pkey spans: FULL SCAN @@ -260,6 +276,8 @@ regions: │ nodes: │ regions: │ actual row count: 5 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ └── • scan nodes: @@ -269,6 +287,7 @@ regions: KV contention time: 0µs KV rows read: 5 KV bytes read: 40 B + estimated max memory allocated: 0 B missing stats table: kv@kv_pkey spans: FULL SCAN @@ -296,6 +315,7 @@ regions: KV contention time: 0µs KV rows read: 0 KV bytes read: 0 B + estimated max memory allocated: 0 B missing stats table: kv@kv_pkey spans: [/0 - /0] @@ -352,6 +372,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 1 │ KV bytes read: 8 B +│ estimated max memory allocated: 0 B │ missing stats │ table: parent@parent_pkey │ spans: LIMITED SCAN diff --git a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index_geospatial b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index_geospatial index 8d8ed3ed7d4c..322e17ef7910 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index_geospatial +++ b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index_geospatial @@ -35,6 +35,8 @@ regions: │ nodes: │ regions: │ actual row count: 2 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ order: +k │ └── • filter @@ -51,12 +53,15 @@ regions: │ KV contention time: 0µs │ KV rows read: 2 │ KV bytes read: 16 B + │ estimated max memory allocated: 0 B │ table: geo_table@geo_table_pkey │ └── • inverted filter │ nodes: │ regions: │ actual row count: 2 + │ estimated max memory allocated: 0 B + │ estimated max sql temp disk usage: 0 B │ inverted column: geom_inverted_key │ num spans: 31 │ @@ -68,6 +73,7 @@ regions: KV contention time: 0µs KV rows read: 4 KV bytes read: 32 B + estimated max memory allocated: 0 B missing stats table: geo_table@geom_index spans: 31 spans @@ -113,6 +119,8 @@ regions: │ nodes: │ regions: │ actual row count: 2 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ order: +k │ └── • filter @@ -129,12 +137,15 @@ regions: │ KV contention time: 0µs │ KV rows read: 2 │ KV bytes read: 16 B + │ estimated max memory allocated: 0 B │ table: geo_table@geo_table_pkey │ └── • inverted filter │ nodes: │ regions: │ actual row count: 2 + │ estimated max memory allocated: 0 B + │ estimated max sql temp disk usage: 0 B │ inverted column: geom_inverted_key │ num spans: 31 │ @@ -146,6 +157,7 @@ regions: KV contention time: 0µs KV rows read: 2 KV bytes read: 16 B + estimated max memory allocated: 0 B missing stats table: geo_table@geom_index spans: 31 spans @@ -175,6 +187,8 @@ regions: │ nodes: │ regions: │ actual row count: 2 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ order: +k │ └── • filter @@ -191,12 +205,15 @@ regions: │ KV contention time: 0µs │ KV rows read: 2 │ KV bytes read: 16 B + │ estimated max memory allocated: 0 B │ table: geo_table@geo_table_pkey │ └── • inverted filter │ nodes: │ regions: │ actual row count: 2 + │ estimated max memory allocated: 0 B + │ estimated max sql temp disk usage: 0 B │ inverted column: geom_inverted_key │ num spans: 31 │ @@ -208,6 +225,7 @@ regions: KV contention time: 0µs KV rows read: 2 KV bytes read: 16 B + estimated max memory allocated: 0 B missing stats table: geo_table@geom_index spans: 31 spans diff --git a/pkg/sql/opt/exec/execbuilder/testdata/prepare b/pkg/sql/opt/exec/execbuilder/testdata/prepare index f90197667797..509586b6ff6d 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/prepare +++ b/pkg/sql/opt/exec/execbuilder/testdata/prepare @@ -128,6 +128,7 @@ regions: KV contention time: 0µs KV rows read: 1 KV bytes read: 8 B + estimated max memory allocated: 0 B estimated row count: 0 table: ab@ab_pkey spans: [/1 - /1] @@ -151,6 +152,7 @@ regions: KV contention time: 0µs KV rows read: 0 KV bytes read: 0 B + estimated max memory allocated: 0 B estimated row count: 0 table: ab@ab_pkey spans: [/2 - /2] diff --git a/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local b/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local index aba3ce5f0de5..e55e92ad6fc6 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local +++ b/pkg/sql/opt/exec/execbuilder/testdata/vectorize_local @@ -55,6 +55,7 @@ regions: KV contention time: 0µs KV rows read: 2,001 KV bytes read: 16 KiB + estimated max memory allocated: 0 B missing stats table: a@a_pkey spans: FULL SCAN @@ -92,6 +93,7 @@ regions: KV contention time: 0µs KV rows read: 2 KV bytes read: 16 B + estimated max memory allocated: 0 B estimated row count: 1 (100% of the table; stats collected ago) table: c@sec spans: FULL SCAN @@ -155,12 +157,16 @@ regions: │ nodes: │ regions: │ actual row count: 2 +│ estimated max memory allocated: 0 B +│ estimated max sql temp disk usage: 0 B │ equality: (a) = (b) │ ├── • sort │ │ nodes: │ │ regions: │ │ actual row count: 2 +│ │ estimated max memory allocated: 0 B +│ │ estimated max sql temp disk usage: 0 B │ │ estimated row count: 1 │ │ order: +a │ │ @@ -172,6 +178,7 @@ regions: │ KV contention time: 0µs │ KV rows read: 2 │ KV bytes read: 16 B +│ estimated max memory allocated: 0 B │ estimated row count: 1 (100% of the table; stats collected ago) │ table: c@sec │ spans: FULL SCAN @@ -184,6 +191,7 @@ regions: KV contention time: 0µs KV rows read: 2 KV bytes read: 16 B + estimated max memory allocated: 0 B missing stats table: d@d_pkey spans: FULL SCAN diff --git a/pkg/sql/opt/exec/explain/emit.go b/pkg/sql/opt/exec/explain/emit.go index 5abd834dab32..c8ce343f7eea 100644 --- a/pkg/sql/opt/exec/explain/emit.go +++ b/pkg/sql/opt/exec/explain/emit.go @@ -376,6 +376,12 @@ func (e *emitter) emitNodeAttributes(n *Node) error { if s.KVBytesRead.HasValue() { e.ob.AddField("KV bytes read", humanize.IBytes(s.KVBytesRead.Value())) } + if s.MaxAllocatedMem.HasValue() { + e.ob.AddField("estimated max memory allocated", humanize.IBytes(s.MaxAllocatedMem.Value())) + } + if s.MaxAllocatedDisk.HasValue() { + e.ob.AddField("estimated max sql temp disk usage", humanize.IBytes(s.MaxAllocatedDisk.Value())) + } if e.ob.flags.Verbose { if s.StepCount.HasValue() { e.ob.AddField("MVCC step count (ext/int)", fmt.Sprintf("%s/%s", diff --git a/pkg/sql/opt/exec/factory.go b/pkg/sql/opt/exec/factory.go index 83f3ae70b947..da1cd6e9e5b0 100644 --- a/pkg/sql/opt/exec/factory.go +++ b/pkg/sql/opt/exec/factory.go @@ -331,6 +331,9 @@ type ExecutionStats struct { SeekCount optional.Uint InternalSeekCount optional.Uint + MaxAllocatedMem optional.Uint + MaxAllocatedDisk optional.Uint + // Nodes on which this operator was executed. Nodes []string From 03603269d847a25e34716cb2dd0a298f4cdac1c0 Mon Sep 17 00:00:00 2001 From: Andrew Werner Date: Mon, 1 Nov 2021 11:12:24 -0400 Subject: [PATCH 144/205] workload/tpcc: check the 90th percentile instead of the .9th The hdrhistogram API was misleading. It expected a percentile for a quantile calculation. All-in-all it appears the author of that library had percentile and quantile confused. At some point in the last year, somebody did add a `ValueAtPercentile` method which has the identical behavior as `ValueAtQuantile`. See https://github.com/cockroachdb/vendored/blob/5b815c7d468d59337db2c742373183af5525e729/github.com/codahale/hdrhistogram/hdr.go#L241-L259 Fixes #72282. Release note: None --- pkg/workload/tpcc/result.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/workload/tpcc/result.go b/pkg/workload/tpcc/result.go index 3394277256f2..1432b1c4a443 100644 --- a/pkg/workload/tpcc/result.go +++ b/pkg/workload/tpcc/result.go @@ -192,7 +192,7 @@ func (r *Result) FailureError() error { if !exists { return errors.Errorf("no %v data exists", query) } - if v := time.Duration(h.ValueAtQuantile(.9)); v > max90th { + if v := time.Duration(h.ValueAtQuantile(90)); v > max90th { err = errors.CombineErrors(err, errors.Errorf("90th percentile latency for %v at %v exceeds passing threshold of %v", query, v, max90th)) From 5017d2908938f03a8f9134e21ea08640adb35142 Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Mon, 1 Nov 2021 11:29:29 -0400 Subject: [PATCH 145/205] codeowners: transfer ownership of jobs package Previously, until the 22.1 release cycle, the jobs package was owned by the SQL Schema team. This ownership has now been transferred to the CDC team. This patch to the CODEOWNERS file reflects this change of ownership. Release note: None --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fad4a3191cbb..e863ee45cddb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -208,7 +208,7 @@ /pkg/internal/rsg/ @cockroachdb/sql-queries /pkg/internal/sqlsmith/ @cockroachdb/sql-queries /pkg/internal/team/ @cockroachdb/test-eng -/pkg/jobs/ @cockroachdb/sql-schema +/pkg/jobs/ @cockroachdb/cdc-prs /pkg/keys/ @cockroachdb/kv-prs # Don't ping KV on updates to reserved descriptor IDs and such. /pkg/keys/constants.go @cockroachdb/kv-prs-noreview From d54673d2e2e8ea404dbd3cd6cb2779f2729a38e1 Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Mon, 1 Nov 2021 11:41:47 -0400 Subject: [PATCH 146/205] storage/metamorphic: Increment endTime in ClearTimeRange Eliminates a panic when endTime ends up being the zero timestamp, and startTime is slightly ahead of endTime as it gets next()'d in TBI creation but endTime doesn't. Fixes #72269 Fixes #72261 Release note: None. --- pkg/storage/metamorphic/operations.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/storage/metamorphic/operations.go b/pkg/storage/metamorphic/operations.go index 6b047945bd88..6ec7d53e05c3 100644 --- a/pkg/storage/metamorphic/operations.go +++ b/pkg/storage/metamorphic/operations.go @@ -930,6 +930,11 @@ var opGenerators = []opGenerator{ if endTime.Less(startTime) { startTime, endTime = endTime, startTime } + // This is to avoid a panic where startTime == endTime == hlc.Timestamp{} + // and where startTime gets next()'d in the time-bound iterator creation + // but endTime doesn't, making it seem like a start timestamp is set + // but an end timestamp is not. + endTime = endTime.Next() return &mvccClearTimeRangeOp{ m: m, writer: writer, From 85b6a04505244f9619dec9b6b48c1ce5b7244d85 Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Sun, 3 Oct 2021 20:40:23 -0400 Subject: [PATCH 147/205] importccl: remove support for non-bundle IMPORT TABLE A non-bundle import refers to the flavour of IMPORT that has separate sources for table creation and data ingestion. A bundle import on the other hand refers to the flavour of IMPORT that reads both the schema and data from the same source. Prior to this change a user could specify the schema of the table being imported into in two ways: - inline definition of the table schema in the IMPORT TABLE command. - IMPORT TABLE ... CREATE USING syntax to point to a file containing a `CREATE TABLE` statement that defines the table schema. Import would then use this schema to first create a table and then import the data from the data source. Table creation in IMPORT was backed by custom table creation logic that attempted to mirror SQL support for `CREATE TABLE` statements. Historically, this always fell behind fixes and additional features that were added to `CREATE TABLE`, resulting in a poor UX when atttempting to create and import into a table, using a single IMPORT command. This change removes support for: - `IMPORT TABLE ... CREATE USING` - `IMPORT TABLE ... DATA` refers to CSV, Delimited, PGCOPY, AVRO. These formats do not define the table schema in the same file as the data. The workaround following this feature removal is to use `CREATE TABLE` with the same schema that was previously being passed into the IMPORT statement, followed by an `IMPORT INTO` the newly created table. Fixes: #70050 Release note (sql change): This change removes support for: - `IMPORT TABLE ... CREATE USING` - `IMPORT TABLE ... DATA` refers to CSV, Delimited, PGCOPY, AVRO. These formats do not define the table schema in the same file as the data. The workaround following this feature removal is to use `CREATE TABLE` with the same schema that was previously being passed into the IMPORT statement, followed by an `IMPORT INTO` the newly created table. --- docs/generated/sql/bnf/import_csv.bnf | 6 +- docs/generated/sql/bnf/stmt_block.bnf | 242 +++-- pkg/ccl/importccl/client_import_test.go | 4 +- pkg/ccl/importccl/exportcsv_test.go | 6 +- pkg/ccl/importccl/import_planning.go | 127 +-- pkg/ccl/importccl/import_stmt_test.go | 916 ++++++++---------- pkg/ccl/importccl/import_table_creation.go | 36 - .../logic_test/multi_region_import_export | 9 +- pkg/internal/sqlsmith/bulkio.go | 10 +- pkg/sql/parser/help_test.go | 1 - pkg/sql/parser/sql.y | 10 - pkg/sql/parser/testdata/import_export | 48 - pkg/sql/parser/testdata/prepared_stmts | 20 +- pkg/sql/sem/tree/import.go | 12 - pkg/sql/sem/tree/pretty.go | 26 +- .../pretty/import3.align-deindent.golden | 37 - .../import3.align-deindent.golden.short | 10 - .../testdata/pretty/import3.align-only.golden | 37 - .../pretty/import3.align-only.golden.short | 10 - .../tree/testdata/pretty/import3.ref.golden | 30 - .../testdata/pretty/import3.ref.golden.short | 13 - pkg/sql/sem/tree/testdata/pretty/import3.sql | 1 - .../pretty/import4.align-deindent.golden | 69 -- .../import4.align-deindent.golden.short | 9 - .../testdata/pretty/import4.align-only.golden | 69 -- .../pretty/import4.align-only.golden.short | 9 - .../tree/testdata/pretty/import4.ref.golden | 43 - .../testdata/pretty/import4.ref.golden.short | 11 - pkg/sql/sem/tree/testdata/pretty/import4.sql | 1 - pkg/sql/sem/tree/walk.go | 9 - 30 files changed, 568 insertions(+), 1263 deletions(-) delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.ref.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.ref.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import3.sql delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.ref.golden delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.ref.golden.short delete mode 100644 pkg/sql/sem/tree/testdata/pretty/import4.sql diff --git a/docs/generated/sql/bnf/import_csv.bnf b/docs/generated/sql/bnf/import_csv.bnf index e424db51d855..fb73bf09a0bf 100644 --- a/docs/generated/sql/bnf/import_csv.bnf +++ b/docs/generated/sql/bnf/import_csv.bnf @@ -1,9 +1,5 @@ import_stmt ::= - 'IMPORT' 'TABLE' table_name 'CREATE' 'USING' file_location 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' 'WITH' kv_option_list - | 'IMPORT' 'TABLE' table_name 'CREATE' 'USING' file_location 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' - | 'IMPORT' 'TABLE' table_name '(' table_elem_list ')' 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' 'WITH' kv_option_list - | 'IMPORT' 'TABLE' table_name '(' table_elem_list ')' 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' - | 'IMPORT' 'INTO' table_name '(' insert_column_list ')' 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' 'WITH' kv_option_list + 'IMPORT' 'INTO' table_name '(' insert_column_list ')' 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' 'WITH' kv_option_list | 'IMPORT' 'INTO' table_name '(' insert_column_list ')' 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' | 'IMPORT' 'INTO' table_name 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' 'WITH' kv_option_list | 'IMPORT' 'INTO' table_name 'CSV' 'DATA' '(' file_location ( ( ',' file_location ) )* ')' diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 3f5edfba22f9..7471467b9030 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -163,8 +163,6 @@ explain_stmt ::= import_stmt ::= 'IMPORT' import_format string_or_placeholder opt_with_options | 'IMPORT' 'TABLE' table_name 'FROM' import_format string_or_placeholder opt_with_options - | 'IMPORT' 'TABLE' table_name 'CREATE' 'USING' string_or_placeholder import_format 'DATA' '(' string_or_placeholder_list ')' opt_with_options - | 'IMPORT' 'TABLE' table_name '(' table_elem_list ')' import_format 'DATA' '(' string_or_placeholder_list ')' opt_with_options | 'IMPORT' 'INTO' table_name '(' insert_column_list ')' import_format 'DATA' '(' string_or_placeholder_list ')' opt_with_options | 'IMPORT' 'INTO' table_name import_format 'DATA' '(' string_or_placeholder_list ')' opt_with_options @@ -534,15 +532,12 @@ opt_with_options ::= | 'WITH' 'OPTIONS' '(' kv_option_list ')' | -string_or_placeholder_list ::= - ( string_or_placeholder ) ( ( ',' string_or_placeholder ) )* - -table_elem_list ::= - ( table_elem ) ( ( ',' table_elem ) )* - insert_column_list ::= ( insert_column_item ) ( ( ',' insert_column_item ) )* +string_or_placeholder_list ::= + ( string_or_placeholder ) ( ( ',' string_or_placeholder ) )* + insert_target ::= table_name | table_name 'AS' table_alias_name @@ -1544,13 +1539,6 @@ non_reserved_word_or_sconst ::= kv_option_list ::= ( kv_option ) ( ( ',' kv_option ) )* -table_elem ::= - column_def - | index_def - | family_def - | table_constraint opt_validate_behavior - | 'LIKE' table_name like_table_option_list - insert_column_item ::= column_name @@ -2093,28 +2081,6 @@ kv_option ::= | 'SCONST' '=' string_or_placeholder | 'SCONST' -column_def ::= - column_name typename col_qual_list - -index_def ::= - 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'UNIQUE' 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - | 'INVERTED' 'INDEX' opt_name '(' index_params ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause - -family_def ::= - 'FAMILY' opt_family_name '(' name_list ')' - -table_constraint ::= - 'CONSTRAINT' constraint_name constraint_elem - | constraint_elem - -opt_validate_behavior ::= - 'NOT' 'VALID' - | - -like_table_option_list ::= - ( ) ( ( 'INCLUDING' like_table_option | 'EXCLUDING' like_table_option ) )* - column_name ::= name @@ -2484,6 +2450,9 @@ partition_by ::= storage_parameter_list ::= ( storage_parameter ) ( ( ',' storage_parameter ) )* +table_elem_list ::= + ( table_elem ) ( ( ',' table_elem ) )* + partition_by_table ::= partition_by | 'PARTITION' 'ALL' 'BY' partition_by_inner @@ -2521,25 +2490,6 @@ only_signed_fconst ::= target_name ::= unrestricted_name -col_qual_list ::= - ( ) ( ( col_qualification ) )* - -opt_family_name ::= - opt_name - -constraint_elem ::= - 'CHECK' '(' a_expr ')' - | 'UNIQUE' '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_where_clause - | 'PRIMARY' 'KEY' '(' index_params ')' opt_hash_sharded opt_interleave - | 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions - -like_table_option ::= - 'CONSTRAINTS' - | 'DEFAULTS' - | 'GENERATED' - | 'INDEXES' - | 'ALL' - scrub_option ::= 'INDEX' 'ALL' | 'INDEX' '(' name_list ')' @@ -2790,9 +2740,19 @@ storage_parameter ::= name '=' var_value | 'SCONST' '=' var_value +table_elem ::= + column_def + | index_def + | family_def + | table_constraint opt_validate_behavior + | 'LIKE' table_name like_table_option_list + create_as_col_qual_list ::= ( ) ( ( create_as_col_qualification ) )* +family_def ::= + 'FAMILY' opt_family_name '(' name_list ')' + create_as_constraint_def ::= create_as_constraint_elem @@ -2818,27 +2778,6 @@ opt_nulls_order ::= | 'NULLS' 'LAST' | -col_qualification ::= - 'CONSTRAINT' constraint_name col_qualification_elem - | col_qualification_elem - | 'COLLATE' collation_name - | 'FAMILY' family_name - | 'CREATE' 'FAMILY' family_name - | 'CREATE' 'FAMILY' - | 'CREATE' 'IF' 'NOT' 'EXISTS' 'FAMILY' family_name - -key_match ::= - 'MATCH' 'SIMPLE' - | 'MATCH' 'FULL' - | - -reference_actions ::= - reference_on_update - | reference_on_delete - | reference_on_update reference_on_delete - | reference_on_delete reference_on_update - | - group_by_list ::= ( group_by_item ) ( ( ',' group_by_item ) )* @@ -2953,6 +2892,9 @@ opt_column ::= 'COLUMN' | +column_def ::= + column_name typename col_qual_list + alter_column_default ::= 'SET' 'DEFAULT' a_expr | 'DROP' 'DEFAULT' @@ -2977,6 +2919,14 @@ opt_alter_column_using ::= 'USING' a_expr | +table_constraint ::= + 'CONSTRAINT' constraint_name constraint_elem + | constraint_elem + +opt_validate_behavior ::= + 'NOT' 'VALID' + | + audit_mode ::= 'READ' 'WRITE' | 'OFF' @@ -3068,40 +3018,24 @@ list_partitions ::= range_partitions ::= ( range_partition ) ( ( ',' range_partition ) )* +index_def ::= + 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'UNIQUE' 'INDEX' opt_index_name '(' index_params ')' opt_hash_sharded opt_storing opt_interleave opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + | 'INVERTED' 'INDEX' opt_name '(' index_params ')' opt_partition_by_index opt_with_storage_parameter_list opt_where_clause + +like_table_option_list ::= + ( ) ( ( 'INCLUDING' like_table_option | 'EXCLUDING' like_table_option ) )* + create_as_col_qualification ::= create_as_col_qualification_elem | 'FAMILY' family_name +opt_family_name ::= + opt_name + create_as_constraint_elem ::= 'PRIMARY' 'KEY' '(' create_as_params ')' -col_qualification_elem ::= - 'NOT' 'NULL' - | 'NULL' - | 'NOT' 'VISIBLE' - | 'UNIQUE' - | 'PRIMARY' 'KEY' - | 'PRIMARY' 'KEY' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' a_expr - | 'CHECK' '(' a_expr ')' - | 'DEFAULT' b_expr - | 'ON' 'UPDATE' b_expr - | 'REFERENCES' table_name opt_name_parens key_match reference_actions - | generated_as '(' a_expr ')' 'STORED' - | generated_as '(' a_expr ')' 'VIRTUAL' - | generated_always_as 'IDENTITY' '(' opt_sequence_option_list ')' - | generated_by_default_as 'IDENTITY' '(' opt_sequence_option_list ')' - | generated_always_as 'IDENTITY' - | generated_by_default_as 'IDENTITY' - -family_name ::= - name - -reference_on_update ::= - 'ON' 'UPDATE' reference_action - -reference_on_delete ::= - 'ON' 'DELETE' reference_action - group_by_item ::= a_expr @@ -3171,6 +3105,15 @@ interval_second ::= 'SECOND' | 'SECOND' '(' iconst32 ')' +col_qual_list ::= + ( ) ( ( col_qualification ) )* + +constraint_elem ::= + 'CHECK' '(' a_expr ')' + | 'UNIQUE' '(' index_params ')' opt_storing opt_interleave opt_partition_by_index opt_where_clause + | 'PRIMARY' 'KEY' '(' index_params ')' opt_hash_sharded opt_interleave + | 'FOREIGN' 'KEY' '(' name_list ')' 'REFERENCES' table_name opt_column_list key_match reference_actions + func_name ::= type_function_name | prefixed_column_path @@ -3220,32 +3163,42 @@ list_partition ::= range_partition ::= partition 'VALUES' 'FROM' '(' expr_list ')' 'TO' '(' expr_list ')' opt_partition_by +like_table_option ::= + 'CONSTRAINTS' + | 'DEFAULTS' + | 'GENERATED' + | 'INDEXES' + | 'ALL' + create_as_col_qualification_elem ::= 'PRIMARY' 'KEY' +family_name ::= + name + create_as_params ::= ( create_as_param ) ( ( ',' create_as_param ) )* -opt_name_parens ::= - '(' name ')' - | - -generated_as ::= - 'AS' - | generated_always_as - -generated_always_as ::= - 'GENERATED_ALWAYS' 'ALWAYS' 'AS' +col_qualification ::= + 'CONSTRAINT' constraint_name col_qualification_elem + | col_qualification_elem + | 'COLLATE' collation_name + | 'FAMILY' family_name + | 'CREATE' 'FAMILY' family_name + | 'CREATE' 'FAMILY' + | 'CREATE' 'IF' 'NOT' 'EXISTS' 'FAMILY' family_name -generated_by_default_as ::= - 'GENERATED_BY_DEFAULT' 'BY' 'DEFAULT' 'AS' +key_match ::= + 'MATCH' 'SIMPLE' + | 'MATCH' 'FULL' + | -reference_action ::= - 'NO' 'ACTION' - | 'RESTRICT' - | 'CASCADE' - | 'SET' 'NULL' - | 'SET' 'DEFAULT' +reference_actions ::= + reference_on_update + | reference_on_delete + | reference_on_update reference_on_delete + | reference_on_delete reference_on_update + | type_function_name ::= 'identifier' @@ -3298,6 +3251,30 @@ opt_partition_by ::= create_as_param ::= column_name +col_qualification_elem ::= + 'NOT' 'NULL' + | 'NULL' + | 'NOT' 'VISIBLE' + | 'UNIQUE' + | 'PRIMARY' 'KEY' + | 'PRIMARY' 'KEY' 'USING' 'HASH' 'WITH' 'BUCKET_COUNT' '=' a_expr + | 'CHECK' '(' a_expr ')' + | 'DEFAULT' b_expr + | 'ON' 'UPDATE' b_expr + | 'REFERENCES' table_name opt_name_parens key_match reference_actions + | generated_as '(' a_expr ')' 'STORED' + | generated_as '(' a_expr ')' 'VIRTUAL' + | generated_always_as 'IDENTITY' '(' opt_sequence_option_list ')' + | generated_by_default_as 'IDENTITY' '(' opt_sequence_option_list ')' + | generated_always_as 'IDENTITY' + | generated_by_default_as 'IDENTITY' + +reference_on_update ::= + 'ON' 'UPDATE' reference_action + +reference_on_delete ::= + 'ON' 'DELETE' reference_action + frame_extent ::= frame_bound | 'BETWEEN' frame_bound 'AND' frame_bound @@ -3328,6 +3305,27 @@ substr_from ::= substr_for ::= 'FOR' a_expr +opt_name_parens ::= + '(' name ')' + | + +generated_as ::= + 'AS' + | generated_always_as + +generated_always_as ::= + 'GENERATED_ALWAYS' 'ALWAYS' 'AS' + +generated_by_default_as ::= + 'GENERATED_BY_DEFAULT' 'BY' 'DEFAULT' 'AS' + +reference_action ::= + 'NO' 'ACTION' + | 'RESTRICT' + | 'CASCADE' + | 'SET' 'NULL' + | 'SET' 'DEFAULT' + frame_bound ::= 'UNBOUNDED' 'PRECEDING' | 'UNBOUNDED' 'FOLLOWING' diff --git a/pkg/ccl/importccl/client_import_test.go b/pkg/ccl/importccl/client_import_test.go index 6853e2182cb2..ce6faf1de0a4 100644 --- a/pkg/ccl/importccl/client_import_test.go +++ b/pkg/ccl/importccl/client_import_test.go @@ -72,10 +72,10 @@ func TestDropDatabaseCascadeDuringImportsFails(t *testing.T) { }) defer srv.Close() + runner.Exec(t, `CREATE TABLE `+dbName+"."+tableName+` (k INT, v STRING)`) importErrCh := make(chan error, 1) go func() { - _, err := db.Exec(`IMPORT TABLE `+dbName+"."+tableName+ - ` (k INT, v STRING) CSV DATA ($1)`, srv.URL) + _, err := db.Exec(`IMPORT INTO `+dbName+"."+tableName+` CSV DATA ($1)`, srv.URL) importErrCh <- err }() select { diff --git a/pkg/ccl/importccl/exportcsv_test.go b/pkg/ccl/importccl/exportcsv_test.go index 1bfdc0773f7f..c8bf7c7261ce 100644 --- a/pkg/ccl/importccl/exportcsv_test.go +++ b/pkg/ccl/importccl/exportcsv_test.go @@ -107,7 +107,8 @@ func TestExportImportBank(t *testing.T) { schema := bank.FromRows(1).Tables()[0].Schema exportedFiles := filepath.Join(exportDir, "*") - db.Exec(t, fmt.Sprintf(`IMPORT TABLE bank2 %s CSV DATA ($1) WITH delimiter = '|'%s`, schema, nullIf), exportedFiles) + db.Exec(t, fmt.Sprintf("CREATE TABLE bank2 %s", schema)) + db.Exec(t, fmt.Sprintf(`IMPORT INTO bank2 CSV DATA ($1) WITH delimiter = '|'%s`, nullIf), exportedFiles) db.CheckQueryResults(t, fmt.Sprintf(`SELECT * FROM bank AS OF SYSTEM TIME %s ORDER BY id`, asOf), db.QueryStr(t, `SELECT * FROM bank2 ORDER BY id`), @@ -155,7 +156,8 @@ func TestExportNullWithEmptyNullAs(t *testing.T) { require.Equal(t, "1,None\n2,8\n", string(contents)) // Verify successful IMPORT statement `WITH nullif="None"` to complete round trip - const importStmt = `IMPORT TABLE accounts2(id INT PRIMARY KEY, balance INT) CSV DATA ('nodelocal://0/t/export*-n*.0.csv') WITH nullif="None"` + const importStmt = `IMPORT INTO accounts2 CSV DATA ('nodelocal://0/t/export*-n*.0.csv') WITH nullif="None"` + db.Exec(t, `CREATE TABLE accounts2(id INT PRIMARY KEY, balance INT)`) db.Exec(t, importStmt) db.CheckQueryResults(t, "SELECT * FROM accounts2", db.QueryStr(t, "SELECT * FROM accounts"), diff --git a/pkg/ccl/importccl/import_planning.go b/pkg/ccl/importccl/import_planning.go index 513698acaea9..45cb4ddf6d20 100644 --- a/pkg/ccl/importccl/import_planning.go +++ b/pkg/ccl/importccl/import_planning.go @@ -211,15 +211,9 @@ func validateFormatOptions( } func importJobDescription( - p sql.PlanHookState, - orig *tree.Import, - defs tree.TableDefs, - files []string, - opts map[string]string, + p sql.PlanHookState, orig *tree.Import, files []string, opts map[string]string, ) (string, error) { stmt := *orig - stmt.CreateFile = nil - stmt.CreateDefs = defs stmt.Files = nil for _, file := range files { clean, err := cloud.SanitizeExternalStorageURI(file, nil /* extraParams */) @@ -353,14 +347,6 @@ func importPlanHook( return nil, nil, nil, false, err } - var createFileFn func() (string, error) - if !importStmt.Bundle && !importStmt.Into && importStmt.CreateDefs == nil { - createFileFn, err = p.TypeAsString(ctx, importStmt.CreateFile, "IMPORT") - if err != nil { - return nil, nil, nil, false, err - } - } - optsFn, err := p.TypeAsStringOpts(ctx, importStmt.Options, importOptionExpectValues) if err != nil { return nil, nil, nil, false, err @@ -378,8 +364,6 @@ func importPlanHook( ctx, span := tracing.ChildSpan(ctx, importStmt.StatementTag()) defer span.Finish() - walltime := p.ExecCfg().Clock.Now().WallTime - if !(p.ExtendedEvalContext().TxnImplicit || isDetached) { return errors.Errorf("IMPORT cannot be used inside a transaction without DETACHED option") } @@ -782,9 +766,8 @@ func importPlanHook( } var tableDetails []jobspb.ImportDetails_Table - var tableDescs []*tabledesc.Mutable // parallel with tableDetails var typeDetails []jobspb.ImportDetails_Type - jobDesc, err := importJobDescription(p, importStmt, nil, filenamePatterns, opts) + jobDesc, err := importJobDescription(p, importStmt, filenamePatterns, opts) if err != nil { return err } @@ -857,98 +840,24 @@ func importPlanHook( } tableDetails = []jobspb.ImportDetails_Table{{Desc: &found.TableDescriptor, IsNew: false, TargetCols: intoCols}} - } else { - seqVals := make(map[descpb.ID]int64) - - if importStmt.Bundle { - // If we target a single table, populate details with one entry of tableName. - if table != nil { - tableDetails = make([]jobspb.ImportDetails_Table, 1) - tableName := table.ObjectName.String() - // PGDUMP supports importing tables from non-public schemas, thus we - // must prepend the target table name with the target schema name. - if format.Format == roachpb.IOFileFormat_PgDump { - if table.Schema() == "" { - return errors.Newf("expected schema for target table %s to be resolved", - tableName) - } - tableName = fmt.Sprintf("%s.%s", table.SchemaName.String(), - table.ObjectName.String()) - } - tableDetails[0] = jobspb.ImportDetails_Table{ - Name: tableName, - IsNew: true, - } - } - } else { - if table == nil { - return errors.Errorf("non-bundle format %q should always have a table name", importStmt.FileFormat) - } - var create *tree.CreateTable - if importStmt.CreateDefs != nil { - create = &tree.CreateTable{ - Table: *importStmt.Table, - Defs: importStmt.CreateDefs, - } - } else { - filename, err := createFileFn() - if err != nil { - return err - } - create, err = readCreateTableFromStore(ctx, filename, - p.ExecCfg().DistSQLSrv.ExternalStorageFromURI, p.User()) - if err != nil { - return err - } - - if table.ObjectName != create.Table.ObjectName { - return errors.Errorf( - "importing table %s, but file specifies a schema for table %s", - table.ObjectName, create.Table.ObjectName, - ) + } else if importStmt.Bundle { + // If we target a single table, populate details with one entry of tableName. + if table != nil { + tableDetails = make([]jobspb.ImportDetails_Table, 1) + tableName := table.ObjectName.String() + // PGDUMP supports importing tables from non-public schemas, thus we + // must prepend the target table name with the target schema name. + if format.Format == roachpb.IOFileFormat_PgDump { + if table.Schema() == "" { + return errors.Newf("expected schema for target table %s to be resolved", + tableName) } + tableName = fmt.Sprintf("%s.%s", table.SchemaName.String(), + table.ObjectName.String()) } - if create.Locality != nil && - create.Locality.LocalityLevel == tree.LocalityLevelRow { - return unimplemented.NewWithIssueDetailf( - 61133, - "import.regional-by-row", - "IMPORT to REGIONAL BY ROW table not supported", - ) - } - // IMPORT TABLE do not support user defined types, and so we nil out the - // type resolver to protect against unexpected behavior on UDT - // resolution. - semaCtxPtr := makeSemaCtxWithoutTypeResolver(p.SemaCtx()) - tbl, err := MakeSimpleTableDescriptor( - ctx, semaCtxPtr, p.ExecCfg().Settings, create, db, sc, defaultCSVTableID, NoFKs, walltime) - if err != nil { - return err - } - descStr, err := importJobDescription(p, importStmt, create.Defs, filenamePatterns, opts) - if err != nil { - return err - } - jobDesc = descStr - - tableDescs = []*tabledesc.Mutable{tbl} - for _, tbl := range tableDescs { - // For reasons relating to #37691, we disallow user defined types in - // the standard IMPORT case. - for _, col := range tbl.Columns { - if col.Type.UserDefined() { - return errors.Newf("IMPORT cannot be used with user defined types; use IMPORT INTO instead") - } - } - } - - tableDetails = make([]jobspb.ImportDetails_Table, len(tableDescs)) - for i := range tableDescs { - tableDetails[i] = jobspb.ImportDetails_Table{ - Desc: tableDescs[i].TableDesc(), - SeqVal: seqVals[tableDescs[i].ID], - IsNew: true, - } + tableDetails[0] = jobspb.ImportDetails_Table{ + Name: tableName, + IsNew: true, } } diff --git a/pkg/ccl/importccl/import_stmt_test.go b/pkg/ccl/importccl/import_stmt_test.go index 01f3d220ca6e..b5a3934b2d45 100644 --- a/pkg/ccl/importccl/import_stmt_test.go +++ b/pkg/ccl/importccl/import_stmt_test.go @@ -1107,56 +1107,68 @@ CREATE TABLE t (a duration); err: `unsupported import format`, }, { - name: "sequences", - create: `i int8 default nextval('s')`, - typ: "CSV", - err: `"s" not found`, - }, - { - name: "statistics collection", - create: "a INT", - typ: "CSV", - data: "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" + - "10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n" + - "20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n" + - "30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n" + - "40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n" + - "50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n" + - "60\n61\n62\n63\n64\n65\n66\n67\n68\n69\n" + - "70\n71\n72\n73\n74\n75\n76\n77\n78\n79\n" + - "80\n81\n82\n83\n84\n85\n86\n87\n88\n89\n" + - "90\n91\n92\n93\n94\n95\n96\n97\n98\n99\n", + name: "statistics collection", + typ: "PGDUMP", + data: func() string { + var sb strings.Builder + _, err := fmt.Fprint(&sb, ` +CREATE TABLE t (a INT); +COPY public.t (a) FROM stdin; +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +\. +`) + require.NoError(t, err) + return sb.String() + }(), query: map[string][][]string{ "SELECT column_names, row_count, distinct_count, null_count " + "FROM [SHOW STATISTICS FOR TABLE t] " + "WHERE statistics_name = '__import__' " + "ORDER BY column_names": { - {"{a}", "100", "10", "1"}, - {"{rowid}", "100", "10", "1"}, + {"{a}", "10", "1", "0"}, + {"{rowid}", "10", "1", "0"}, }, }, }, { - name: "statistics collection multi", - create: "a INT PRIMARY KEY, b INT, INDEX (b, a)", - typ: "CSV", - data: "0,0\n1,1\n2,2\n3,3\n4,4\n5,5\n6,6\n7,7\n8,8\n9,9\n" + - "10,10\n11,11\n12,12\n13,13\n14,14\n15,15\n16,16\n17,17\n18,18\n19,19\n" + - "20,20\n21,21\n22,22\n23,23\n24,24\n25,25\n26,26\n27,27\n28,28\n29,29\n" + - "30,30\n31,31\n32,32\n33,33\n34,34\n35,35\n36,36\n37,37\n38,38\n39,39\n" + - "40,40\n41,41\n42,42\n43,43\n44,44\n45,45\n46,46\n47,47\n48,48\n49,49\n" + - "50,50\n51,51\n52,52\n53,53\n54,54\n55,55\n56,56\n57,57\n58,58\n59,59\n" + - "60,60\n61,61\n62,62\n63,63\n64,64\n65,65\n66,66\n67,67\n68,68\n69,69\n" + - "70,70\n71,71\n72,72\n73,73\n74,74\n75,75\n76,76\n77,77\n78,78\n79,79\n" + - "80,80\n81,81\n82,82\n83,83\n84,84\n85,85\n86,86\n87,87\n88,88\n89,89\n" + - "90,90\n91,91\n92,92\n93,93\n94,94\n95,95\n96,96\n97,97\n98,98\n99,99", + name: "statistics collection multi", + typ: "PGDUMP", + data: func() string { + var sb strings.Builder + _, err := fmt.Fprint(&sb, ` +CREATE TABLE t (a INT PRIMARY KEY, b INT, INDEX(b, a)); +COPY public.t (a, b) FROM stdin; +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +\. +`) + require.NoError(t, err) + return sb.String() + }(), query: map[string][][]string{ "SELECT column_names, row_count, distinct_count, null_count " + "FROM [SHOW STATISTICS FOR TABLE t] " + "WHERE statistics_name = '__import__' " + "ORDER BY column_names": { - {"{a}", "100", "10", "1"}, - {"{b}", "100", "10", "1"}, + {"{a}", "10", "1", "0"}, + {"{b}", "10", "1", "0"}, }, }, }, @@ -1214,7 +1226,8 @@ CREATE TABLE t (a duration); defer sqlDB.Exec(t, fmt.Sprintf(`DROP DATABASE %s`, dbName)) var q string if tc.create != "" { - q = fmt.Sprintf(`IMPORT TABLE t (%s) %s DATA ($1) %s`, tc.create, tc.typ, tc.with) + sqlDB.Exec(t, fmt.Sprintf(`CREATE TABLE t (%s)`, tc.create)) + q = fmt.Sprintf(`IMPORT INTO t %s DATA ($1) %s`, tc.typ, tc.with) } else { q = fmt.Sprintf(`IMPORT %s ($1) %s`, tc.typ, tc.with) } @@ -1242,7 +1255,8 @@ CREATE TABLE t (a duration); t.Run("mysqlout multiple", func(t *testing.T) { sqlDB.Exec(t, `CREATE DATABASE mysqlout; USE mysqlout`) mockRecorder.dataString = "1" - sqlDB.Exec(t, `IMPORT TABLE t (s STRING) DELIMITED DATA ($1, $1)`, srv.URL) + sqlDB.Exec(t, `CREATE TABLE t (s STRING)`) + sqlDB.Exec(t, `IMPORT INTO t DELIMITED DATA ($1, $1)`, srv.URL) sqlDB.CheckQueryResults(t, `SELECT * FROM t`, [][]string{{"1"}, {"1"}}) }) } @@ -1714,20 +1728,20 @@ func TestImportRowLimit(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - + defer sqlDB.Exec(t, `DROP TABLE IF EXISTS t`) data = test.data - importTableQuery := fmt.Sprintf(`IMPORT TABLE t (%s) %s DATA ($1) %s`, test.create, test.typ, test.with) + importIntoQuery := fmt.Sprintf(`IMPORT INTO t %s DATA ($1) %s`, test.typ, test.with) if test.err != "" { - sqlDB.ExpectErr(t, test.err, importTableQuery, srv.URL) - + sqlDB.Exec(t, fmt.Sprintf(`CREATE TABLE t (%s)`, test.create)) + sqlDB.ExpectErr(t, test.err, importIntoQuery, srv.URL) } else { if test.typ == "CSV" || test.typ == "AVRO" || test.typ == "DELIMITED" { - sqlDB.Exec(t, importTableQuery, srv.URL) + sqlDB.Exec(t, fmt.Sprintf(`CREATE TABLE t (%s)`, test.create)) + sqlDB.Exec(t, importIntoQuery, srv.URL) // Ensure that the table data is as we expect. sqlDB.CheckQueryResults(t, test.verifyQuery, test.expected) - sqlDB.Exec(t, `DROP TABLE t`) } else if test.typ == "PGDUMP" || test.typ == "MYSQLDUMP" { sqlDB.Exec(t, `DROP TABLE IF EXISTS t, u`) @@ -1844,7 +1858,8 @@ func TestImportRowLimit(t *testing.T) { defer sqlDB.Exec(t, (`DROP DATABASE test`)) data = "pear\navocado\nwatermelon\nsugar" - sqlDB.Exec(t, `IMPORT TABLE t (s STRING) CSV DATA ($1, $2) WITH row_limit='2'`, + sqlDB.Exec(t, `CREATE TABLE t (s STRING)`) + sqlDB.Exec(t, `IMPORT INTO t CSV DATA ($1, $2) WITH row_limit='2'`, srv.URL, srv.URL) sqlDB.CheckQueryResults(t, `SELECT * FROM t`, @@ -1854,6 +1869,96 @@ func TestImportRowLimit(t *testing.T) { }) } +func TestFailedImportGC(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + const nodes = 3 + + var forceFailure bool + blockGC := make(chan struct{}) + + ctx := context.Background() + baseDir := testutils.TestDataPath(t, "pgdump") + tc := testcluster.StartTestCluster(t, nodes, base.TestClusterArgs{ServerArgs: base.TestServerArgs{ + SQLMemoryPoolSize: 256 << 20, + ExternalIODir: baseDir, + Knobs: base.TestingKnobs{ + GCJob: &sql.GCJobTestingKnobs{RunBeforeResume: func(_ jobspb.JobID) error { <-blockGC; return nil }}, + }, + }}) + defer tc.Stopper().Stop(ctx) + conn := tc.Conns[0] + + for i := range tc.Servers { + tc.Servers[i].JobRegistry().(*jobs.Registry).TestingResumerCreationKnobs = map[jobspb.Type]func(raw jobs.Resumer) jobs.Resumer{ + jobspb.TypeImport: func(raw jobs.Resumer) jobs.Resumer { + r := raw.(*importResumer) + r.testingKnobs.afterImport = func(_ backupccl.RowCount) error { + if forceFailure { + return errors.New("testing injected failure") + } + return nil + } + return r + }, + } + } + + sqlDB := sqlutils.MakeSQLRunner(conn) + kvDB := tc.Server(0).DB() + + sqlDB.Exec(t, `SET CLUSTER SETTING kv.bulk_ingest.batch_size = '10KB'`) + + forceFailure = true + defer func() { forceFailure = false }() + defer gcjob.SetSmallMaxGCIntervalForTest()() + beforeImport, err := tree.MakeDTimestampTZ(tc.Server(0).Clock().Now().GoTime(), time.Millisecond) + if err != nil { + t.Fatal(err) + } + + sqlDB.Exec(t, "CREATE DATABASE failedimport; USE failedimport;") + // Hit a failure during import. + sqlDB.ExpectErr( + t, `testing injected failure`, + fmt.Sprintf(`IMPORT TABLE simple FROM PGDUMP ('%s') WITH ignore_unsupported_statements`, "nodelocal://1/simple.sql"), + ) + // Nudge the registry to quickly adopt the job. + tc.Server(0).JobRegistry().(*jobs.Registry).TestingNudgeAdoptionQueue() + + // In the case of the test, the ID of the table that will be cleaned up due + // to the failed import will be one higher than the ID of the empty database + // it was created in. + dbID := sqlutils.QueryDatabaseID(t, sqlDB.DB, "failedimport") + tableID := descpb.ID(dbID + 1) + var td catalog.TableDescriptor + if err := kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) (err error) { + td, err = catalogkv.MustGetTableDescByID(ctx, txn, keys.SystemSQLCodec, tableID) + return err + }); err != nil { + t.Fatal(err) + } + // Ensure that we have garbage written to the descriptor that we want to + // clean up. + tests.CheckKeyCount(t, kvDB, td.TableSpan(keys.SystemSQLCodec), 87) + + // Allow GC to progress. + close(blockGC) + // Ensure that a GC job was created, and wait for it to finish. + doneGCQuery := fmt.Sprintf( + "SELECT count(*) FROM [SHOW JOBS] WHERE job_type = '%s' AND status = '%s' AND created > %s", + "SCHEMA CHANGE GC", jobs.StatusSucceeded, beforeImport.String(), + ) + sqlDB.CheckQueryResultsRetry(t, doneGCQuery, [][]string{{"1"}}) + // Expect there are no more KVs for this span. + tests.CheckKeyCount(t, kvDB, td.TableSpan(keys.SystemSQLCodec), 0) +} + +// Verify that a failed import will clean up after itself. This means: +// - Delete the garbage data that it partially imported. +// - Delete the table descriptor for the table that was created during the +// import. func TestImportCSVStmt(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1867,16 +1972,12 @@ func TestImportCSVStmt(t *testing.T) { rowsPerRaceFile := 16 var forceFailure bool - blockGC := make(chan struct{}) ctx := context.Background() baseDir := testutils.TestDataPath(t, "csv") tc := testcluster.StartTestCluster(t, nodes, base.TestClusterArgs{ServerArgs: base.TestServerArgs{ SQLMemoryPoolSize: 256 << 20, ExternalIODir: baseDir, - Knobs: base.TestingKnobs{ - GCJob: &sql.GCJobTestingKnobs{RunBeforeResume: func(_ jobspb.JobID) error { <-blockGC; return nil }}, - }, }}) defer tc.Stopper().Stop(ctx) conn := tc.Conns[0] @@ -1897,7 +1998,6 @@ func TestImportCSVStmt(t *testing.T) { } sqlDB := sqlutils.MakeSQLRunner(conn) - kvDB := tc.Server(0).DB() sqlDB.Exec(t, `SET CLUSTER SETTING kv.bulk_ingest.batch_size = '10KB'`) @@ -1909,139 +2009,112 @@ func TestImportCSVStmt(t *testing.T) { rowsPerFile = rowsPerRaceFile } - // Table schema used in IMPORT TABLE tests. - schema := []interface{}{"nodelocal://0/table"} empty := []string{"'nodelocal://0/empty.csv'"} - emptySchema := []interface{}{"nodelocal://0/empty.schema"} // Support subtests by keeping track of the number of jobs that are executed. testNum := -1 expectedRows := numFiles * rowsPerFile for i, tc := range []struct { - name string - query string // must have one `%s` for the files list. - args []interface{} // will have backupPath appended - files []string - jobOpts string - err string + name string + createQuery string + query string // must have one `%s` for the files list. + files []string + jobOpts string + err string }{ { - "schema-in-file", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - schema, - testFiles.files, - ``, - "", - }, - { - "schema-in-file-intodb", - `IMPORT TABLE csv1.t CREATE USING $1 CSV DATA (%s)`, - schema, - testFiles.files, - ``, - "", - }, - { - "schema-in-query", - `IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING, INDEX (b), INDEX (a, b)) CSV DATA (%s)`, - nil, - testFiles.files, - ``, - "", - }, - { - "schema-in-query-opts", - `IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING, INDEX (b), INDEX (a, b)) CSV DATA (%s) WITH delimiter = '|', comment = '#', nullif='', skip = '2'`, - nil, + "query-opts", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH delimiter = '|', comment = '#', nullif='', skip = '2'`, testFiles.filesWithOpts, ` WITH comment = '#', delimiter = '|', "nullif" = '', skip = '2'`, "", }, { // Force some SST splits. - "schema-in-file-sstsize", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH sstsize = '10K'`, - schema, + "file-sstsize", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH sstsize = '10K'`, testFiles.files, ` WITH sstsize = '10K'`, "", }, { "empty-file", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - schema, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s)`, empty, ``, "", }, { "empty-with-files", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - schema, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s)`, append(empty, testFiles.files...), ``, "", }, { - "schema-in-file-auto-decompress", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'auto'`, - schema, + "auto-decompress", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'auto'`, testFiles.files, ` WITH decompress = 'auto'`, "", }, { - "schema-in-file-no-decompress", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'none'`, - schema, + "no-decompress", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'none'`, testFiles.files, ` WITH decompress = 'none'`, "", }, { - "schema-in-file-explicit-gzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'gzip'`, - schema, + "explicit-gzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'gzip'`, testFiles.gzipFiles, ` WITH decompress = 'gzip'`, "", }, { - "schema-in-file-auto-gzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'auto'`, - schema, + "auto-gzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'auto'`, testFiles.bzipFiles, ` WITH decompress = 'auto'`, "", }, { - "schema-in-file-implicit-gzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - schema, + "implicit-gzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s)`, testFiles.gzipFiles, ``, "", }, { - "schema-in-file-explicit-bzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'bzip'`, - schema, + "explicit-bzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'bzip'`, testFiles.bzipFiles, ` WITH decompress = 'bzip'`, "", }, { - "schema-in-file-auto-bzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'auto'`, - schema, + "auto-bzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'auto'`, testFiles.bzipFiles, ` WITH decompress = 'auto'`, "", }, { - "schema-in-file-implicit-bzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - schema, + "implicit-bzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s)`, testFiles.bzipFiles, ``, "", @@ -2049,40 +2122,40 @@ func TestImportCSVStmt(t *testing.T) { // NB: successes above, failures below, because we check the i-th job. { "bad-opt-name", - `IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING, INDEX (b), INDEX (a, b)) CSV DATA (%s) WITH foo = 'bar'`, - nil, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH foo = 'bar'`, testFiles.files, ``, "invalid option \"foo\"", }, { "primary-key-dup", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - schema, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s)`, testFiles.filesWithDups, ``, "duplicate key in primary index", }, { "no-database", - `IMPORT TABLE nonexistent.t CREATE USING $1 CSV DATA (%s)`, - schema, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO nonexistent.t CSV DATA (%s)`, testFiles.files, ``, `database does not exist: "nonexistent.t"`, }, { "into-db-fails", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH into_db = 'test'`, - schema, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH into_db = 'test'`, testFiles.files, ``, `invalid option "into_db"`, }, { - "schema-in-file-no-decompress-gzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'none'`, - schema, + "no-decompress-gzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'none'`, testFiles.gzipFiles, ` WITH decompress = 'none'`, // This returns different errors for `make test` and `make testrace` but @@ -2090,29 +2163,21 @@ func TestImportCSVStmt(t *testing.T) { `field`, }, { - "schema-in-file-decompress-gzip", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH decompress = 'gzip'`, - schema, + "decompress-gzip", + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH decompress = 'gzip'`, testFiles.files, ` WITH decompress = 'gzip'`, "gzip: invalid header", }, { "csv-with-invalid-delimited-option", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s) WITH fields_delimited_by = '|'`, - schema, + `CREATE TABLE t (a int8 primary key, b string, index (b), index (a, b))`, + `IMPORT INTO t CSV DATA (%s) WITH fields_delimited_by = '|'`, testFiles.files, ``, "invalid option", }, - { - "empty-schema-in-file", - `IMPORT TABLE t CREATE USING $1 CSV DATA (%s)`, - emptySchema, - testFiles.files, - ``, - "expected 1 create table statement", - }, } { t.Run(tc.name, func(t *testing.T) { if strings.Contains(tc.name, "bzip") && len(testFiles.bzipFiles) == 0 { @@ -2127,18 +2192,22 @@ func TestImportCSVStmt(t *testing.T) { rows, idx, bytes int } + if tc.createQuery != "" { + sqlDB.Exec(t, tc.createQuery) + } + var result int query := fmt.Sprintf(tc.query, strings.Join(tc.files, ", ")) testNum++ if tc.err != "" { - sqlDB.ExpectErr(t, tc.err, query, tc.args...) + sqlDB.ExpectErr(t, tc.err, query) return } - sqlDB.QueryRow(t, query, tc.args...).Scan( + sqlDB.QueryRow(t, query).Scan( &unused, &unused, &unused, &restored.rows, &restored.idx, &restored.bytes, ) - jobPrefix := fmt.Sprintf(`IMPORT TABLE %s.public.t (a INT8 PRIMARY KEY, b STRING, INDEX (b), INDEX (a, b))`, intodb) + jobPrefix := fmt.Sprintf(`IMPORT INTO %s.public.t`, intodb) var intodbID descpb.ID sqlDB.QueryRow(t, fmt.Sprintf(`SELECT id FROM system.namespace WHERE name = '%s'`, @@ -2206,107 +2275,43 @@ func TestImportCSVStmt(t *testing.T) { // Verify unique_rowid is replaced for tables without primary keys. t.Run("unique_rowid", func(t *testing.T) { sqlDB.Exec(t, "CREATE DATABASE pk") - sqlDB.Exec(t, fmt.Sprintf(`IMPORT TABLE pk.t (a INT8, b STRING) CSV DATA (%s)`, strings.Join(testFiles.files, ", "))) + sqlDB.Exec(t, `CREATE TABLE pk.t (a INT8, b STRING)`) + sqlDB.Exec(t, fmt.Sprintf(`IMPORT INTO pk.t CSV DATA (%s)`, strings.Join(testFiles.files, ", "))) // Verify the rowids are being generated as expected. sqlDB.CheckQueryResults(t, `SELECT count(*) FROM pk.t`, sqlDB.QueryStr(t, ` - SELECT count(*) FROM - (SELECT * FROM - (SELECT generate_series(0, $1 - 1) file), - (SELECT generate_series(1, $2) rownum) - ) - `, numFiles, rowsPerFile), + SELECT count(*) FROM + (SELECT * FROM + (SELECT generate_series(0, $1 - 1) file), + (SELECT generate_series(1, $2) rownum) + ) + `, numFiles, rowsPerFile), ) }) // Verify a failed IMPORT won't prevent a second IMPORT. t.Run("checkpoint-leftover", func(t *testing.T) { sqlDB.Exec(t, "CREATE DATABASE checkpoint; USE checkpoint") + sqlDB.Exec(t, `CREATE TABLE t (a INT8 PRIMARY KEY, b STRING)`) // Specify wrong number of columns. sqlDB.ExpectErr( - t, "expected 1 fields, got 2", - fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY) CSV DATA (%s)`, testFiles.files[0]), - ) - - // Specify wrong table name; still shouldn't leave behind a checkpoint file. - sqlDB.ExpectErr( - t, `file specifies a schema for table t`, - fmt.Sprintf(`IMPORT TABLE bad CREATE USING $1 CSV DATA (%s)`, testFiles.files[0]), schema[0], + t, "error parsing row 1: expected 1 fields, got 2", + fmt.Sprintf(`IMPORT INTO t (a) CSV DATA (%s)`, testFiles.files[0]), ) // Expect it to succeed with correct columns. - sqlDB.Exec(t, fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`, testFiles.files[0])) + sqlDB.Exec(t, fmt.Sprintf(`IMPORT INTO t CSV DATA (%s)`, testFiles.files[0])) // A second attempt should fail fast. A "slow fail" is the error message // "restoring table desc and namespace entries: table already exists". sqlDB.ExpectErr( - t, `relation "t" already exists`, - fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`, testFiles.files[0]), + t, `ingested key collides with an existing one`, + fmt.Sprintf(`IMPORT INTO t CSV DATA (%s)`, testFiles.files[0]), ) }) - // Verify that a failed import will clean up after itself. This means: - // - Delete the garbage data that it partially imported. - // - Delete the table descriptor for the table that was created during the - // import. - t.Run("failed-import-gc", func(t *testing.T) { - forceFailure = true - defer func() { forceFailure = false }() - defer gcjob.SetSmallMaxGCIntervalForTest()() - beforeImport, err := tree.MakeDTimestampTZ(tc.Server(0).Clock().Now().GoTime(), time.Millisecond) - if err != nil { - t.Fatal(err) - } - - sqlDB.Exec(t, "CREATE DATABASE failedimport; USE failedimport;") - // Hit a failure during import. - sqlDB.ExpectErr( - t, `testing injected failure`, - fmt.Sprintf(`IMPORT TABLE t (a INT PRIMARY KEY, b STRING) CSV DATA (%s)`, testFiles.files[1]), - ) - // Nudge the registry to quickly adopt the job. - tc.Server(0).JobRegistry().(*jobs.Registry).TestingNudgeAdoptionQueue() - - // In the case of the test, the ID of the table that will be cleaned up due - // to the failed import will be one higher than the ID of the empty database - // it was created in. - dbID := sqlutils.QueryDatabaseID(t, sqlDB.DB, "failedimport") - tableID := descpb.ID(dbID + 1) - var td catalog.TableDescriptor - if err := kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) (err error) { - td, err = catalogkv.MustGetTableDescByID(ctx, txn, keys.SystemSQLCodec, tableID) - return err - }); err != nil { - t.Fatal(err) - } - // Ensure that we have garbage written to the descriptor that we want to - // clean up. - tests.CheckKeyCount(t, kvDB, td.TableSpan(keys.SystemSQLCodec), rowsPerFile) - - // Allow GC to progress. - close(blockGC) - // Ensure that a GC job was created, and wait for it to finish. - doneGCQuery := fmt.Sprintf( - "SELECT count(*) FROM [SHOW JOBS] WHERE job_type = '%s' AND status = '%s' AND created > %s", - "SCHEMA CHANGE GC", jobs.StatusSucceeded, beforeImport.String(), - ) - sqlDB.CheckQueryResultsRetry(t, doneGCQuery, [][]string{{"1"}}) - // Expect there are no more KVs for this span. - tests.CheckKeyCount(t, kvDB, td.TableSpan(keys.SystemSQLCodec), 0) - // Expect that the table descriptor is deleted. - if err := kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { - _, err := catalogkv.MustGetTableDescByID(ctx, txn, keys.SystemSQLCodec, tableID) - if !testutils.IsError(err, "descriptor not found") { - return err - } - return nil - }); err != nil { - t.Fatal(err) - } - }) - // Test basic role based access control. Users who have the admin role should // be able to IMPORT. t.Run("RBAC-SuperUser", func(t *testing.T) { @@ -2323,22 +2328,13 @@ func TestImportCSVStmt(t *testing.T) { } defer testuser.Close() - t.Run("IMPORT TABLE", func(t *testing.T) { - if _, err := testuser.Exec(fmt.Sprintf(`IMPORT TABLE rbac_superuser (a INT8 PRIMARY KEY, -b STRING) CSV DATA (%s)`, testFiles.files[0])); err != nil { - t.Fatal(err) - } - }) - - t.Run("IMPORT INTO", func(t *testing.T) { - if _, err := testuser.Exec("CREATE TABLE rbac_into_superuser (a INT8 PRIMARY KEY, " + - "b STRING)"); err != nil { - t.Fatal(err) - } - if _, err := testuser.Exec(fmt.Sprintf(`IMPORT INTO rbac_into_superuser (a, b) CSV DATA (%s)`, testFiles.files[0])); err != nil { - t.Fatal(err) - } - }) + if _, err := testuser.Exec("CREATE TABLE rbac_into_superuser (a INT8 PRIMARY KEY, " + + "b STRING)"); err != nil { + t.Fatal(err) + } + if _, err := testuser.Exec(fmt.Sprintf(`IMPORT INTO rbac_into_superuser (a, b) CSV DATA (%s)`, testFiles.files[0])); err != nil { + t.Fatal(err) + } }) // Verify DEFAULT columns and SERIAL are allowed but not evaluated. @@ -2355,18 +2351,21 @@ b STRING) CSV DATA (%s)`, testFiles.files[0])); err != nil { sqlDB.Exec(t, `SET DATABASE = d`) const ( - query = `IMPORT TABLE t ( - a SERIAL8, - b INT8 DEFAULT unique_rowid(), - c STRING DEFAULT 's', - d SERIAL8, - e INT8 DEFAULT unique_rowid(), - f STRING DEFAULT 's', - PRIMARY KEY (a, b, c) - ) CSV DATA ($1)` + create = `CREATE TABLE t ( + a SERIAL8, + b INT8 DEFAULT unique_rowid(), + c STRING DEFAULT 's', + d SERIAL8, + e INT8 DEFAULT unique_rowid(), + f STRING DEFAULT 's', + PRIMARY KEY (a, b, c) + )` + query = `IMPORT INTO t CSV DATA ($1)` nullif = ` WITH nullif=''` ) + sqlDB.Exec(t, create) + data = ",5,e,7,," t.Run(data, func(t *testing.T) { sqlDB.ExpectErr( @@ -2421,8 +2420,8 @@ b STRING) CSV DATA (%s)`, testFiles.files[0])); err != nil { data := []byte("1,2") require.NoError(t, cloud.WriteFile(ctx, userfileStorage, "", bytes.NewReader(data))) - sqlDB.Exec(t, fmt.Sprintf("IMPORT TABLE foo (id INT PRIMARY KEY, "+ - "id2 INT) CSV DATA ('%s')", userfileURI)) + sqlDB.Exec(t, `CREATE TABLE foo (id INT PRIMARY KEY, id2 INT)`) + sqlDB.Exec(t, fmt.Sprintf("IMPORT INTO foo CSV DATA ('%s')", userfileURI)) sqlDB.CheckQueryResults(t, "SELECT * FROM foo", sqlDB.QueryStr(t, "SELECT 1, 2")) require.NoError(t, userfileStorage.Delete(ctx, "")) @@ -2437,8 +2436,8 @@ b STRING) CSV DATA (%s)`, testFiles.files[0])); err != nil { data := []byte("1,2") require.NoError(t, cloud.WriteFile(ctx, userfileStorage, "", bytes.NewReader(data))) - sqlDB.Exec(t, fmt.Sprintf("IMPORT TABLE baz (id INT PRIMARY KEY, "+ - "id2 INT) CSV DATA ('%s')", userfileURI)) + sqlDB.Exec(t, `CREATE TABLE baz (id INT PRIMARY KEY, id2 INT)`) + sqlDB.Exec(t, fmt.Sprintf("IMPORT INTO baz CSV DATA ('%s')", userfileURI)) sqlDB.CheckQueryResults(t, "SELECT * FROM baz", sqlDB.QueryStr(t, "SELECT 1, 2")) require.NoError(t, userfileStorage.Delete(ctx, "")) @@ -2448,9 +2447,10 @@ b STRING) CSV DATA (%s)`, testFiles.files[0])); err != nil { sqlDB.Exec(t, `USE defaultdb`) sqlDB.Exec(t, `CREATE USER foo`) sqlDB.Exec(t, `GRANT ALL ON DATABASE defaultdb TO foo`) + sqlDB.Exec(t, `CREATE TABLE import_with_db_privs (a INT8 PRIMARY KEY, b STRING)`) sqlDB.Exec(t, fmt.Sprintf(` - IMPORT TABLE import_with_db_privs (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`, + IMPORT INTO import_with_db_privs CSV DATA (%s)`, testFiles.files[0])) // Verify correct number of rows via COUNT. @@ -2492,20 +2492,30 @@ func TestImportFeatureFlag(t *testing.T) { defer tc.Stopper().Stop(ctx) sqlDB := sqlutils.MakeSQLRunner(tc.Conns[0]) + data := ` +CREATE TABLE t (id INT); +INSERT INTO foo VALUES (1); +` + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + _, _ = w.Write([]byte(data)) + } + })) + defer srv.Close() + testFiles := makeCSVData(t, numFiles, rowsPerFile, nodes, rowsPerRaceFile) // Feature flag is off — test that IMPORT and IMPORT INTO surface error. sqlDB.Exec(t, `SET CLUSTER SETTING feature.import.enabled = FALSE`) sqlDB.ExpectErr(t, `feature IMPORT was disabled by the database administrator`, - fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`, testFiles.files[0])) + fmt.Sprintf(`IMPORT TABLE t FROM PGDUMP '%s'`, srv.URL)) sqlDB.Exec(t, `CREATE TABLE feature_flags (a INT8 PRIMARY KEY, b STRING)`) sqlDB.ExpectErr(t, `feature IMPORT was disabled by the database administrator`, fmt.Sprintf(`IMPORT INTO feature_flags (a, b) CSV DATA (%s)`, testFiles.files[0])) // Feature flag is on — test that IMPORT and IMPORT INTO do not error. sqlDB.Exec(t, `SET CLUSTER SETTING feature.import.enabled = TRUE`) - sqlDB.Exec(t, fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`, - testFiles.files[0])) + sqlDB.Exec(t, fmt.Sprintf(`IMPORT TABLE t FROM PGDUMP '%s'`, srv.URL)) sqlDB.Exec(t, fmt.Sprintf(`IMPORT INTO feature_flags (a, b) CSV DATA (%s)`, testFiles.files[0])) } @@ -2515,7 +2525,9 @@ func TestImportObjectLevelRBAC(t *testing.T) { const nodes = 3 ctx := context.Background() + baseDir := filepath.Join("testdata", "pgdump") tc := testcluster.StartTestCluster(t, nodes, base.TestClusterArgs{ServerArgs: base.TestServerArgs{ + ExternalIODir: baseDir, SQLMemoryPoolSize: 256 << 20, }}) defer tc.Stopper().Stop(ctx) @@ -2539,23 +2551,22 @@ func TestImportObjectLevelRBAC(t *testing.T) { filename := "path/to/file" dest := userfile.MakeUserFileStorageURI(qualifiedTableName, filename) - writeToUserfile := func(filename string) { + writeToUserfile := func(filename, data string) { // Write to userfile storage now that testuser has CREATE privileges. ie := tc.Server(0).InternalExecutor().(*sql.InternalExecutor) fileTableSystem1, err := cloud.ExternalStorageFromURI(ctx, dest, base.ExternalIODirConfig{}, cluster.NoSettings, blobs.TestEmptyBlobClientFactory, security.TestUserName(), ie, tc.Server(0).DB()) require.NoError(t, err) - require.NoError(t, cloud.WriteFile(ctx, fileTableSystem1, filename, bytes.NewReader([]byte("1,aaa")))) + require.NoError(t, cloud.WriteFile(ctx, fileTableSystem1, filename, bytes.NewReader([]byte(data)))) } t.Run("import-RBAC", func(t *testing.T) { - userfileDest := dest + "/" + t.Name() + userFileDest := dest + "/" + t.Name() testuser := startTestUser() // User has no privileges at this point. Check that an IMPORT requires // CREATE privileges on the database. - _, err := testuser.Exec(fmt.Sprintf(`IMPORT TABLE rbac_import_priv (a INT8 PRIMARY KEY, -b STRING) CSV DATA ('%s')`, userfileDest)) + _, err := testuser.Exec(fmt.Sprintf(`IMPORT TABLE simple FROM PGDUMP '%s'`, userFileDest)) require.True(t, testutils.IsError(err, "testuser does not have CREATE privilege on database")) // Grant user CREATE privilege on the database. @@ -2569,11 +2580,12 @@ b STRING) CSV DATA ('%s')`, userfileDest)) defer testuser.Close() // Write to userfile now that the user has CREATE privileges. - writeToUserfile(t.Name()) + writeToUserfile(t.Name(), ` +CREATE TABLE simple (id INT); +`) // Import should now have the required privileges to start the job. - _, err = testuser.Exec(fmt.Sprintf(`IMPORT TABLE rbac_import_priv (a INT8 PRIMARY KEY, -b STRING) CSV DATA ('%s')`, userfileDest)) + _, err = testuser.Exec(fmt.Sprintf(`IMPORT TABLE simple FROM PGDUMP '%s'`, userFileDest)) require.NoError(t, err) }) @@ -2605,7 +2617,7 @@ b) CSV DATA ('%s')`, userFileDest)) defer testuser.Close() // Write to userfile now that the user has CREATE privileges. - writeToUserfile(t.Name()) + writeToUserfile(t.Name(), "1,aaa") // Import should now have the required privileges to start the job. _, err := testuser.Exec(fmt.Sprintf(`IMPORT INTO rbac_import_into_priv (a,b) CSV DATA ('%s')`, @@ -2638,6 +2650,7 @@ func TestURIRequiresAdminRole(t *testing.T) { testuser, err := gosql.Open("postgres", pgURL.String()) require.NoError(t, err) defer testuser.Close() + rootDB.Exec(t, `CREATE TABLE foo (id INT)`) for _, tc := range []struct { name string @@ -2691,7 +2704,7 @@ func TestURIRequiresAdminRole(t *testing.T) { }, } { t.Run(tc.name+"-via-import", func(t *testing.T) { - _, err := testuser.Exec(fmt.Sprintf(`IMPORT TABLE foo (id INT) CSV DATA ('%s')`, tc.uri)) + _, err := testuser.Exec(fmt.Sprintf(`IMPORT INTO foo CSV DATA ('%s')`, tc.uri)) if tc.requiresAdmin { require.True(t, testutils.IsError(err, "only users with the admin role are allowed to IMPORT")) } else { @@ -2732,19 +2745,22 @@ func TestExportImportRoundTrip(t *testing.T) { // with a unique directory name per run. { stmts: `EXPORT INTO CSV 'nodelocal://0/%[1]s' FROM SELECT ARRAY['a', 'b', 'c']; - IMPORT TABLE t (x TEXT[]) CSV DATA ('nodelocal://0/%[1]s/export*-n*.0.csv')`, + CREATE TABLE t (x TEXT[]); + IMPORT INTO t CSV DATA ('nodelocal://0/%[1]s/export*-n*.0.csv')`, tbl: "t", expected: `SELECT ARRAY['a', 'b', 'c']`, }, { stmts: `EXPORT INTO CSV 'nodelocal://0/%[1]s' FROM SELECT ARRAY[b'abc', b'\141\142\143', b'\x61\x62\x63']; - IMPORT TABLE t (x BYTES[]) CSV DATA ('nodelocal://0/%[1]s/export*-n*.0.csv')`, + CREATE TABLE t (x BYTES[]); + IMPORT INTO t CSV DATA ('nodelocal://0/%[1]s/export*-n*.0.csv')`, tbl: "t", expected: `SELECT ARRAY[b'abc', b'\141\142\143', b'\x61\x62\x63']`, }, { stmts: `EXPORT INTO CSV 'nodelocal://0/%[1]s' FROM SELECT 'dog' COLLATE en; - IMPORT TABLE t (x STRING COLLATE en) CSV DATA ('nodelocal://0/%[1]s/export*-n*.0.csv')`, + CREATE TABLE t (x STRING COLLATE en); + IMPORT INTO t CSV DATA ('nodelocal://0/%[1]s/export*-n*.0.csv')`, tbl: "t", expected: `SELECT 'dog' COLLATE en`, }, @@ -3628,10 +3644,10 @@ func benchUserUpload(b *testing.B, uploadBaseURI string) { b.SetBytes(numBytes) b.ResetTimer() + sqlDB.Exec(b, `CREATE TABLE t (a INT8 PRIMARY KEY, b STRING, INDEX (b), INDEX (a, b))`) sqlDB.Exec(b, fmt.Sprintf( - `IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING, INDEX (b), INDEX (a, b)) - CSV DATA ('%s%s')`, + `IMPORT INTO t CSV DATA ('%s%s')`, uploadBaseURI, testFileBase, )) } @@ -4621,54 +4637,6 @@ INSERT INTO users (a, b) VALUES (1, 2), (3, 4); format: "AVRO", expectedResults: [][]string{{"1", "2"}, {"3", "4"}}, }, - { - into: false, - name: "import-table-csv", - data: "35,23\n67,10", - create: "a INT, c INT AS (a + b) STORED, b INT", - format: "CSV", - expectedError: "to use computed columns, use IMPORT INTO", - }, - { - into: false, - name: "import-table-csv-virtual", - data: "35,23\n67,10", - create: "a INT, c INT AS (a + b) VIRTUAL, b INT", - format: "CSV", - expectedError: "to import into a table with virtual computed columns, use IMPORT INTO", - }, - { - into: false, - name: "import-table-csv-expression-index", - data: "35,23\n67,10", - create: "a INT, b INT, INDEX ((a + b))", - format: "CSV", - expectedError: "to import into a table with expression indexes, use IMPORT INTO", - }, - { - into: false, - name: "import-table-avro", - data: avroData, - create: "a INT, c INT AS (a + b) STORED, b INT", - format: "AVRO", - expectedResults: [][]string{{"1", "3", "2"}, {"3", "7", "4"}}, - }, - { - into: false, - name: "import-table-avro-virtual", - data: avroData, - create: "a INT, c INT AS (a + b) VIRTUAL, b INT", - format: "AVRO", - expectedError: "to import into a table with virtual computed columns, use IMPORT INTO", - }, - { - into: false, - name: "import-table-avro-expression-index", - data: avroData, - create: "a INT, b INT, INDEX ((a + b))", - format: "AVRO", - expectedError: "to import into a table with expression indexes, use IMPORT INTO", - }, { into: false, name: "pgdump", @@ -4687,12 +4655,7 @@ INSERT INTO users (a, b) VALUES (1, 2), (3, 4); importStmt = fmt.Sprintf(`IMPORT INTO users (%s) %s DATA (%q)`, test.targetCols, test.format, srv.URL) } else { - if test.format == "CSV" || test.format == "AVRO" { - importStmt = fmt.Sprintf( - `IMPORT TABLE users (%s) %s DATA (%q)`, test.create, test.format, srv.URL) - } else { - importStmt = fmt.Sprintf(`IMPORT %s (%q)`, test.format, srv.URL) - } + importStmt = fmt.Sprintf(`IMPORT %s (%q)`, test.format, srv.URL) } if test.expectedError != "" { sqlDB.ExpectErr(t, test.expectedError, importStmt) @@ -5076,7 +5039,8 @@ func TestImportWorkerFailure(t *testing.T) { urls[i] = fmt.Sprintf("'%s/%d'", srv.URL, i) } csvURLs := strings.Join(urls, ", ") - query := fmt.Sprintf(`IMPORT TABLE t (i INT8 PRIMARY KEY) CSV DATA (%s) WITH sstsize = '1B'`, csvURLs) + sqlDB.Exec(t, `CREATE TABLE t (i INT8 PRIMARY KEY)`) + query := fmt.Sprintf(`IMPORT INTO t CSV DATA (%s) WITH sstsize = '1B'`, csvURLs) errCh := make(chan error) go func() { @@ -5130,12 +5094,8 @@ func TestImportMVCCChecksums(t *testing.T) { })) defer srv.Close() - sqlDB.Exec(t, `IMPORT TABLE d.t ( - a INT8 PRIMARY KEY, - b INT8, - c INT8, - INDEX (b) STORING (c) - ) CSV DATA ($1)`, srv.URL) + sqlDB.Exec(t, `CREATE TABLE d.t (a INT8 PRIMARY KEY, b INT8, c INT8, INDEX (b) STORING (c))`) + sqlDB.Exec(t, `IMPORT INTO d.t CSV DATA ($1)`, srv.URL) sqlDB.Exec(t, `UPDATE d.t SET c = 2 WHERE a = 1`) } @@ -5171,7 +5131,6 @@ func TestImportMysql(t *testing.T) { query string args []interface{} }{ - {`read data only`, expectSimple, `IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea) MYSQLDUMP DATA ($1)`, simple}, {`single table dump`, expectSimple, `IMPORT TABLE simple FROM MYSQLDUMP ($1)`, simple}, {`second table dump`, expectSecond, `IMPORT TABLE second FROM MYSQLDUMP ($1) WITH skip_foreign_keys`, second}, {`simple from multi`, expectSimple, `IMPORT TABLE simple FROM MYSQLDUMP ($1)`, multitable}, @@ -5315,14 +5274,24 @@ func TestImportDelimited(t *testing.T) { sqlDB.Exec(t, `CREATE DATABASE foo; SET DATABASE = foo`) testRows, configs := getMysqlOutfileTestdata(t) + checkQueryResults := func(validationQuery string) { + for idx, row := range sqlDB.QueryStr(t, validationQuery) { + expected, actual := testRows[idx].s, row[1] + if expected == injectNull { + expected = "NULL" + } + + if expected != actual { + t.Fatalf("expected row i=%s string to be %q, got %q", row[0], expected, actual) + } + } + } for i, cfg := range configs { t.Run(cfg.name, func(t *testing.T) { var opts []interface{} - cmd := fmt.Sprintf(`IMPORT TABLE test%d (i INT8 PRIMARY KEY, s text, b bytea) DELIMITED DATA ($1)`, i) opts = append(opts, fmt.Sprintf("nodelocal://0%s", strings.TrimPrefix(cfg.filename, baseDir))) - var flags []string if cfg.opts.RowSeparator != '\n' { opts = append(opts, string(cfg.opts.RowSeparator)) @@ -5340,20 +5309,6 @@ func TestImportDelimited(t *testing.T) { opts = append(opts, string(cfg.opts.Escape)) flags = append(flags, fmt.Sprintf("fields_escaped_by = $%d", len(opts))) } - if len(flags) > 0 { - cmd += " WITH " + strings.Join(flags, ", ") - } - sqlDB.Exec(t, cmd, opts...) - for idx, row := range sqlDB.QueryStr(t, fmt.Sprintf("SELECT * FROM test%d ORDER BY i", i)) { - expected, actual := testRows[idx].s, row[1] - if expected == injectNull { - expected = "NULL" - } - - if expected != actual { - t.Fatalf("expected row i=%s string to be %q, got %q", row[0], expected, actual) - } - } // Test if IMPORT INTO works here by testing that they produce the same // results as IMPORT TABLE. t.Run("import-into", func(t *testing.T) { @@ -5364,9 +5319,7 @@ func TestImportDelimited(t *testing.T) { intoCmd += " WITH " + strings.Join(flags, ", ") } sqlDB.Exec(t, intoCmd, opts...) - importStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT * FROM test%d ORDER BY i", i)) - intoStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT * FROM into%d ORDER BY i", i)) - require.Equal(t, importStr, intoStr) + checkQueryResults(fmt.Sprintf(`SELECT i, s, b FROM into%d ORDER BY i`, i)) }) t.Run("import-into-target-cols-reordered", func(t *testing.T) { defer sqlDB.Exec(t, fmt.Sprintf(`DROP TABLE into%d`, i)) @@ -5376,12 +5329,7 @@ func TestImportDelimited(t *testing.T) { intoCmd += " WITH " + strings.Join(flags, ", ") } sqlDB.Exec(t, intoCmd, opts...) - colNames := []string{"i", "s", "b"} - for _, colName := range colNames { - importStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT (%s) FROM test%d ORDER BY i", colName, i)) - intoStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT (%s) FROM into%d ORDER BY i", colName, i)) - require.Equal(t, importStr, intoStr) - } + checkQueryResults(fmt.Sprintf(`SELECT i, s, b FROM into%d ORDER BY i`, i)) }) }) } @@ -5407,11 +5355,35 @@ func TestImportPgCopy(t *testing.T) { testRows, configs := getPgCopyTestdata(t) + checkQueryResults := func(validationQuery string) { + for idx, row := range sqlDB.QueryStr(t, validationQuery) { + { + expected, actual := testRows[idx].s, row[1] + if expected == injectNull { + expected = "NULL" + } + + if expected != actual { + t.Fatalf("expected row i=%s string to be %q, got %q", row[0], expected, actual) + } + } + + { + expected, actual := testRows[idx].b, row[2] + if expected == nil { + expected = []byte("NULL") + } + if !bytes.Equal(expected, []byte(actual)) { + t.Fatalf("expected rowi=%s bytes to be %q, got %q", row[0], expected, actual) + } + } + } + } + for i, cfg := range configs { t.Run(cfg.name, func(t *testing.T) { var opts []interface{} - cmd := fmt.Sprintf(`IMPORT TABLE test%d (i INT8 PRIMARY KEY, s text, b bytea) PGCOPY DATA ($1)`, i) opts = append(opts, fmt.Sprintf("nodelocal://0%s", strings.TrimPrefix(cfg.filename, baseDir))) var flags []string @@ -5423,33 +5395,6 @@ func TestImportPgCopy(t *testing.T) { opts = append(opts, cfg.opts.Null) flags = append(flags, fmt.Sprintf("nullif = $%d", len(opts))) } - if len(flags) > 0 { - cmd += " WITH " + strings.Join(flags, ", ") - } - t.Log(cmd, opts) - sqlDB.Exec(t, cmd, opts...) - for idx, row := range sqlDB.QueryStr(t, fmt.Sprintf("SELECT * FROM test%d ORDER BY i", i)) { - { - expected, actual := testRows[idx].s, row[1] - if expected == injectNull { - expected = "NULL" - } - - if expected != actual { - t.Fatalf("expected row i=%s string to be %q, got %q", row[0], expected, actual) - } - } - - { - expected, actual := testRows[idx].b, row[2] - if expected == nil { - expected = []byte("NULL") - } - if !bytes.Equal(expected, []byte(actual)) { - t.Fatalf("expected rowi=%s bytes to be %q, got %q", row[0], expected, actual) - } - } - } // Test if IMPORT INTO works here by testing that they produce the same // results as IMPORT TABLE. t.Run("import-into", func(t *testing.T) { @@ -5460,9 +5405,7 @@ func TestImportPgCopy(t *testing.T) { intoCmd += " WITH " + strings.Join(flags, ", ") } sqlDB.Exec(t, intoCmd, opts...) - importStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT * FROM test%d ORDER BY i", i)) - intoStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT * FROM into%d ORDER BY i", i)) - require.Equal(t, importStr, intoStr) + checkQueryResults(fmt.Sprintf(`SELECT * FROM into%d ORDER BY i`, i)) }) t.Run("import-into-target-cols-reordered", func(t *testing.T) { defer sqlDB.Exec(t, fmt.Sprintf(`DROP TABLE into%d`, i)) @@ -5472,12 +5415,7 @@ func TestImportPgCopy(t *testing.T) { intoCmd += " WITH " + strings.Join(flags, ", ") } sqlDB.Exec(t, intoCmd, opts...) - colNames := []string{"i", "s", "b"} - for _, colName := range colNames { - importStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT (%s) FROM test%d ORDER BY i", colName, i)) - intoStr := sqlDB.QueryStr(t, fmt.Sprintf("SELECT (%s) FROM into%d ORDER BY i", colName, i)) - require.Equal(t, importStr, intoStr) - } + checkQueryResults(fmt.Sprintf(`SELECT i, s, b FROM into%d ORDER BY i`, i)) }) }) } @@ -5516,19 +5454,6 @@ func TestImportPgDump(t *testing.T) { query string args []interface{} }{ - { - `read data only`, - expectSimple, - `IMPORT TABLE simple ( - i INT8, - s text, - b bytea, - CONSTRAINT simple_pkey PRIMARY KEY (i), - UNIQUE INDEX simple_b_s_idx (b, s), - INDEX simple_s_idx (s) - ) PGDUMP DATA ($1) WITH ignore_unsupported_statements`, - simple, - }, {`single table dump`, expectSimple, `IMPORT TABLE simple FROM PGDUMP ($1) WITH ignore_unsupported_statements`, simple}, {`second table dump`, expectSecond, `IMPORT TABLE second FROM PGDUMP ($1) WITH ignore_unsupported_statements`, second}, {`simple from multi`, expectSimple, `IMPORT TABLE simple FROM PGDUMP ($1) WITH ignore_unsupported_statements`, multitable}, @@ -6320,13 +6245,6 @@ func TestImportAvro(t *testing.T) { simpleJSON := fmt.Sprintf("nodelocal://0/%s", "simple-sorted.json") simplePrettyJSON := fmt.Sprintf("nodelocal://0/%s", "simple-sorted.pjson") simpleBinRecords := fmt.Sprintf("nodelocal://0/%s", "simple-sorted-records.avro") - tableSchema := fmt.Sprintf("nodelocal://0/%s", "simple-schema.sql") - - data, err := ioutil.ReadFile("testdata/avro/simple-schema.json") - if err != nil { - t.Fatal(err) - } - simpleSchema := string(data) tests := []struct { name string @@ -6335,11 +6253,6 @@ func TestImportAvro(t *testing.T) { args []interface{} err bool }{ - { - name: "import-ocf", - sql: "IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea) AVRO DATA ($1)", - args: []interface{}{simpleOcf}, - }, { name: "import-ocf-into-table", sql: "IMPORT INTO simple AVRO DATA ($1)", @@ -6353,14 +6266,10 @@ func TestImportAvro(t *testing.T) { args: []interface{}{simpleOcf}, }, { - name: "import-ocf-create-using", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2)", - args: []interface{}{tableSchema, simpleOcf}, - }, - { - name: "import-json-records", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2) WITH data_as_json_records, schema_uri=$3", - args: []interface{}{tableSchema, simpleJSON, simpleSchemaURI}, + name: "import-json-records", + sql: "IMPORT INTO simple AVRO DATA ($1) WITH data_as_json_records, schema_uri=$2", + create: "CREATE TABLE simple (i INT8, s text, b bytea)", + args: []interface{}{simpleJSON, simpleSchemaURI}, }, { name: "import-json-records-into-table-ignores-extra-fields", @@ -6369,47 +6278,43 @@ func TestImportAvro(t *testing.T) { args: []interface{}{simpleJSON, simpleSchemaURI}, }, { - name: "import-json-records-inline-schema", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2) WITH data_as_json_records, schema=$3", - args: []interface{}{tableSchema, simpleJSON, simpleSchema}, - }, - { - name: "import-json-pretty-printed-records", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2) WITH data_as_json_records, schema_uri=$3", - args: []interface{}{tableSchema, simplePrettyJSON, simpleSchemaURI}, - }, - { - name: "import-avro-fragments", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2) WITH data_as_binary_records, records_terminated_by='', schema_uri=$3", - args: []interface{}{tableSchema, simpleBinRecords, simpleSchemaURI}, + name: "import-json-pretty-printed-records", + sql: "IMPORT INTO simple AVRO DATA ($1) WITH data_as_json_records, schema_uri=$2", + create: "CREATE TABLE simple (i INT8 PRIMARY KEY)", + args: []interface{}{simplePrettyJSON, simpleSchemaURI}, }, { - name: "fail-import-expect-ocf-got-json", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2)", - args: []interface{}{tableSchema, simpleJSON}, - err: true, + name: "import-avro-fragments", + sql: "IMPORT INTO simple AVRO DATA ($1) WITH data_as_binary_records, records_terminated_by='', schema_uri=$2", + create: "CREATE TABLE simple (i INT8 PRIMARY KEY)", + args: []interface{}{simpleBinRecords, simpleSchemaURI}, }, { - name: "relaxed-import-sets-missing-fields", - sql: "IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea, z int) AVRO DATA ($1)", - args: []interface{}{simpleOcf}, + name: "fail-import-expect-ocf-got-json", + sql: "IMPORT INTO simple AVRO DATA ($2)", + create: "CREATE TABLE simple (i INT8 PRIMARY KEY)", + args: []interface{}{simpleJSON}, + err: true, }, { - name: "relaxed-import-ignores-extra-fields", - sql: "IMPORT TABLE simple (i INT8 PRIMARY KEY) AVRO DATA ($1)", - args: []interface{}{simpleOcf}, + name: "relaxed-import-sets-missing-fields", + sql: "IMPORT INTO simple AVRO DATA ($1)", + create: "CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea, z int)", + args: []interface{}{simpleOcf}, }, { - name: "strict-import-errors-missing-fields", - sql: "IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea, z int) AVRO DATA ($1) WITH strict_validation", - args: []interface{}{simpleOcf}, - err: true, + name: "strict-import-errors-missing-fields", + sql: "IMPORT INTO simple AVRO DATA ($1) WITH strict_validation", + create: "CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea, z int)", + args: []interface{}{simpleOcf}, + err: true, }, { - name: "strict-import-errors-extra-fields", - sql: "IMPORT TABLE simple (i INT8 PRIMARY KEY) AVRO DATA ($1) WITH strict_validation", - args: []interface{}{simpleOcf}, - err: true, + name: "strict-import-errors-extra-fields", + sql: "IMPORT INTO simple AVRO DATA ($1) WITH strict_validation", + create: "CREATE TABLE simple (i INT8 PRIMARY KEY)", + args: []interface{}{simpleOcf}, + err: true, }, } @@ -6498,11 +6403,6 @@ func TestImportMultiRegion(t *testing.T) { })) defer srv.Close() - // Table schemas for USING - tableSchemaMR := fmt.Sprintf("nodelocal://0/avro/%s", "simple-schema-multi-region.sql") - tableSchemaMRRegionalByRow := fmt.Sprintf("nodelocal://0/avro/%s", - "simple-schema-multi-region-regional-by-row.sql") - viewsAndSequencesTestCases := []struct { desc string importSQL string @@ -6564,27 +6464,12 @@ DROP VIEW IF EXISTS v`, during string }{ { - name: "import-create-using-multi-region-to-non-multi-region-database", - db: "foo", - table: "simple", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2)", - args: []interface{}{tableSchemaMR, simpleOcf}, - errString: "cannot restore or create multi-region table simple into non-multi-region database foo", - }, - { - name: "import-create-using-multi-region-regional-by-table-to-multi-region-database", - db: "multi_region", - table: "simple", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2)", - args: []interface{}{tableSchemaMR, simpleOcf}, - }, - { - name: "import-create-using-multi-region-regional-by-row-to-multi-region-database", - db: "multi_region", - table: "simple", - sql: "IMPORT TABLE simple CREATE USING $1 AVRO DATA ($2)", - args: []interface{}{tableSchemaMRRegionalByRow, simpleOcf}, - errString: "IMPORT to REGIONAL BY ROW table not supported", + name: "import-create-using-multi-region-regional-by-table-to-multi-region-database", + db: "multi_region", + table: "simple", + sql: "IMPORT INTO simple AVRO DATA ($1)", + create: "CREATE TABLE simple (i integer PRIMARY KEY, s text, b bytea) LOCALITY REGIONAL BY TABLE", + args: []interface{}{simpleOcf}, }, { name: "import-into-multi-region-regional-by-row-default-col-to-multi-region-database", @@ -6636,7 +6521,7 @@ CREATE TABLE mr_regional_by_row (i INT8 PRIMARY KEY, s typ, b bytea) LOCALITY RE `, sql: "IMPORT INTO mr_regional_by_row (i, s, b, crdb_region) CSV DATA ($1)", during: `ALTER TYPE typ ADD VALUE 'b'`, - errString: `type descriptor "typ" \(67\) has been ` + + errString: `type descriptor "typ" \(66\) has been ` + `modified, potentially incompatibly, since import planning; ` + `aborting to avoid possible corruption`, args: []interface{}{srv.URL}, @@ -6837,6 +6722,7 @@ func TestImportClientDisconnect(t *testing.T) { pgURL, cleanup := sqlutils.PGUrl(t, tc.Server(0).ServingSQLAddr(), "TestImportClientDisconnect-testuser", url.User("testuser")) defer cleanup() + runner.Exec(t, "CREATE TABLE foo (k INT PRIMARY KEY, v STRING)") // Kick off the import on a new connection which we're going to close. done := make(chan struct{}) @@ -6849,7 +6735,7 @@ func TestImportClientDisconnect(t *testing.T) { db, err := pgx.ConnectConfig(ctx, connCfg) assert.NoError(t, err) defer func() { _ = db.Close(ctx) }() - _, err = db.Exec(ctxToCancel, `IMPORT TABLE foo (k INT PRIMARY KEY, v STRING) CSV DATA ($1)`, srv.URL) + _, err = db.Exec(ctxToCancel, `IMPORT INTO foo CSV DATA ($1)`, srv.URL) assert.Equal(t, context.Canceled, errors.Unwrap(err)) }() @@ -6991,26 +6877,25 @@ func TestImportInTenant(t *testing.T) { t11 := sqlutils.MakeSQLRunner(conn11) const userfileURI = "userfile://defaultdb.public.root/test.csv" - const importStmt = "IMPORT TABLE foo (k INT PRIMARY KEY, v INT) CSV DATA ($1)" + const createStmt = "CREATE TABLE foo (k INT PRIMARY KEY, v INT)" + const importStmt = "IMPORT INTO foo CSV DATA ($1)" // Upload different files to same userfile name on each of host and tenants. require.NoError(t, putUserfile(ctx, conn, security.RootUserName(), userfileURI, []byte("1,2\n3,4"))) require.NoError(t, putUserfile(ctx, conn10, security.RootUserName(), userfileURI, []byte("10,2"))) require.NoError(t, putUserfile(ctx, conn11, security.RootUserName(), userfileURI, []byte("11,22\n33,44\n55,66"))) + sqlDB.Exec(t, createStmt) sqlDB.Exec(t, importStmt, userfileURI) sqlDB.CheckQueryResults(t, "SELECT * FROM foo", [][]string{{"1", "2"}, {"3", "4"}}) + t10.Exec(t, createStmt) t10.Exec(t, importStmt, userfileURI) t10.CheckQueryResults(t, "SELECT * FROM foo", [][]string{{"10", "2"}}) + t11.Exec(t, createStmt) t11.Exec(t, importStmt, userfileURI) t11.CheckQueryResults(t, "SELECT * FROM foo", [][]string{{"11", "22"}, {"33", "44"}, {"55", "66"}}) - - const createStmt = "CREATE TABLE bar (k INT PRIMARY KEY, v INT)" - const importIntoStmt = "IMPORT INTO bar CSV DATA ($1)" - t10.Exec(t, createStmt) - t10.Exec(t, importIntoStmt, userfileURI) } func putUserfile( @@ -7073,24 +6958,24 @@ func TestDetachedImport(t *testing.T) { sqlDB := sqlutils.MakeSQLRunner(connDB) sqlDB.Exec(t, `CREATE DATABASE foo; SET DATABASE = foo`) + sqlDB.Exec(t, "CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea)") simpleOcf := fmt.Sprintf("nodelocal://0/%s", "simple.ocf") - importQuery := `IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea) AVRO DATA ($1)` - importQueryDetached := importQuery + " WITH DETACHED" importIntoQuery := `IMPORT INTO simple AVRO DATA ($1)` importIntoQueryDetached := importIntoQuery + " WITH DETACHED" // DETACHED import w/out transaction is okay. var jobID jobspb.JobID - sqlDB.QueryRow(t, importQueryDetached, simpleOcf).Scan(&jobID) + sqlDB.QueryRow(t, importIntoQueryDetached, simpleOcf).Scan(&jobID) waitForJobResult(t, tc, jobID, jobs.StatusSucceeded) sqlDB.Exec(t, "DROP table simple") + sqlDB.Exec(t, "CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea)") // Running import under transaction requires DETACHED option. importWithoutDetached := func(txn *gosql.Tx) error { - return txn.QueryRow(importQuery, simpleOcf).Scan(&jobID) + return txn.QueryRow(importIntoQuery, simpleOcf).Scan(&jobID) } err := crdb.ExecuteTx(ctx, connDB, nil, importWithoutDetached) require.True(t, @@ -7098,24 +6983,26 @@ func TestDetachedImport(t *testing.T) { // We can execute IMPORT under transaction with detached option. importWithDetached := func(txn *gosql.Tx) error { - return txn.QueryRow(importQueryDetached, simpleOcf).Scan(&jobID) + return txn.QueryRow(importIntoQueryDetached, simpleOcf).Scan(&jobID) } err = crdb.ExecuteTx(ctx, connDB, nil, importWithDetached) require.NoError(t, err) waitForJobResult(t, tc, jobID, jobs.StatusSucceeded) sqlDB.Exec(t, "DROP table simple") + sqlDB.Exec(t, "CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea)") // Detached import should fail when the table already exists. - sqlDB.QueryRow(t, importQueryDetached, simpleOcf).Scan(&jobID) + sqlDB.QueryRow(t, importIntoQueryDetached, simpleOcf).Scan(&jobID) waitForJobResult(t, tc, jobID, jobs.StatusSucceeded) - sqlDB.QueryRow(t, importQueryDetached, simpleOcf).Scan(&jobID) + sqlDB.QueryRow(t, importIntoQueryDetached, simpleOcf).Scan(&jobID) waitForJobResult(t, tc, jobID, jobs.StatusFailed) sqlDB.Exec(t, "DROP table simple") + sqlDB.Exec(t, "CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea)") // Detached import into should fail when there are key collisions. - sqlDB.QueryRow(t, importQueryDetached, simpleOcf).Scan(&jobID) + sqlDB.QueryRow(t, importIntoQueryDetached, simpleOcf).Scan(&jobID) waitForJobResult(t, tc, jobID, jobs.StatusSucceeded) sqlDB.QueryRow(t, importIntoQueryDetached, simpleOcf).Scan(&jobID) waitForJobResult(t, tc, jobID, jobs.StatusFailed) @@ -7163,10 +7050,12 @@ func TestImportJobEventLogging(t *testing.T) { // the event log for the entries. sqlDB.Exec(t, `CREATE DATABASE foo; SET DATABASE = foo`) beforeImport := timeutil.Now() - importQuery := `IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea) AVRO DATA ($1)` + createQuery := `CREATE TABLE simple (i INT8 PRIMARY KEY, s text, b bytea)` + importQuery := `IMPORT INTO simple AVRO DATA ($1)` var jobID int64 var unused interface{} + sqlDB.Exec(t, createQuery) sqlDB.QueryRow(t, importQuery, simpleOcf).Scan(&jobID, &unused, &unused, &unused, &unused, &unused) @@ -7178,8 +7067,8 @@ func TestImportJobEventLogging(t *testing.T) { // Now let's test the events that are emitted when a job fails. forceFailure = true beforeSecondImport := timeutil.Now() - secondImport := `IMPORT TABLE simple (i INT8 PRIMARY KEY, s text, b bytea) AVRO DATA ($1)` - sqlDB.ExpectErrSucceedsSoon(t, "testing injected failure", secondImport, simpleOcf) + sqlDB.Exec(t, createQuery) + sqlDB.ExpectErrSucceedsSoon(t, "testing injected failure", importQuery, simpleOcf) row := sqlDB.QueryRow(t, "SELECT job_id FROM [SHOW JOBS] WHERE status = 'failed'") row.Scan(&jobID) @@ -7217,9 +7106,8 @@ CREATE TABLE default_int ( defer srv.Close() tests := []struct { - name string - query string - nonBundledFormat bool + name string + query string }{ { name: "import pgdump", @@ -7237,17 +7125,6 @@ CREATE TABLE default_int ( name: "import table from mysqldump", query: `IMPORT TABLE default_int FROM MYSQLDUMP ($1)`, }, - { - name: "non-bundled csv format", - query: ` -IMPORT TABLE default_int ( - a INTEGER PRIMARY KEY, - b INT -) -CSV DATA ($1) -`, - nonBundledFormat: true, - }, } for _, test := range tests { @@ -7260,11 +7137,6 @@ CSV DATA ($1) sqlDB.Exec(t, fmt.Sprintf("set cluster setting sql.defaults.default_int_size = %d;", defaultIntSize)) sqlDB.Exec(t, fmt.Sprintf("set default_int_size = %d;", defaultIntSize)) - if test.nonBundledFormat { - // Initialize empty data for non-bundled import format - data = "" - } - sqlDB.Exec(t, test.query, srv.URL) // Verify that the columns have the expected data type diff --git a/pkg/ccl/importccl/import_table_creation.go b/pkg/ccl/importccl/import_table_creation.go index 33763339fcce..395723202d0f 100644 --- a/pkg/ccl/importccl/import_table_creation.go +++ b/pkg/ccl/importccl/import_table_creation.go @@ -11,10 +11,8 @@ package importccl import ( "context" "fmt" - "io/ioutil" "strings" - "github.com/cockroachdb/cockroach/pkg/cloud" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/security" @@ -46,40 +44,6 @@ const ( defaultCSVTableID descpb.ID = defaultCSVParentID + 1 ) -func readCreateTableFromStore( - ctx context.Context, - filename string, - externalStorageFromURI cloud.ExternalStorageFromURIFactory, - user security.SQLUsername, -) (*tree.CreateTable, error) { - store, err := externalStorageFromURI(ctx, filename, user) - if err != nil { - return nil, err - } - defer store.Close() - reader, err := store.ReadFile(ctx, "") - if err != nil { - return nil, err - } - defer reader.Close() - tableDefStr, err := ioutil.ReadAll(reader) - if err != nil { - return nil, err - } - stmts, err := parser.Parse(string(tableDefStr)) - if err != nil { - return nil, err - } - if len(stmts) != 1 { - return nil, errors.Errorf("expected 1 create table statement, found %d", len(stmts)) - } - create, ok := stmts[0].AST.(*tree.CreateTable) - if !ok { - return nil, errors.New("expected CREATE TABLE statement in table file") - } - return create, nil -} - type fkHandler struct { allowed bool skip bool diff --git a/pkg/ccl/logictestccl/testdata/logic_test/multi_region_import_export b/pkg/ccl/logictestccl/testdata/logic_test/multi_region_import_export index 13c0cb561d2c..703d75d3b577 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/multi_region_import_export +++ b/pkg/ccl/logictestccl/testdata/logic_test/multi_region_import_export @@ -75,13 +75,14 @@ EXPORT INTO CSV 'nodelocal://1/team_export/' WITH DELIMITER = '|' FROM TABLE tea statement ok use multi_region_test_db; -IMPORT TABLE team ( +CREATE TABLE team ( id int PRIMARY KEY, name string, likes string[], - dislikes string[] -) -CSV DATA ('nodelocal://1/team_export/export*.csv') WITH DELIMITER = '|' + dislikes string[], + FAMILY "primary" (id, name, likes, dislikes) +); +IMPORT INTO team CSV DATA ('nodelocal://1/team_export/export*.csv') WITH DELIMITER = '|' query ITTT colnames SELECT * FROM team diff --git a/pkg/internal/sqlsmith/bulkio.go b/pkg/internal/sqlsmith/bulkio.go index 693037669b8d..32b9f58856dc 100644 --- a/pkg/internal/sqlsmith/bulkio.go +++ b/pkg/internal/sqlsmith/bulkio.go @@ -228,15 +228,21 @@ func makeImport(s *Smither) (tree.Statement, bool) { tab := s.name("tab") s.lock.Lock() schema := fmt.Sprintf("/%s%s", exp, exportSchema) - s.bulkFiles[schema] = importCreateTableRE.ReplaceAll( + tableSchema := importCreateTableRE.ReplaceAll( s.bulkFiles[schema], []byte(fmt.Sprintf("CREATE TABLE %s (", tab)), ) s.lock.Unlock() + // Create the table to be imported into. + _, err := s.db.Exec(string(tableSchema)) + if err != nil { + return nil, false + } + return &tree.Import{ Table: tree.NewUnqualifiedTableName(tab), - CreateFile: tree.NewStrVal(s.bulkSrv.URL + schema), + Into: true, FileFormat: "CSV", Files: files, Options: tree.KVOptions{ diff --git a/pkg/sql/parser/help_test.go b/pkg/sql/parser/help_test.go index ed6ae4b11e07..30652fad37e5 100644 --- a/pkg/sql/parser/help_test.go +++ b/pkg/sql/parser/help_test.go @@ -450,7 +450,6 @@ func TestContextualHelp(t *testing.T) { {`RESTORE foo FROM 'bar' ??`, `RESTORE`}, {`RESTORE DATABASE ??`, `RESTORE`}, - {`IMPORT TABLE foo CREATE USING 'foo.sql' CSV DATA ('foo') ??`, `IMPORT`}, {`IMPORT TABLE ??`, `IMPORT`}, {`EXPORT ??`, `EXPORT`}, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index e2b14391ab3a..b365b6069e6e 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -3107,16 +3107,6 @@ import_stmt: name := $3.unresolvedObjectName().ToTableName() $$.val = &tree.Import{Bundle: true, Table: &name, FileFormat: $5, Files: tree.Exprs{$6.expr()}, Options: $7.kvOptions()} } -| IMPORT TABLE table_name CREATE USING string_or_placeholder import_format DATA '(' string_or_placeholder_list ')' opt_with_options - { - name := $3.unresolvedObjectName().ToTableName() - $$.val = &tree.Import{Table: &name, CreateFile: $6.expr(), FileFormat: $7, Files: $10.exprs(), Options: $12.kvOptions()} - } -| IMPORT TABLE table_name '(' table_elem_list ')' import_format DATA '(' string_or_placeholder_list ')' opt_with_options - { - name := $3.unresolvedObjectName().ToTableName() - $$.val = &tree.Import{Table: &name, CreateDefs: $5.tblDefs(), FileFormat: $7, Files: $10.exprs(), Options: $12.kvOptions()} - } | IMPORT INTO table_name '(' insert_column_list ')' import_format DATA '(' string_or_placeholder_list ')' opt_with_options { name := $3.unresolvedObjectName().ToTableName() diff --git a/pkg/sql/parser/testdata/import_export b/pkg/sql/parser/testdata/import_export index 5e744ec4f64a..e7f367c4c20d 100644 --- a/pkg/sql/parser/testdata/import_export +++ b/pkg/sql/parser/testdata/import_export @@ -1,43 +1,3 @@ -parse -IMPORT TABLE foo CREATE USING 'nodelocal://0/some/file' CSV DATA ('path/to/some/file', $1) WITH temp = 'path/to/temp' ----- -IMPORT TABLE foo CREATE USING 'nodelocal://0/some/file' CSV DATA ('path/to/some/file', $1) WITH temp = 'path/to/temp' -IMPORT TABLE foo CREATE USING ('nodelocal://0/some/file') CSV DATA (('path/to/some/file'), ($1)) WITH temp = ('path/to/temp') -- fully parenthesized -IMPORT TABLE foo CREATE USING '_' CSV DATA ('_', $1) WITH temp = '_' -- literals removed -IMPORT TABLE _ CREATE USING 'nodelocal://0/some/file' CSV DATA ('path/to/some/file', $1) WITH _ = 'path/to/temp' -- identifiers removed - -parse -EXPLAIN IMPORT TABLE foo CREATE USING 'nodelocal://0/some/file' CSV DATA ('path/to/some/file', $1) WITH temp = 'path/to/temp' ----- -EXPLAIN IMPORT TABLE foo CREATE USING 'nodelocal://0/some/file' CSV DATA ('path/to/some/file', $1) WITH temp = 'path/to/temp' -EXPLAIN IMPORT TABLE foo CREATE USING ('nodelocal://0/some/file') CSV DATA (('path/to/some/file'), ($1)) WITH temp = ('path/to/temp') -- fully parenthesized -EXPLAIN IMPORT TABLE foo CREATE USING '_' CSV DATA ('_', $1) WITH temp = '_' -- literals removed -EXPLAIN IMPORT TABLE _ CREATE USING 'nodelocal://0/some/file' CSV DATA ('path/to/some/file', $1) WITH _ = 'path/to/temp' -- identifiers removed - -parse -IMPORT TABLE foo CREATE USING 'nodelocal://0/some/file' DELIMITED DATA ('path/to/some/file', $1) ----- -IMPORT TABLE foo CREATE USING 'nodelocal://0/some/file' DELIMITED DATA ('path/to/some/file', $1) -IMPORT TABLE foo CREATE USING ('nodelocal://0/some/file') DELIMITED DATA (('path/to/some/file'), ($1)) -- fully parenthesized -IMPORT TABLE foo CREATE USING '_' DELIMITED DATA ('_', $1) -- literals removed -IMPORT TABLE _ CREATE USING 'nodelocal://0/some/file' DELIMITED DATA ('path/to/some/file', $1) -- identifiers removed - -parse -IMPORT TABLE foo (id INT8 PRIMARY KEY, email STRING, age INT8) CSV DATA ('path/to/some/file', $1) WITH temp = 'path/to/temp' ----- -IMPORT TABLE foo (id INT8 PRIMARY KEY, email STRING, age INT8) CSV DATA ('path/to/some/file', $1) WITH temp = 'path/to/temp' -IMPORT TABLE foo (id INT8 PRIMARY KEY, email STRING, age INT8) CSV DATA (('path/to/some/file'), ($1)) WITH temp = ('path/to/temp') -- fully parenthesized -IMPORT TABLE foo (id INT8 PRIMARY KEY, email STRING, age INT8) CSV DATA ('_', $1) WITH temp = '_' -- literals removed -IMPORT TABLE _ (_ INT8 PRIMARY KEY, _ STRING, _ INT8) CSV DATA ('path/to/some/file', $1) WITH _ = 'path/to/temp' -- identifiers removed - -parse -IMPORT TABLE foo (id INT8, email STRING, age INT8) CSV DATA ('path/to/some/file', $1) WITH comma = ',', "nullif" = 'n/a', temp = $2 ----- -IMPORT TABLE foo (id INT8, email STRING, age INT8) CSV DATA ('path/to/some/file', $1) WITH comma = ',', "nullif" = 'n/a', temp = $2 -IMPORT TABLE foo (id INT8, email STRING, age INT8) CSV DATA (('path/to/some/file'), ($1)) WITH comma = (','), "nullif" = ('n/a'), temp = ($2) -- fully parenthesized -IMPORT TABLE foo (id INT8, email STRING, age INT8) CSV DATA ('_', $1) WITH comma = '_', "nullif" = '_', temp = $2 -- literals removed -IMPORT TABLE _ (_ INT8, _ STRING, _ INT8) CSV DATA ('path/to/some/file', $1) WITH _ = ',', _ = 'n/a', _ = $2 -- identifiers removed - parse IMPORT TABLE foo FROM PGDUMPCREATE 'nodelocal://0/foo/bar' WITH temp = 'path/to/temp' ---- @@ -70,14 +30,6 @@ IMPORT INTO foo CSV DATA (('path/to/some/file'), ($1)) WITH temp = ('path/to/tem IMPORT INTO foo CSV DATA ('_', $1) WITH temp = '_' -- literals removed IMPORT INTO _ CSV DATA ('path/to/some/file', $1) WITH _ = 'path/to/temp' -- identifiers removed -parse -IMPORT TABLE a.foo (LIKE tab, col INT8 CONSTRAINT conname NULL NOT VISIBLE) CSV DATA ('placeholder') ----- -IMPORT TABLE a.foo (LIKE tab, col INT8 CONSTRAINT conname NULL NOT VISIBLE) CSV DATA ('placeholder') -IMPORT TABLE a.foo (LIKE tab, col INT8 CONSTRAINT conname NULL NOT VISIBLE) CSV DATA (('placeholder')) -- fully parenthesized -IMPORT TABLE a.foo (LIKE tab, col INT8 CONSTRAINT conname NULL NOT VISIBLE) CSV DATA ('_') -- literals removed -IMPORT TABLE _._ (LIKE _, _ INT8 CONSTRAINT _ NULL NOT VISIBLE) CSV DATA ('placeholder') -- identifiers removed - parse IMPORT PGDUMP 'nodelocal://0/foo/bar' WITH temp = 'path/to/temp' ---- diff --git a/pkg/sql/parser/testdata/prepared_stmts b/pkg/sql/parser/testdata/prepared_stmts index a852b4e808c3..abe61757a437 100644 --- a/pkg/sql/parser/testdata/prepared_stmts +++ b/pkg/sql/parser/testdata/prepared_stmts @@ -239,20 +239,20 @@ PREPARE a (INT8) AS RESUME JOBS SELECT $1 -- literals removed PREPARE _ (INT8) AS RESUME JOBS SELECT $1 -- identifiers removed parse -PREPARE a AS IMPORT TABLE a CREATE USING 'b' CSV DATA ('c') WITH temp = 'd' +PREPARE a AS IMPORT INTO a CSV DATA ('c') WITH temp = 'd' ---- -PREPARE a AS IMPORT TABLE a CREATE USING 'b' CSV DATA ('c') WITH temp = 'd' -PREPARE a AS IMPORT TABLE a CREATE USING ('b') CSV DATA (('c')) WITH temp = ('d') -- fully parenthesized -PREPARE a AS IMPORT TABLE a CREATE USING '_' CSV DATA ('_') WITH temp = '_' -- literals removed -PREPARE _ AS IMPORT TABLE _ CREATE USING 'b' CSV DATA ('c') WITH _ = 'd' -- identifiers removed +PREPARE a AS IMPORT INTO a CSV DATA ('c') WITH temp = 'd' +PREPARE a AS IMPORT INTO a CSV DATA (('c')) WITH temp = ('d') -- fully parenthesized +PREPARE a AS IMPORT INTO a CSV DATA ('_') WITH temp = '_' -- literals removed +PREPARE _ AS IMPORT INTO _ CSV DATA ('c') WITH _ = 'd' -- identifiers removed parse -PREPARE a (STRING, STRING, STRING) AS IMPORT TABLE a CREATE USING $1 CSV DATA ($2) WITH temp = $3 +PREPARE a (STRING, STRING, STRING) AS IMPORT INTO a CSV DATA ($2) WITH temp = $3 ---- -PREPARE a (STRING, STRING, STRING) AS IMPORT TABLE a CREATE USING $1 CSV DATA ($2) WITH temp = $3 -PREPARE a (STRING, STRING, STRING) AS IMPORT TABLE a CREATE USING ($1) CSV DATA (($2)) WITH temp = ($3) -- fully parenthesized -PREPARE a (STRING, STRING, STRING) AS IMPORT TABLE a CREATE USING $1 CSV DATA ($2) WITH temp = $3 -- literals removed -PREPARE _ (STRING, STRING, STRING) AS IMPORT TABLE _ CREATE USING $1 CSV DATA ($2) WITH _ = $3 -- identifiers removed +PREPARE a (STRING, STRING, STRING) AS IMPORT INTO a CSV DATA ($2) WITH temp = $3 +PREPARE a (STRING, STRING, STRING) AS IMPORT INTO a CSV DATA (($2)) WITH temp = ($3) -- fully parenthesized +PREPARE a (STRING, STRING, STRING) AS IMPORT INTO a CSV DATA ($2) WITH temp = $3 -- literals removed +PREPARE _ (STRING, STRING, STRING) AS IMPORT INTO _ CSV DATA ($2) WITH _ = $3 -- identifiers removed parse PREPARE a AS OPT PLAN 'some-string' diff --git a/pkg/sql/sem/tree/import.go b/pkg/sql/sem/tree/import.go index 19d492371388..ea3c76149cd5 100644 --- a/pkg/sql/sem/tree/import.go +++ b/pkg/sql/sem/tree/import.go @@ -15,8 +15,6 @@ type Import struct { Table *TableName Into bool IntoCols NameList - CreateFile Expr - CreateDefs TableDefs FileFormat string Files Exprs Bundle bool @@ -52,16 +50,6 @@ func (node *Import) Format(ctx *FmtCtx) { } else { ctx.WriteString("TABLE ") ctx.FormatNode(node.Table) - - if node.CreateFile != nil { - ctx.WriteString(" CREATE USING ") - ctx.FormatNode(node.CreateFile) - ctx.WriteString(" ") - } else { - ctx.WriteString(" (") - ctx.FormatNode(&node.CreateDefs) - ctx.WriteString(") ") - } } ctx.WriteString(node.FileFormat) ctx.WriteString(" DATA (") diff --git a/pkg/sql/sem/tree/pretty.go b/pkg/sql/sem/tree/pretty.go index 26d95c143479..0b95a6fd27b8 100644 --- a/pkg/sql/sem/tree/pretty.go +++ b/pkg/sql/sem/tree/pretty.go @@ -2153,27 +2153,12 @@ func (node *Import) doc(p *PrettyCfg) pretty.Doc { items = append(items, p.row("FROM", pretty.Nil)) } items = append(items, p.row(node.FileFormat, p.Doc(&node.Files))) - } else { - if node.Into { - into := p.Doc(node.Table) - if node.IntoCols != nil { - into = p.nestUnder(into, p.bracket("(", p.Doc(&node.IntoCols), ")")) - } - items = append(items, p.row("INTO", into)) - } else { - if node.CreateFile != nil { - items = append(items, p.row("TABLE", p.Doc(node.Table))) - items = append(items, p.row("CREATE USING", p.Doc(node.CreateFile))) - } else { - table := p.bracketDoc( - pretty.ConcatSpace(p.Doc(node.Table), pretty.Text("(")), - p.Doc(&node.CreateDefs), - pretty.Text(")"), - ) - items = append(items, p.row("TABLE", table)) - } + } else if node.Into { + into := p.Doc(node.Table) + if node.IntoCols != nil { + into = p.nestUnder(into, p.bracket("(", p.Doc(&node.IntoCols), ")")) } - + items = append(items, p.row("INTO", into)) data := p.bracketKeyword( "DATA", " (", p.Doc(&node.Files), @@ -2181,6 +2166,7 @@ func (node *Import) doc(p *PrettyCfg) pretty.Doc { ) items = append(items, p.row(node.FileFormat, data)) } + if node.Options != nil { items = append(items, p.row("WITH", p.Doc(&node.Options))) } diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden b/pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden deleted file mode 100644 index e373b1046875..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden +++ /dev/null @@ -1,37 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t -CREATE USING - 'http://test' -CSV - DATA ( - $1, - 'http://csv' - ) - -12: ------------- - IMPORT - TABLE t -CREATE USING 'http://test' - CSV DATA ( - $1, - 'http://csv' - ) - -36: ------------------------------------- - IMPORT - TABLE t -CREATE USING 'http://test' - CSV DATA ($1, 'http://csv') - -69: ---------------------------------------------------------------------- -IMPORT TABLE t CREATE USING 'http://test' CSV DATA ($1, 'http://csv') - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden.short b/pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden.short deleted file mode 100644 index a5af51a71a83..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.align-deindent.golden.short +++ /dev/null @@ -1,10 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- - IMPORT - TABLE t -CREATE USING 'http://test' - CSV DATA ($1, 'http://csv') - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden b/pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden deleted file mode 100644 index e373b1046875..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden +++ /dev/null @@ -1,37 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t -CREATE USING - 'http://test' -CSV - DATA ( - $1, - 'http://csv' - ) - -12: ------------- - IMPORT - TABLE t -CREATE USING 'http://test' - CSV DATA ( - $1, - 'http://csv' - ) - -36: ------------------------------------- - IMPORT - TABLE t -CREATE USING 'http://test' - CSV DATA ($1, 'http://csv') - -69: ---------------------------------------------------------------------- -IMPORT TABLE t CREATE USING 'http://test' CSV DATA ($1, 'http://csv') - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden.short b/pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden.short deleted file mode 100644 index a5af51a71a83..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.align-only.golden.short +++ /dev/null @@ -1,10 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- - IMPORT - TABLE t -CREATE USING 'http://test' - CSV DATA ($1, 'http://csv') - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.ref.golden b/pkg/sql/sem/tree/testdata/pretty/import3.ref.golden deleted file mode 100644 index 22b593410578..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.ref.golden +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t -CREATE USING - 'http://test' -CSV - DATA ( - $1, - 'http://csv' - ) - -27: ---------------------------- -IMPORT -TABLE - t -CREATE USING - 'http://test' -CSV - DATA ($1, 'http://csv') - -69: ---------------------------------------------------------------------- -IMPORT TABLE t CREATE USING 'http://test' CSV DATA ($1, 'http://csv') - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.ref.golden.short b/pkg/sql/sem/tree/testdata/pretty/import3.ref.golden.short deleted file mode 100644 index f712fc2c424a..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.ref.golden.short +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t -CREATE USING - 'http://test' -CSV - DATA ($1, 'http://csv') - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import3.sql b/pkg/sql/sem/tree/testdata/pretty/import3.sql deleted file mode 100644 index f0f7d55bb104..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import3.sql +++ /dev/null @@ -1 +0,0 @@ -import table t create using 'http://test' csv data ($1, 'http://csv') diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden b/pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden deleted file mode 100644 index 97ebb64dac9d..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden +++ /dev/null @@ -1,69 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t ( - i - INT8, - s - STRING - ) -CSV - DATA ( - $1 - ) - -6: ------- -IMPORT - TABLE t ( - i - INT8, - s - STRING - ) - CSV DATA ( - $1 - ) - -15: ---------------- -IMPORT - TABLE t ( - i INT8, - s STRING - ) - CSV DATA ( - $1 - ) - -16: ----------------- -IMPORT - TABLE t ( - i INT8, - s STRING - ) - CSV DATA ($1) - -24: ------------------------- -IMPORT - TABLE t ( - i INT8, s STRING - ) - CSV DATA ($1) - -27: ---------------------------- -IMPORT - TABLE t (i INT8, s STRING) - CSV DATA ($1) - -47: ------------------------------------------------ -IMPORT TABLE t (i INT8, s STRING) CSV DATA ($1) - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden.short b/pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden.short deleted file mode 100644 index ddbd1ba9a316..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.align-deindent.golden.short +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT - TABLE t (i INT8, s STRING) - CSV DATA ($1) - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden b/pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden deleted file mode 100644 index 97ebb64dac9d..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden +++ /dev/null @@ -1,69 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t ( - i - INT8, - s - STRING - ) -CSV - DATA ( - $1 - ) - -6: ------- -IMPORT - TABLE t ( - i - INT8, - s - STRING - ) - CSV DATA ( - $1 - ) - -15: ---------------- -IMPORT - TABLE t ( - i INT8, - s STRING - ) - CSV DATA ( - $1 - ) - -16: ----------------- -IMPORT - TABLE t ( - i INT8, - s STRING - ) - CSV DATA ($1) - -24: ------------------------- -IMPORT - TABLE t ( - i INT8, s STRING - ) - CSV DATA ($1) - -27: ---------------------------- -IMPORT - TABLE t (i INT8, s STRING) - CSV DATA ($1) - -47: ------------------------------------------------ -IMPORT TABLE t (i INT8, s STRING) CSV DATA ($1) - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden.short b/pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden.short deleted file mode 100644 index ddbd1ba9a316..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.align-only.golden.short +++ /dev/null @@ -1,9 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT - TABLE t (i INT8, s STRING) - CSV DATA ($1) - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.ref.golden b/pkg/sql/sem/tree/testdata/pretty/import4.ref.golden deleted file mode 100644 index 26c200707f3e..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.ref.golden +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t ( - i - INT8, - s - STRING - ) -CSV - DATA ( - $1 - ) - -13: -------------- -IMPORT -TABLE - t ( - i - INT8, - s - STRING - ) -CSV - DATA ($1) - -24: ------------------------- -IMPORT -TABLE - t (i INT8, s STRING) -CSV - DATA ($1) - -47: ------------------------------------------------ -IMPORT TABLE t (i INT8, s STRING) CSV DATA ($1) - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.ref.golden.short b/pkg/sql/sem/tree/testdata/pretty/import4.ref.golden.short deleted file mode 100644 index 675c6ce15bc5..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.ref.golden.short +++ /dev/null @@ -1,11 +0,0 @@ -// Code generated by TestPretty. DO NOT EDIT. -// GENERATED FILE DO NOT EDIT -1: -- -IMPORT -TABLE - t (i INT8, s STRING) -CSV - DATA ($1) - - diff --git a/pkg/sql/sem/tree/testdata/pretty/import4.sql b/pkg/sql/sem/tree/testdata/pretty/import4.sql deleted file mode 100644 index 3ca9eb30e29c..000000000000 --- a/pkg/sql/sem/tree/testdata/pretty/import4.sql +++ /dev/null @@ -1 +0,0 @@ -import table t (i int, s string) csv data ($1) diff --git a/pkg/sql/sem/tree/walk.go b/pkg/sql/sem/tree/walk.go index d80d5466a5af..728e667e1edf 100644 --- a/pkg/sql/sem/tree/walk.go +++ b/pkg/sql/sem/tree/walk.go @@ -1084,15 +1084,6 @@ func (stmt *Import) copyNode() *Import { // walkStmt is part of the walkableStmt interface. func (stmt *Import) walkStmt(v Visitor) Statement { ret := stmt - if stmt.CreateFile != nil { - e, changed := WalkExpr(v, stmt.CreateFile) - if changed { - if ret == stmt { - ret = stmt.copyNode() - } - ret.CreateFile = e - } - } for i, expr := range stmt.Files { e, changed := WalkExpr(v, expr) if changed { From bddddcd8330cd824cd52302a676b48b1b3d89438 Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Sun, 31 Oct 2021 13:58:39 +0000 Subject: [PATCH 148/205] backupccl: simplify index span merging logic for backup This change simplifies the index span merging logic used by BACKUP when identifying what spans to backup and protect. Previously, we would attempt to "logically" merge spans by checking if any dropped indexes that have been GC'ed can be merged over. This has been the cause of a few subtle bugs as outlined in https://github.com/cockroachdb/cockroach/issues/72263. This change simplifies the merging logic to only merge adjacent, public index spans into a single span. If there exist any non-public index IDs between the two public index IDs, we no longer attempt to merge. In the presence of dropped indexes this will result in more spans being backed up and protected than before, but we believe that the simplification is worth losing the optimization in this case. This change also fixes an existing bug in the merging logic where two (or more) public indexes followed by an interleaved index would result in certain index keys being missed during backup. A regression test has been added to this effect. Fixes: #72263 Release note (bug fix): This change fixes two bugs in the logic that optimized the number of spans to backup. --- pkg/ccl/backupccl/backup_planning.go | 193 +++------------------- pkg/ccl/backupccl/backup_test.go | 234 ++++++++++++++++----------- 2 files changed, 160 insertions(+), 267 deletions(-) diff --git a/pkg/ccl/backupccl/backup_planning.go b/pkg/ccl/backupccl/backup_planning.go index 033a1ed0be65..ece152699768 100644 --- a/pkg/ccl/backupccl/backup_planning.go +++ b/pkg/ccl/backupccl/backup_planning.go @@ -15,7 +15,6 @@ import ( "fmt" "net/url" "path" - "sort" "strconv" "github.com/cockroachdb/cockroach/pkg/base" @@ -153,204 +152,49 @@ func (e *encryptedDataKeyMap) rangeOverMap(fn func(masterKeyID hashedMasterKeyID } } -type sortedIndexIDs []descpb.IndexID - -func (s sortedIndexIDs) Less(i, j int) bool { - return s[i] < s[j] -} - -func (s sortedIndexIDs) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s sortedIndexIDs) Len() int { - return len(s) -} - -// getLogicallyMergedTableSpans returns all the non-drop index spans of the -// provided table but after merging them so as to minimize the number of spans -// generated. The following rules are used to logically merge the sorted set of -// non-drop index spans: -// - Contiguous index spans are merged. -// - Two non-contiguous index spans are merged if a scan request for the index -// IDs between them does not return any results. -// -// Egs: {/Table/51/1 - /Table/51/2}, {/Table/51/3 - /Table/51/4} => {/Table/51/1 - /Table/51/4} -// provided the dropped index represented by the span -// {/Table/51/2 - /Table/51/3} has been gc'ed. -func getLogicallyMergedTableSpans( - ctx context.Context, - table catalog.TableDescriptor, - added map[tableAndIndex]bool, - codec keys.SQLCodec, - endTime hlc.Timestamp, - checkForKVInBounds func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error), +// getPublicIndexTableSpans returns all the public index spans of the +// provided table. +func getPublicIndexTableSpans( + table catalog.TableDescriptor, added map[tableAndIndex]bool, codec keys.SQLCodec, ) ([]roachpb.Span, error) { - // Spans with adding indexes are not safe to include in the backup since - // they may see non-transactional AddSST traffic. Future incremental backups - // will not have a way of incrementally backing up the data until #62585 is - // resolved. - addingIndexIDs := make(map[descpb.IndexID]struct{}) - var publicIndexIDs []descpb.IndexID - - allPhysicalIndexOpts := catalog.IndexOpts{DropMutations: true, AddMutations: true} - if err := catalog.ForEachIndex(table, allPhysicalIndexOpts, func(idx catalog.Index) error { + publicIndexSpans := make([]roachpb.Span, 0) + if err := catalog.ForEachActiveIndex(table, func(idx catalog.Index) error { key := tableAndIndex{tableID: table.GetID(), indexID: idx.GetID()} if added[key] { return nil } added[key] = true - if idx.Public() { - publicIndexIDs = append(publicIndexIDs, idx.GetID()) - } - if idx.Adding() { - addingIndexIDs[idx.GetID()] = struct{}{} - } + publicIndexSpans = append(publicIndexSpans, table.IndexSpan(codec, idx.GetID())) return nil }); err != nil { return nil, err } - if len(publicIndexIDs) == 0 { - return nil, nil - } - - // There is no merging possible with only a single index, short circuit. - if len(publicIndexIDs) == 1 { - return []roachpb.Span{table.IndexSpan(codec, publicIndexIDs[0])}, nil - } - - sort.Sort(sortedIndexIDs(publicIndexIDs)) - - var mergedIndexSpans []roachpb.Span - - // mergedSpan starts off as the first span in the set of spans being - // considered for a logical merge. - // The logical span merge algorithm walks over the table's non drop indexes - // using an lhsSpan and rhsSpan (always offset by 1). It checks all index IDs - // between lhsSpan and rhsSpan to look for dropped but non-gced KVs. The - // existence of such a KV indicates that the rhsSpan cannot be included in the - // current set of spans being logically merged, and so we update the - // mergedSpan to encompass the lhsSpan as that is the furthest we can go. - // After recording the new "merged" span, we update mergedSpan to be the - // rhsSpan, and start processing the next logically mergeable span set. - mergedSpan := table.IndexSpan(codec, publicIndexIDs[0]) - for curIndex := 0; curIndex < len(publicIndexIDs)-1; curIndex++ { - lhsIndexID := publicIndexIDs[curIndex] - rhsIndexID := publicIndexIDs[curIndex+1] - - lhsSpan := table.IndexSpan(codec, lhsIndexID) - rhsSpan := table.IndexSpan(codec, rhsIndexID) - - lhsIndex, err := table.FindIndexWithID(lhsIndexID) - if err != nil { - return nil, err - } - rhsIndex, err := table.FindIndexWithID(rhsIndexID) - if err != nil { - return nil, err - } - - // If either the lhs or rhs is an interleaved index, we do not attempt to - // perform a logical merge of the spans because the index span for - // interleaved contains the tableID/indexID of the furthest ancestor in - // the interleaved chain. - if lhsIndex.IsInterleaved() || rhsIndex.IsInterleaved() { - mergedIndexSpans = append(mergedIndexSpans, mergedSpan) - mergedSpan = rhsSpan - } else { - var foundDroppedKV bool - // Iterate over all index IDs between the two candidates (lhs and - // rhs) which may be logically merged. These index IDs represent - // non-public (and perhaps dropped) indexes between the two public - // index spans. - for i := lhsIndexID + 1; i < rhsIndexID; i++ { - // If we find an index which has been dropped but not gc'ed, we - // cannot merge the lhs and rhs spans. - foundDroppedKV, err = checkForKVInBounds(lhsSpan.EndKey, rhsSpan.Key, endTime) - if err != nil { - // If we're unable to check for KVs in bounds, assume that we've found - // one. It's always safe to assume that since we won't merge over this - // span. One possible error is a GC threshold error if this schema - // revision is older than the configured GC window on the span we're - // checking. - log.Warningf(ctx, "error while scanning [%s, %s) @ %v: %v", - lhsSpan.EndKey, rhsSpan.Key, endTime, err) - foundDroppedKV = true - } - // If we find an index that is being added, don't merge the spans. We - // don't want to backup data that is being backfilled until the backfill - // is complete. Even if the backfill has not started yet and there is no - // data we should not back up this span since we want these spans to - // appear as introduced when the index becomes PUBLIC. - // The indexes will appear in introduced spans because indexes - // will never go from PUBLIC to ADDING. - _, foundAddingIndex := addingIndexIDs[i] - if foundDroppedKV || foundAddingIndex { - mergedSpan.EndKey = lhsSpan.EndKey - mergedIndexSpans = append(mergedIndexSpans, mergedSpan) - mergedSpan = rhsSpan - break - } - } - } - - // The loop will terminate after this iteration and so we must update the - // current mergedSpan to encompass the last element in the indexIDs - // slice as well. - if curIndex == len(publicIndexIDs)-2 { - mergedSpan.EndKey = rhsSpan.EndKey - mergedIndexSpans = append(mergedIndexSpans, mergedSpan) - } - } - - return mergedIndexSpans, nil + return publicIndexSpans, nil } // spansForAllTableIndexes returns non-overlapping spans for every index and // table passed in. They would normally overlap if any of them are interleaved. -// The outputted spans are merged as described by the method -// getLogicallyMergedTableSpans, so as to optimize the size/number of the spans -// we BACKUP and lay protected ts records for. +// Overlapping index spans are merged so as to optimize the size/number of the +// spans we BACKUP and lay protected ts records for. func spansForAllTableIndexes( - ctx context.Context, execCfg *sql.ExecutorConfig, - endTime hlc.Timestamp, tables []catalog.TableDescriptor, revs []BackupManifest_DescriptorRevision, ) ([]roachpb.Span, error) { added := make(map[tableAndIndex]bool, len(tables)) sstIntervalTree := interval.NewTree(interval.ExclusiveOverlapper) - var mergedIndexSpans []roachpb.Span + var publicIndexSpans []roachpb.Span var err error - // checkForKVInBounds issues a scan request between start and end at endTime, - // and returns true if a non-nil result is returned. - checkForKVInBounds := func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - var foundKV bool - err := execCfg.DB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { - if err := txn.SetFixedTimestamp(ctx, endTime); err != nil { - return err - } - res, err := txn.Scan(ctx, start, end, 1 /* maxRows */) - if err != nil { - return err - } - foundKV = len(res) != 0 - return nil - }) - return foundKV, err - } - for _, table := range tables { - mergedIndexSpans, err = getLogicallyMergedTableSpans(ctx, table, added, execCfg.Codec, endTime, - checkForKVInBounds) + publicIndexSpans, err = getPublicIndexTableSpans(table, added, execCfg.Codec) if err != nil { return nil, err } - for _, indexSpan := range mergedIndexSpans { + for _, indexSpan := range publicIndexSpans { if err := sstIntervalTree.Insert(intervalSpan(indexSpan), false); err != nil { panic(errors.NewAssertionErrorWithWrappedErrf(err, "IndexSpan")) } @@ -367,14 +211,13 @@ func spansForAllTableIndexes( rawTbl, _, _, _ := descpb.FromDescriptor(rev.Desc) if rawTbl != nil && rawTbl.Public() { tbl := tabledesc.NewBuilder(rawTbl).BuildImmutableTable() - revSpans, err := getLogicallyMergedTableSpans(ctx, tbl, added, execCfg.Codec, rev.Time, - checkForKVInBounds) + revSpans, err := getPublicIndexTableSpans(tbl, added, execCfg.Codec) if err != nil { return nil, err } - mergedIndexSpans = append(mergedIndexSpans, revSpans...) - for _, indexSpan := range mergedIndexSpans { + publicIndexSpans = append(publicIndexSpans, revSpans...) + for _, indexSpan := range publicIndexSpans { if err := sstIntervalTree.Insert(intervalSpan(indexSpan), false); err != nil { panic(errors.NewAssertionErrorWithWrappedErrf(err, "IndexSpan")) } @@ -1009,7 +852,7 @@ func backupPlanHook( } tenants = []descpb.TenantInfoWithUsage{tenantInfo} } else { - tableSpans, err := spansForAllTableIndexes(ctx, p.ExecCfg(), endTime, tables, revs) + tableSpans, err := spansForAllTableIndexes(p.ExecCfg(), tables, revs) if err != nil { return err } @@ -1526,7 +1369,7 @@ func getReintroducedSpans( } } - tableSpans, err := spansForAllTableIndexes(ctx, p.ExecCfg(), endTime, tablesToReinclude, allRevs) + tableSpans, err := spansForAllTableIndexes(p.ExecCfg(), tablesToReinclude, allRevs) if err != nil { return nil, err } diff --git a/pkg/ccl/backupccl/backup_test.go b/pkg/ccl/backupccl/backup_test.go index 6c044f2fcae6..b659ef02b902 100644 --- a/pkg/ccl/backupccl/backup_test.go +++ b/pkg/ccl/backupccl/backup_test.go @@ -6363,7 +6363,8 @@ func TestProtectedTimestampSpanSelectionDuringBackup(t *testing.T) { runner.Exec(t, fmt.Sprintf(`BACKUP DATABASE test INTO '%s'`, baseBackupURI+t.Name())) tableID := getTableID(db, "test", "foo") - require.Equal(t, []string{fmt.Sprintf("/Table/%d/{1-4}", tableID)}, actualResolvedSpans) + require.Equal(t, []string{fmt.Sprintf("/Table/%d/{1-2}", tableID), + fmt.Sprintf("/Table/%d/{3-4}", tableID)}, actualResolvedSpans) runner.Exec(t, "DROP DATABASE test;") actualResolvedSpans = nil }) @@ -6389,6 +6390,27 @@ func TestProtectedTimestampSpanSelectionDuringBackup(t *testing.T) { actualResolvedSpans = nil }) + // This is a regression test for a bug that was fixed in + // https://github.com/cockroachdb/cockroach/pull/72270 where two (or more) + // public indexes followed by an interleaved index would result in index keys + // being missed during backup. + // Prior to the fix in https://github.com/cockroachdb/cockroach/pull/72270, + // the resolved spans would be `/Table/63/{1-3}` thereby missing the span for + // idx2. + // With the change we now backup `/Table/63/{1-4}` to include pkIndex, idx1, + // idx2 and idx3 (since it is interleaved it produces the span + // `/Table/63/{1-2}`). + t.Run("public-and-interleaved-indexes", func(t *testing.T) { + runner.Exec(t, "CREATE DATABASE test; USE test;") + runner.Exec(t, "CREATE TABLE foo (a INT PRIMARY KEY, b INT, v BYTES, INDEX idx1 (v), INDEX idx2(b))") + runner.Exec(t, "CREATE INDEX idx3 ON foo (a, b) INTERLEAVE IN PARENT foo (a)") + runner.Exec(t, fmt.Sprintf(`BACKUP DATABASE test INTO '%s' WITH include_deprecated_interleaves`, baseBackupURI+t.Name())) + tableID := getTableID(db, "test", "foo") + require.Equal(t, []string{fmt.Sprintf("/Table/%d/{1-4}", tableID)}, actualResolvedSpans) + runner.Exec(t, "DROP DATABASE test;") + actualResolvedSpans = nil + }) + t.Run("revs-span-merge", func(t *testing.T) { runner.Exec(t, "CREATE DATABASE test; USE test;") runner.Exec(t, "CREATE TABLE foo (k INT PRIMARY KEY, v BYTES, name STRING, "+ @@ -6501,140 +6523,114 @@ func getMockTableDesc( return tabledesc.NewBuilder(&mockTableDescriptor).BuildImmutableTable() } -// Unit tests for the getLogicallyMergedTableSpans() method. -// TODO(pbardea): Add ADDING and DROPPING indexes to these tests. -func TestLogicallyMergedTableSpans(t *testing.T) { +// Unit tests for the spansForAllTableIndexes and getPublicIndexTableSpans() +// methods. +func TestPublicIndexTableSpans(t *testing.T) { defer leaktest.AfterTest(t)() - ctx := context.Background() codec := keys.TODOSQLCodec + execCfg := &sql.ExecutorConfig{ + Codec: codec, + } unusedMap := make(map[tableAndIndex]bool) testCases := []struct { - name string - checkForKVInBoundsOverride func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) - tableID descpb.ID - pkIndex descpb.IndexDescriptor - indexes []descpb.IndexDescriptor - addingIndexes []descpb.IndexDescriptor - droppingIndexes []descpb.IndexDescriptor - expectedSpans []string + name string + tableID descpb.ID + pkIndex descpb.IndexDescriptor + indexes []descpb.IndexDescriptor + addingIndexes []descpb.IndexDescriptor + droppingIndexes []descpb.IndexDescriptor + expectedSpans []string + expectedMergedSpans []string }{ { - name: "contiguous-spans", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - return false, nil - }, - tableID: 55, - pkIndex: getMockIndexDesc(1), - indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(2)}, - expectedSpans: []string{"/Table/55/{1-3}"}, + name: "contiguous-spans", + tableID: 55, + pkIndex: getMockIndexDesc(1), + indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(2)}, + expectedSpans: []string{"/Table/55/{1-2}", "/Table/55/{2-3}"}, + expectedMergedSpans: []string{"/Table/55/{1-3}"}, }, { - name: "dropped-span-between-two-spans", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - if start.String() == "/Table/56/2" && end.String() == "/Table/56/3" { - return true, nil - } - return false, nil - }, - tableID: 56, - pkIndex: getMockIndexDesc(1), - indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3)}, - droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, - expectedSpans: []string{"/Table/56/{1-2}", "/Table/56/{3-4}"}, + name: "dropped-span-between-two-spans", + tableID: 56, + pkIndex: getMockIndexDesc(1), + indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3)}, + droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, + expectedSpans: []string{"/Table/56/{1-2}", "/Table/56/{3-4}"}, + expectedMergedSpans: []string{"/Table/56/{1-2}", "/Table/56/{3-4}"}, }, { - name: "gced-span-between-two-spans", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - return false, nil - }, - tableID: 57, - pkIndex: getMockIndexDesc(1), - indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3)}, - expectedSpans: []string{"/Table/57/{1-4}"}, + name: "gced-span-between-two-spans", + tableID: 57, + pkIndex: getMockIndexDesc(1), + indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3)}, + expectedSpans: []string{"/Table/57/{1-2}", "/Table/57/{3-4}"}, + expectedMergedSpans: []string{"/Table/57/{1-2}", "/Table/57/{3-4}"}, }, { - name: "alternate-spans-dropped", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - if (start.String() == "/Table/58/2" && end.String() == "/Table/58/3") || - (start.String() == "/Table/58/4" && end.String() == "/Table/58/5") { - return true, nil - } - return false, nil - }, + name: "alternate-spans-dropped", tableID: 58, pkIndex: getMockIndexDesc(1), indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3), getMockIndexDesc(5)}, - droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2), getMockIndexDesc(4)}, - expectedSpans: []string{"/Table/58/{1-2}", "/Table/58/{3-4}", "/Table/58/{5-6}"}, + droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2), getMockIndexDesc(4)}, + expectedSpans: []string{"/Table/58/{1-2}", "/Table/58/{3-4}", "/Table/58/{5-6}"}, + expectedMergedSpans: []string{"/Table/58/{1-2}", "/Table/58/{3-4}", "/Table/58/{5-6}"}, }, { - name: "alternate-spans-gced", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - return false, nil - }, + name: "alternate-spans-gced", tableID: 59, pkIndex: getMockIndexDesc(1), indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3), getMockIndexDesc(5)}, - expectedSpans: []string{"/Table/59/{1-6}"}, + expectedSpans: []string{"/Table/59/{1-2}", "/Table/59/{3-4}", "/Table/59/{5-6}"}, + expectedMergedSpans: []string{"/Table/59/{1-2}", "/Table/59/{3-4}", "/Table/59/{5-6}"}, }, { - name: "one-drop-one-gc", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - if start.String() == "/Table/60/2" && end.String() == "/Table/60/3" { - return true, nil - } - return false, nil - }, + name: "one-drop-one-gc", tableID: 60, pkIndex: getMockIndexDesc(1), indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3), getMockIndexDesc(5)}, - droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, - expectedSpans: []string{"/Table/60/{1-2}", "/Table/60/{3-6}"}, + droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, + expectedSpans: []string{"/Table/60/{1-2}", "/Table/60/{3-4}", "/Table/60/{5-6}"}, + expectedMergedSpans: []string{"/Table/60/{1-2}", "/Table/60/{3-4}", "/Table/60/{5-6}"}, }, { // Although there are no keys on index 2, we should not include its // span since it holds an adding index. - name: "empty-adding-index", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - return false, nil - }, + name: "empty-adding-index", tableID: 61, pkIndex: getMockIndexDesc(1), indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3), getMockIndexDesc(4)}, - addingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, - expectedSpans: []string{"/Table/61/{1-2}", "/Table/61/{3-5}"}, - }, - { - // It is safe to include empty dropped indexes. - name: "empty-dropping-index", - checkForKVInBoundsOverride: func(start, end roachpb.Key, endTime hlc.Timestamp) (bool, error) { - return false, nil - }, - tableID: 62, - pkIndex: getMockIndexDesc(1), - indexes: []descpb.IndexDescriptor{getMockIndexDesc(1), getMockIndexDesc(3), - getMockIndexDesc(4)}, - droppingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, - expectedSpans: []string{"/Table/62/{1-5}"}, + addingIndexes: []descpb.IndexDescriptor{getMockIndexDesc(2)}, + expectedSpans: []string{"/Table/61/{1-2}", "/Table/61/{3-4}", "/Table/61/{4-5}"}, + expectedMergedSpans: []string{"/Table/61/{1-2}", "/Table/61/{3-5}"}, }, } for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - tableDesc := getMockTableDesc(test.tableID, test.pkIndex, - test.indexes, test.addingIndexes, test.droppingIndexes) - spans, err := getLogicallyMergedTableSpans(ctx, tableDesc, unusedMap, codec, - hlc.Timestamp{}, test.checkForKVInBoundsOverride) - var mergedSpans []string + tableDesc := getMockTableDesc(test.tableID, test.pkIndex, + test.indexes, test.addingIndexes, test.droppingIndexes) + t.Run(fmt.Sprintf("%s:%s", "getPublicIndexTableSpans", test.name), func(t *testing.T) { + spans, err := getPublicIndexTableSpans(tableDesc, unusedMap, codec) + require.NoError(t, err) + var unmergedSpans []string for _, span := range spans { - mergedSpans = append(mergedSpans, span.String()) + unmergedSpans = append(unmergedSpans, span.String()) } + require.Equal(t, test.expectedSpans, unmergedSpans) + }) + + t.Run(fmt.Sprintf("%s:%s", "spansForAllTableIndexes", test.name), func(t *testing.T) { + mergedSpans, err := spansForAllTableIndexes(execCfg, []catalog.TableDescriptor{tableDesc}, nil /* revs */) require.NoError(t, err) - require.Equal(t, test.expectedSpans, mergedSpans) + var mergedSpanStrings []string + for _, mSpan := range mergedSpans { + mergedSpanStrings = append(mergedSpanStrings, mSpan.String()) + } + require.Equal(t, test.expectedMergedSpans, mergedSpanStrings) }) } } @@ -8913,3 +8909,57 @@ CREATE TYPE status AS ENUM ('open', 'closed', 'inactive'); RESTORE TABLE foo FROM 'nodelocal://0/foo'; `) } + +// TestGCDropIndexSpanExpansion is a regression test for +// https://github.com/cockroachdb/cockroach/issues/72263. +func TestGCDropIndexSpanExpansion(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + aboutToGC := make(chan struct{}) + allowGC := make(chan struct{}) + var gcJobID jobspb.JobID + baseDir, cleanup := testutils.TempDir(t) + defer cleanup() + ctx := context.Background() + tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{ServerArgs: base.TestServerArgs{ + ExternalIODir: baseDir, + Knobs: base.TestingKnobs{ + GCJob: &sql.GCJobTestingKnobs{RunBeforePerformGC: func(id jobspb.JobID) error { + gcJobID = id + aboutToGC <- struct{}{} + <-allowGC + return nil + }}, + }, + }}) + defer tc.Stopper().Stop(ctx) + sqlRunner := sqlutils.MakeSQLRunner(tc.Conns[0]) + + sqlRunner.Exec(t, ` +CREATE DATABASE test; USE test; +CREATE TABLE foo (id INT PRIMARY KEY, id2 INT, id3 INT, INDEX bar (id2), INDEX baz(id3)); +ALTER TABLE foo CONFIGURE ZONE USING gc.ttlseconds = '1'; +INSERT INTO foo VALUES (1, 2, 3); +DROP INDEX foo@bar; +`) + + // Wait until the index is about to get gc'ed. + <-aboutToGC + + // Take a full backup with revision history so it includes the PUBLIC version + // of index `bar` in the backed up spans. + sqlRunner.Exec(t, `BACKUP INTO 'nodelocal://0/foo' WITH revision_history`) + + // Take an incremental backup with revision history. This backup will not + // include the dropped index span. + sqlRunner.Exec(t, `BACKUP INTO LATEST IN 'nodelocal://0/foo' WITH revision_history`) + + // Allow the GC to complete. + close(allowGC) + + // Wait for the GC to complete. + jobutils.WaitForJob(t, sqlRunner, gcJobID) + + sqlRunner.Exec(t, `BACKUP INTO LATEST IN 'nodelocal://0/foo' WITH revision_history`) +} From ab420f0d74cf0b0956ca600908e014622f688c24 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Mon, 1 Nov 2021 12:58:25 -0400 Subject: [PATCH 149/205] tree: allow string->tuple cast This is a follow-up / forgotten case from b37603815a36a60059afc244ec431ab1398ce9cd No release note, since the note on the other commit covers it. Release note: None --- pkg/sql/logictest/testdata/logic_test/record | 12 +++++++++++- pkg/sql/sem/tree/cast.go | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pkg/sql/logictest/testdata/logic_test/record b/pkg/sql/logictest/testdata/logic_test/record index 802727bba6b1..861ac7a699dd 100644 --- a/pkg/sql/logictest/testdata/logic_test/record +++ b/pkg/sql/logictest/testdata/logic_test/record @@ -149,7 +149,7 @@ CREATE VIEW v AS SELECT (1,'a')::b statement error cannot modify table record type CREATE VIEW v AS SELECT ((1,'a')::b).a -# Test parsing of record types from string literals. +# Test parsing/casting of record types from string literals. query T SELECT COALESCE(ARRAY[ROW(1, 2)], '{}') @@ -170,3 +170,13 @@ query T SELECT COALESCE(NULL::a[], '{"(1, 3)", "(1, 2)"}'); ---- {"(1,\" 3\")","(1,\" 2\")"} + +statement ok +CREATE TABLE strings(s TEXT); +INSERT INTO strings VALUES('(1,2)'), ('(5,6)') + +query TT +SELECT s, s::a FROM strings ORDER BY 1 +---- +(1,2) (1,2) +(5,6) (5,6) diff --git a/pkg/sql/sem/tree/cast.go b/pkg/sql/sem/tree/cast.go index b50712e4908a..782c41635ad3 100644 --- a/pkg/sql/sem/tree/cast.go +++ b/pkg/sql/sem/tree/cast.go @@ -1216,6 +1216,7 @@ var validCasts = []castInfo{ // Casts to TupleFamily. {from: types.UnknownFamily, to: types.TupleFamily, volatility: VolatilityImmutable}, {from: types.TupleFamily, to: types.TupleFamily, volatility: VolatilityStable}, + {from: types.StringFamily, to: types.TupleFamily, volatility: VolatilityStable}, } type castsMapKey struct { @@ -2366,6 +2367,9 @@ func performCastWithoutPrecisionTruncation( } } return ret, nil + case *DString: + res, _, err := ParseDTupleFromString(ctx, string(*v), t) + return res, err } } From e7320e3d90a1f3917c4a7cbb78c24debfd403301 Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Mon, 1 Nov 2021 10:28:52 -0700 Subject: [PATCH 150/205] sqlsmith: don't use crdb_internal.reset_index_usage_stats Release note: None --- pkg/internal/sqlsmith/schema.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/internal/sqlsmith/schema.go b/pkg/internal/sqlsmith/schema.go index 086d5c7f7ad2..411ee5b2cd5e 100644 --- a/pkg/internal/sqlsmith/schema.go +++ b/pkg/internal/sqlsmith/schema.go @@ -493,6 +493,7 @@ var functions = func() map[tree.FunctionClass]map[oid.Oid][]function { "crdb_internal.unsafe_", "crdb_internal.create_join_token", "crdb_internal.reset_multi_region_zone_configs_for_database", + "crdb_internal.reset_index_usage_stats", } { skip = skip || strings.Contains(def.Name, substr) } From 21b124749b1a93621786412bd54b7816a6fb1bf4 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Fri, 29 Oct 2021 11:35:45 -0400 Subject: [PATCH 151/205] sql: type VALUES smarter to handle tuple arrays The VALUES clause previously would take the type of the first non-null element. Now it also searches for the first type that is not an array containing AnyTuple typed elements. This fixes a bug that was causing internal errors with some queries. Release note: none --- pkg/sql/distsql_physical_planner.go | 4 +- pkg/sql/logictest/testdata/logic_test/tuple | 105 ++++++++++++++++++++ pkg/sql/opt/optbuilder/values.go | 9 ++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index ea2db24a1bff..516d35feaeb5 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -248,12 +248,12 @@ func (v *distSQLExprCheckVisitor) VisitPre(expr tree.Expr) (recurse bool, newExp } case *tree.DArray: // We need to check for arrays of untyped tuples here since constant-folding - // on builtin functions sometimes produces this. + // on builtin functions sometimes produces this. DecodeUntaggedDatum + // requires that all the types of the tuple contents are known. if t.ResolvedType().ArrayContents() == types.AnyTuple { v.err = newQueryNotSupportedErrorf("array %s cannot be executed with distsql", t) return false, expr } - } return true, expr } diff --git a/pkg/sql/logictest/testdata/logic_test/tuple b/pkg/sql/logictest/testdata/logic_test/tuple index 847b52e04eee..35d1603c6683 100644 --- a/pkg/sql/logictest/testdata/logic_test/tuple +++ b/pkg/sql/logictest/testdata/logic_test/tuple @@ -979,3 +979,108 @@ UNION SELECT (1::INT, NULL) ---- (1,) + +# Regression test for #70767. Make sure we avoid encoding arrays where the +# array content type is AnyTuple. +subtest regression_70767 + +query T +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[()]))) + AS t(col) +ORDER BY + col ASC +---- +{} +{()} + +query T +SELECT + col +FROM + (VALUES (NULL), ((ARRAY[]::RECORD[])), ((ARRAY[()]))) + AS t(col) +ORDER BY + col ASC +---- +NULL +{} +{()} + +# ARRAY[(1)] is type-checked as an int[] -- this also matches Postgres. +statement error VALUES types int\[\] and tuple\[\] cannot be matched +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[(1)]))) + AS t(col) +ORDER BY + col ASC + +statement error VALUES types tuple{int, string}\[\] and tuple{int, int}\[\] cannot be matched +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[(1,2)])), ((ARRAY[(1,'cat')]))) + AS t(col) +ORDER BY + col ASC + +query T +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[(1,2)]))) + AS t(col) +ORDER BY + col ASC +---- +{} +{"(1,2)"} + +query T +SELECT + col +FROM + (VALUES ((ARRAY[(1,2)])), ((ARRAY[]::RECORD[]))) + AS t(col) +ORDER BY + col ASC +---- +{} +{"(1,2)"} + +query T +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[(1,2), (3,4)]))) + AS t(col) +ORDER BY + col ASC +---- +{} +{"(1,2)","(3,4)"} + +statement error expected tuple \(3, 4, 5\) to have a length of 2 +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[(1,2), (3,4), (3,4,5)]))) + AS t(col) +ORDER BY + col ASC + +query T +SELECT + col +FROM + (VALUES ((ARRAY[]::RECORD[])), ((ARRAY[(1,'cat')]))) + AS t(col) +ORDER BY + col ASC +---- +{} +{"(1,cat)"} diff --git a/pkg/sql/opt/optbuilder/values.go b/pkg/sql/opt/optbuilder/values.go index c98878edd8ae..c7c5c698be2c 100644 --- a/pkg/sql/opt/optbuilder/values.go +++ b/pkg/sql/opt/optbuilder/values.go @@ -78,6 +78,15 @@ func (b *Builder) buildValuesClause( if typ := texpr.ResolvedType(); typ.Family() != types.UnknownFamily { if colTypes[colIdx].Family() == types.UnknownFamily { colTypes[colIdx] = typ + } else if colTypes[colIdx].Family() == types.ArrayFamily && + colTypes[colIdx].ArrayContents() == types.AnyTuple && + typ.Family() == types.ArrayFamily && + typ.ArrayContents().Family() == types.TupleFamily && + typ.ArrayContents() != types.AnyTuple { + // This more complicated condition handles the case when an earlier + // expression in the VALUES clause is an array of AnyTuple, but a + // later expression is an array of a more specific tuple type. + colTypes[colIdx] = typ } else if !typ.Equivalent(colTypes[colIdx]) { panic(pgerror.Newf(pgcode.DatatypeMismatch, "VALUES types %s and %s cannot be matched", typ, colTypes[colIdx])) From b8d4082899098c46ef94db37bca95ce4c3bd82a8 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Sat, 30 Oct 2021 03:30:20 -0400 Subject: [PATCH 152/205] sql: remove dead code for VALUES Release note: none --- pkg/sql/sem/tree/values.go | 12 --- pkg/sql/values.go | 88 -------------------- pkg/sql/values_test.go | 161 ------------------------------------- 3 files changed, 261 deletions(-) diff --git a/pkg/sql/sem/tree/values.go b/pkg/sql/sem/tree/values.go index ce985231dd37..15cac06ba420 100644 --- a/pkg/sql/sem/tree/values.go +++ b/pkg/sql/sem/tree/values.go @@ -36,15 +36,3 @@ func (node *ValuesClause) Format(ctx *FmtCtx) { comma = ", " } } - -// ValuesClauseWithNames is a VALUES clause that has been annotated with column -// names. This is only produced at plan time, never by the parser. It's used to -// pass column names to the VALUES planNode, so it can produce intelligible -// error messages during value type checking. -type ValuesClauseWithNames struct { - ValuesClause - - // Names is a list of the column names that each tuple in the ValuesClause - // corresponds to. - Names NameList -} diff --git a/pkg/sql/values.go b/pkg/sql/values.go index 73572b3a7a1e..6e79ee6a1cc4 100644 --- a/pkg/sql/values.go +++ b/pkg/sql/values.go @@ -12,15 +12,10 @@ package sql import ( "context" - "strconv" "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" - "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" - "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/rowcontainer" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/types" - "github.com/cockroachdb/errors" ) type valuesNode struct { @@ -37,82 +32,6 @@ type valuesNode struct { valuesRun } -// Values implements the VALUES clause. -func (p *planner) Values( - ctx context.Context, origN tree.Statement, desiredTypes []*types.T, -) (planNode, error) { - v := &valuesNode{ - specifiedInQuery: true, - } - - // If we have names, extract them. - var n *tree.ValuesClause - switch t := origN.(type) { - case *tree.ValuesClauseWithNames: - n = &t.ValuesClause - case *tree.ValuesClause: - n = t - default: - return nil, errors.AssertionFailedf("unhandled case in values: %T %v", origN, origN) - } - - if len(n.Rows) == 0 { - return v, nil - } - - numCols := len(n.Rows[0]) - - v.tuples = make([][]tree.TypedExpr, 0, len(n.Rows)) - tupleBuf := make([]tree.TypedExpr, len(n.Rows)*numCols) - - v.columns = make(colinfo.ResultColumns, 0, numCols) - - // We need to save and restore the previous value of the field in - // semaCtx in case we are recursively called within a subquery - // context. - defer p.semaCtx.Properties.Restore(p.semaCtx.Properties) - - // Ensure there are no special functions in the clause. - p.semaCtx.Properties.Require("VALUES", tree.RejectSpecial) - - for num, tuple := range n.Rows { - if a, e := len(tuple), numCols; a != e { - return nil, newValuesListLenErr(e, a) - } - - // Chop off prefix of tupleBuf and limit its capacity. - tupleRow := tupleBuf[:numCols:numCols] - tupleBuf = tupleBuf[numCols:] - - for i, expr := range tuple { - desired := types.Any - if len(desiredTypes) > i { - desired = desiredTypes[i] - } - - // Clear the properties so we can check them below. - typedExpr, err := p.analyzeExpr(ctx, expr, nil, tree.IndexedVarHelper{}, desired, false, "") - if err != nil { - return nil, err - } - - typ := typedExpr.ResolvedType() - if num == 0 { - v.columns = append(v.columns, colinfo.ResultColumn{Name: "column" + strconv.Itoa(i+1), Typ: typ}) - } else if v.columns[i].Typ.Family() == types.UnknownFamily { - v.columns[i].Typ = typ - } else if typ.Family() != types.UnknownFamily && !typ.Equivalent(v.columns[i].Typ) { - return nil, pgerror.Newf(pgcode.DatatypeMismatch, - "VALUES types %s and %s cannot be matched", typ, v.columns[i].Typ) - } - - tupleRow[i] = typedExpr - } - v.tuples = append(v.tuples, tupleRow) - } - return v, nil -} - func (p *planner) newContainerValuesNode(columns colinfo.ResultColumns, capacity int) *valuesNode { return &valuesNode{ columns: columns, @@ -184,10 +103,3 @@ func (n *valuesNode) Close(ctx context.Context) { n.rows = nil } } - -func newValuesListLenErr(exp, got int) error { - return pgerror.Newf( - pgcode.Syntax, - "VALUES lists must all be the same length, expected %d columns, found %d", - exp, got) -} diff --git a/pkg/sql/values_test.go b/pkg/sql/values_test.go index 54b177796f10..557ca98df380 100644 --- a/pkg/sql/values_test.go +++ b/pkg/sql/values_test.go @@ -11,181 +11,20 @@ package sql import ( - "context" - "fmt" - "go/constant" - "reflect" "testing" "time" "github.com/cockroachdb/apd/v2" - "github.com/cockroachdb/cockroach/pkg/base" "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/security" - "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" - "github.com/cockroachdb/cockroach/pkg/sql/catalog/descs" - "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/util/bitarray" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/mon" "github.com/cockroachdb/cockroach/pkg/util/timeutil" - "github.com/cockroachdb/cockroach/pkg/util/uuid" - "github.com/cockroachdb/errors" "github.com/stretchr/testify/require" ) -func makeTestPlanner() *planner { - // Initialize an Executorconfig sufficiently for the purposes of creating a - // planner. - settings := cluster.MakeTestingClusterSettings() - execCfg := ExecutorConfig{ - Settings: settings, - NodeInfo: NodeInfo{ - NodeID: base.TestingIDContainer, - ClusterID: func() uuid.UUID { - return uuid.MakeV4() - }, - }, - RootMemoryMonitor: mon.NewUnlimitedMonitor(context.Background(), "test", mon.MemoryResource, nil, nil, 0, nil), - CollectionFactory: descs.NewCollectionFactory(settings, nil, nil, nil), - } - - // TODO(andrei): pass the cleanup along to the caller. - p, _ /* cleanup */ := NewInternalPlanner( - "test", - nil, /* txn */ - security.RootUserName(), - &MemoryMetrics{}, - &execCfg, - sessiondatapb.SessionData{}, - ) - return p.(*planner) -} - -func TestValues(t *testing.T) { - defer leaktest.AfterTest(t)() - defer log.Scope(t).Close(t) - - p := makeTestPlanner() - - vInt := int64(5) - vNum := 3.14159 - vStr := "two furs one cub" - vBool := true - - unsupp := &tree.RangeCond{} - - intVal := func(v int64) *tree.NumVal { - return tree.NewNumVal( - constant.MakeInt64(v), - "", /* origString */ - false /* negative */) - } - floatVal := func(f float64) *tree.CastExpr { - return &tree.CastExpr{ - Expr: tree.NewNumVal( - constant.MakeFloat64(f), - "", /* origString */ - false /* negative */), - Type: types.Float, - } - } - asRow := func(datums ...tree.Datum) []tree.Datums { - return []tree.Datums{datums} - } - - makeValues := func(tuples ...tree.Exprs) *tree.ValuesClause { - return &tree.ValuesClause{Rows: tuples} - } - makeTuple := func(exprs ...tree.Expr) tree.Exprs { - return tree.Exprs(exprs) - } - - testCases := []struct { - stmt *tree.ValuesClause - rows []tree.Datums - ok bool - }{ - { - makeValues(makeTuple(intVal(vInt))), - asRow(tree.NewDInt(tree.DInt(vInt))), - true, - }, - { - makeValues(makeTuple(intVal(vInt), intVal(vInt))), - asRow(tree.NewDInt(tree.DInt(vInt)), tree.NewDInt(tree.DInt(vInt))), - true, - }, - { - makeValues(makeTuple(floatVal(vNum))), - asRow(tree.NewDFloat(tree.DFloat(vNum))), - true, - }, - { - makeValues(makeTuple(tree.NewDString(vStr))), - asRow(tree.NewDString(vStr)), - true, - }, - { - makeValues(makeTuple(tree.NewDBytes(tree.DBytes(vStr)))), - asRow(tree.NewDBytes(tree.DBytes(vStr))), - true, - }, - { - makeValues(makeTuple(tree.MakeDBool(tree.DBool(vBool)))), - asRow(tree.MakeDBool(tree.DBool(vBool))), - true, - }, - { - makeValues(makeTuple(unsupp)), - nil, - false, - }, - } - - ctx := context.Background() - for i, tc := range testCases { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - plan, err := func() (_ planNode, err error) { - defer func() { - if r := recover(); r != nil { - err = errors.Errorf("%v", r) - } - }() - return p.Values(context.Background(), tc.stmt, nil) - }() - if plan != nil { - defer plan.Close(ctx) - } - if err == nil != tc.ok { - t.Errorf("%d: error_expected=%t, but got error %v", i, tc.ok, err) - } - if plan == nil { - return - } - params := runParams{ctx: ctx, p: p, extendedEvalCtx: &p.extendedEvalCtx} - if err := startExec(params, plan); err != nil { - t.Fatalf("%d: unexpected error in Start: %v", i, err) - } - var rows []tree.Datums - next, err := plan.Next(params) - for ; next; next, err = plan.Next(params) { - rows = append(rows, plan.Values()) - } - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(rows, tc.rows) { - t.Errorf("%d: expected rows:\n%+v\nactual rows:\n%+v", i, tc.rows, rows) - } - }) - } -} - type floatAlias float32 type boolAlias bool type stringAlias string From a5d1526db043b3ed358e3c565b9b04624539587c Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Wed, 20 Oct 2021 14:00:57 -0400 Subject: [PATCH 153/205] Add Parquet to SQL EXPORT parsing logic Previously, the sql package only parsed the EXPORT command for CSV files. This commit adds some very basic logic to process EXPORT for parquet files. This commit doesn't add any user facing options for exporting parquet files, which likely needs more product discovery. The next commit in this PR creates the Export Parquet processor. Release note (sql change): adds logic to process an EXPORT PARQUET command --- pkg/roachpb/io-formats.pb.go | 302 ++++++++++++++----- pkg/roachpb/io-formats.proto | 5 + pkg/sql/distsql_physical_planner.go | 38 ++- pkg/sql/execinfrapb/api.go | 5 + pkg/sql/execinfrapb/processors.pb.go | 226 +++++++++----- pkg/sql/execinfrapb/processors.proto | 1 + pkg/sql/execinfrapb/processors_bulk_io.pb.go | 13 +- pkg/sql/execinfrapb/processors_bulk_io.proto | 23 ++ pkg/sql/export.go | 60 +++- pkg/sql/parser/sql.y | 1 + 10 files changed, 494 insertions(+), 180 deletions(-) diff --git a/pkg/roachpb/io-formats.pb.go b/pkg/roachpb/io-formats.pb.go index 14e890cf6786..73c154f355ae 100644 --- a/pkg/roachpb/io-formats.pb.go +++ b/pkg/roachpb/io-formats.pb.go @@ -164,7 +164,7 @@ func (x *MySQLOutfileOptions_Enclose) UnmarshalJSON(data []byte) error { } func (MySQLOutfileOptions_Enclose) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{2, 0} + return fileDescriptor_16c5b508d081359f, []int{3, 0} } type AvroOptions_Format int32 @@ -210,7 +210,7 @@ func (x *AvroOptions_Format) UnmarshalJSON(data []byte) error { } func (AvroOptions_Format) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{6, 0} + return fileDescriptor_16c5b508d081359f, []int{7, 0} } type IOFileFormat struct { @@ -302,6 +302,40 @@ func (m *CSVOptions) XXX_DiscardUnknown() { var xxx_messageInfo_CSVOptions proto.InternalMessageInfo +type ParquetOptions struct { + // null_encoding, if not nil, is the string which identifies a NULL. Can be the empty string. + NullEncoding *string `protobuf:"bytes,1,opt,name=null_encoding,json=nullEncoding" json:"null_encoding,omitempty"` +} + +func (m *ParquetOptions) Reset() { *m = ParquetOptions{} } +func (m *ParquetOptions) String() string { return proto.CompactTextString(m) } +func (*ParquetOptions) ProtoMessage() {} +func (*ParquetOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_16c5b508d081359f, []int{2} +} +func (m *ParquetOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ParquetOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ParquetOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ParquetOptions.Merge(m, src) +} +func (m *ParquetOptions) XXX_Size() int { + return m.Size() +} +func (m *ParquetOptions) XXX_DiscardUnknown() { + xxx_messageInfo_ParquetOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_ParquetOptions proto.InternalMessageInfo + // MySQLOutfileOptions describe the format of mysql's outfile. type MySQLOutfileOptions struct { // row_separator is the delimiter between rows (mysql's --rows-terminated-by) @@ -328,7 +362,7 @@ func (m *MySQLOutfileOptions) Reset() { *m = MySQLOutfileOptions{} } func (m *MySQLOutfileOptions) String() string { return proto.CompactTextString(m) } func (*MySQLOutfileOptions) ProtoMessage() {} func (*MySQLOutfileOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{2} + return fileDescriptor_16c5b508d081359f, []int{3} } func (m *MySQLOutfileOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -367,7 +401,7 @@ func (m *PgCopyOptions) Reset() { *m = PgCopyOptions{} } func (m *PgCopyOptions) String() string { return proto.CompactTextString(m) } func (*PgCopyOptions) ProtoMessage() {} func (*PgCopyOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{3} + return fileDescriptor_16c5b508d081359f, []int{4} } func (m *PgCopyOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -413,7 +447,7 @@ func (m *PgDumpOptions) Reset() { *m = PgDumpOptions{} } func (m *PgDumpOptions) String() string { return proto.CompactTextString(m) } func (*PgDumpOptions) ProtoMessage() {} func (*PgDumpOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{4} + return fileDescriptor_16c5b508d081359f, []int{5} } func (m *PgDumpOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -448,7 +482,7 @@ func (m *MysqldumpOptions) Reset() { *m = MysqldumpOptions{} } func (m *MysqldumpOptions) String() string { return proto.CompactTextString(m) } func (*MysqldumpOptions) ProtoMessage() {} func (*MysqldumpOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{5} + return fileDescriptor_16c5b508d081359f, []int{6} } func (m *MysqldumpOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -491,7 +525,7 @@ func (m *AvroOptions) Reset() { *m = AvroOptions{} } func (m *AvroOptions) String() string { return proto.CompactTextString(m) } func (*AvroOptions) ProtoMessage() {} func (*AvroOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_16c5b508d081359f, []int{6} + return fileDescriptor_16c5b508d081359f, []int{7} } func (m *AvroOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -523,6 +557,7 @@ func init() { proto.RegisterEnum("cockroach.roachpb.AvroOptions_Format", AvroOptions_Format_name, AvroOptions_Format_value) proto.RegisterType((*IOFileFormat)(nil), "cockroach.roachpb.IOFileFormat") proto.RegisterType((*CSVOptions)(nil), "cockroach.roachpb.CSVOptions") + proto.RegisterType((*ParquetOptions)(nil), "cockroach.roachpb.ParquetOptions") proto.RegisterType((*MySQLOutfileOptions)(nil), "cockroach.roachpb.MySQLOutfileOptions") proto.RegisterType((*PgCopyOptions)(nil), "cockroach.roachpb.PgCopyOptions") proto.RegisterType((*PgDumpOptions)(nil), "cockroach.roachpb.PgDumpOptions") @@ -533,69 +568,70 @@ func init() { func init() { proto.RegisterFile("roachpb/io-formats.proto", fileDescriptor_16c5b508d081359f) } var fileDescriptor_16c5b508d081359f = []byte{ - // 985 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0x16, 0xf5, 0x47, 0x71, 0x24, 0xd9, 0xcc, 0x36, 0x28, 0x88, 0xa0, 0x65, 0x55, 0xa5, 0x29, - 0x9c, 0xb6, 0xa1, 0x01, 0xb7, 0x06, 0x8a, 0x5e, 0x8a, 0x58, 0xb6, 0x1b, 0x07, 0xb6, 0xd4, 0x48, - 0x48, 0x0e, 0xbd, 0x10, 0x2c, 0xb5, 0x96, 0x59, 0x93, 0x5c, 0x7a, 0x97, 0x94, 0xa2, 0x3c, 0x45, - 0x9f, 0xa9, 0x27, 0xf7, 0x96, 0x5b, 0x03, 0x14, 0x28, 0x5a, 0xfb, 0x3d, 0x8a, 0x62, 0x97, 0x4b, - 0x89, 0xb4, 0xd9, 0x24, 0xb7, 0xc1, 0x7c, 0xf3, 0x0d, 0x67, 0xe6, 0x9b, 0x1d, 0x82, 0x41, 0x89, - 0xe3, 0x9e, 0x45, 0x3f, 0x6f, 0x7b, 0xe4, 0xd1, 0x29, 0xa1, 0x81, 0x13, 0x33, 0x2b, 0xa2, 0x24, - 0x26, 0xe8, 0x8e, 0x4b, 0xdc, 0x73, 0x81, 0x5a, 0x32, 0xe6, 0xde, 0xdd, 0x19, 0x99, 0x11, 0x81, - 0x6e, 0x73, 0x2b, 0x0d, 0xec, 0xff, 0xdb, 0x80, 0xce, 0xd1, 0xe8, 0xd0, 0xf3, 0xf1, 0xa1, 0x48, - 0x80, 0x9e, 0x40, 0x33, 0x4d, 0x65, 0x28, 0x3d, 0x65, 0x6b, 0x63, 0xe7, 0x0b, 0xeb, 0x56, 0x2a, - 0x2b, 0x4f, 0xb0, 0xd6, 0xe6, 0x5e, 0xfd, 0xf2, 0xaf, 0x4f, 0x2a, 0x63, 0xc9, 0x47, 0xbb, 0x50, - 0x73, 0xd9, 0xdc, 0xa8, 0xf6, 0x94, 0xad, 0xf6, 0xce, 0xc7, 0x25, 0x69, 0x06, 0x93, 0x17, 0xa3, - 0x28, 0xf6, 0x48, 0xc8, 0x24, 0x93, 0xc7, 0xa3, 0x23, 0xd0, 0x82, 0x25, 0xbb, 0xf0, 0x6d, 0x92, - 0xc4, 0x46, 0x4d, 0x90, 0x3f, 0x2f, 0x21, 0x9f, 0x2c, 0x27, 0xcf, 0x8e, 0x47, 0x49, 0x7c, 0xea, - 0xf9, 0xb8, 0x98, 0xa5, 0x25, 0xe8, 0xa3, 0x24, 0x46, 0xdf, 0x83, 0x1a, 0xcd, 0x6c, 0x97, 0x44, - 0x4b, 0xa3, 0x2e, 0x12, 0xf5, 0x4a, 0x12, 0xfd, 0x38, 0x1b, 0x90, 0x68, 0x59, 0x4c, 0xd1, 0x8c, - 0x84, 0x13, 0x3d, 0x01, 0x48, 0x6b, 0x99, 0x26, 0x41, 0x64, 0x68, 0x22, 0xc7, 0xfd, 0xd2, 0x62, - 0xd8, 0x85, 0xcf, 0x63, 0x8a, 0x69, 0xd2, 0x46, 0xf6, 0x93, 0x20, 0x92, 0xa5, 0x88, 0x34, 0xcd, - 0xb7, 0x94, 0xb2, 0x7f, 0x2b, 0x47, 0x33, 0x12, 0x4e, 0xf4, 0x2d, 0xd4, 0x9d, 0x39, 0x25, 0x46, - 0x4b, 0xb0, 0xcd, 0x12, 0xf6, 0xe3, 0x39, 0x25, 0x45, 0xae, 0x60, 0xa0, 0x09, 0xb4, 0x5d, 0x12, - 0x44, 0x14, 0x33, 0xe6, 0x91, 0xd0, 0x68, 0x08, 0x59, 0xbf, 0x7c, 0x97, 0xac, 0x83, 0x35, 0x45, - 0x66, 0xcb, 0x67, 0x41, 0x0f, 0xa1, 0xcb, 0x9c, 0x39, 0xb6, 0x29, 0xfe, 0x05, 0xbb, 0x31, 0x9e, - 0x1a, 0x6a, 0x4f, 0xd9, 0x6a, 0xc9, 0xc8, 0x0e, 0x87, 0xc6, 0x12, 0xe9, 0x63, 0x80, 0xdc, 0x7e, - 0xb5, 0x41, 0x7d, 0x1e, 0x9e, 0x87, 0x64, 0x11, 0xea, 0x15, 0xa4, 0x42, 0x6d, 0x30, 0x79, 0xa1, - 0x2b, 0x48, 0x87, 0xce, 0x89, 0x54, 0x8d, 0x0b, 0xaa, 0x57, 0x51, 0x17, 0xb4, 0xd5, 0x54, 0xf5, - 0x1a, 0x02, 0x68, 0xa6, 0x42, 0xe9, 0xf5, 0xd4, 0xe6, 0x43, 0xd1, 0x1b, 0xa8, 0x05, 0x75, 0xde, - 0xb7, 0xde, 0xec, 0xef, 0x42, 0x3b, 0x57, 0xb3, 0x00, 0x92, 0x98, 0xe8, 0x15, 0x6e, 0x0d, 0x49, - 0x88, 0x75, 0x85, 0x5b, 0x3f, 0xbc, 0xf2, 0x22, 0xbd, 0xca, 0xad, 0x3d, 0x6e, 0xd5, 0xfa, 0x7f, - 0x2a, 0x00, 0xeb, 0x45, 0x44, 0xf7, 0xa0, 0xe1, 0x92, 0x20, 0x70, 0xc4, 0xf6, 0x37, 0x64, 0x3f, - 0xa9, 0x0b, 0x99, 0xa0, 0x72, 0x03, 0x87, 0xb1, 0x58, 0xea, 0x0c, 0xcd, 0x9c, 0x7c, 0x26, 0x61, - 0xe2, 0xfb, 0x36, 0x0e, 0x5d, 0x32, 0xf5, 0xc2, 0x99, 0xd8, 0x5e, 0x4d, 0x44, 0x29, 0xe3, 0x0e, - 0x87, 0x0e, 0x24, 0x82, 0x0c, 0xa8, 0xb3, 0x73, 0x2f, 0x12, 0x6b, 0xd9, 0xcd, 0xd4, 0xe2, 0x1e, - 0x31, 0xd8, 0x98, 0x7a, 0x6e, 0x6c, 0x5f, 0x24, 0x24, 0xc6, 0x4c, 0xe8, 0xb5, 0x1e, 0xac, 0x80, - 0x9e, 0x09, 0x04, 0x7d, 0x0a, 0x1a, 0x25, 0x0b, 0xdb, 0xf7, 0x02, 0x2f, 0x16, 0x5b, 0x55, 0xcb, - 0x5e, 0x00, 0x25, 0x8b, 0x63, 0xee, 0xed, 0xff, 0x56, 0x83, 0x0f, 0x4a, 0x5e, 0x0a, 0xff, 0x0a, - 0xa7, 0x32, 0x1c, 0x39, 0xd4, 0x89, 0x09, 0x2d, 0xb4, 0xdb, 0xa1, 0x64, 0x31, 0xc9, 0x10, 0xf4, - 0x08, 0x36, 0x4f, 0x3d, 0xec, 0x4f, 0x73, 0xc1, 0xf9, 0xee, 0x37, 0x04, 0xb8, 0x0e, 0x1f, 0x82, - 0x8a, 0x43, 0xd7, 0x27, 0x0c, 0x8b, 0xf6, 0x37, 0x76, 0xac, 0xf7, 0x7b, 0xbc, 0xd6, 0x41, 0xca, - 0xca, 0x86, 0x2a, 0x93, 0xa0, 0x1e, 0xb4, 0xa4, 0x49, 0xc5, 0xb4, 0xb2, 0xef, 0xae, 0xbc, 0xe8, - 0x3e, 0xc0, 0x99, 0xc3, 0x6c, 0xcc, 0x5c, 0x27, 0xc2, 0x85, 0x71, 0x69, 0x67, 0x0e, 0x3b, 0x10, - 0x6e, 0xf4, 0x11, 0x34, 0x65, 0x40, 0x33, 0x97, 0x44, 0xfa, 0x56, 0x72, 0xa8, 0x65, 0x72, 0x14, - 0x35, 0x6d, 0xfd, 0xaf, 0xa6, 0x05, 0x39, 0xa0, 0x54, 0x0e, 0x0b, 0x54, 0xd9, 0x26, 0xd2, 0xa0, - 0x31, 0xc4, 0x73, 0x4c, 0xf5, 0x0a, 0xdf, 0xe7, 0xc7, 0xfe, 0xc2, 0x59, 0x32, 0x5d, 0x41, 0x1d, - 0x68, 0xa5, 0x03, 0x71, 0x7c, 0xbd, 0xfa, 0xb4, 0xde, 0xd2, 0x74, 0xe8, 0x33, 0xe8, 0x16, 0x8e, - 0x14, 0xea, 0x83, 0x36, 0xc5, 0xe2, 0x3b, 0xb8, 0xa8, 0xdc, 0xda, 0xcd, 0x5b, 0xe2, 0xd5, 0x09, - 0xad, 0xb4, 0xac, 0x25, 0xee, 0x41, 0x9f, 0x01, 0x04, 0xce, 0xcb, 0x31, 0x59, 0x4c, 0xbc, 0x57, - 0xa9, 0x48, 0x19, 0x3d, 0xe7, 0xef, 0xff, 0xae, 0xf0, 0xaf, 0xe6, 0xee, 0xd1, 0x0d, 0x9e, 0x52, - 0xce, 0x2b, 0x4e, 0xa1, 0x5a, 0x36, 0x05, 0xf4, 0x35, 0x20, 0x6f, 0x16, 0x12, 0x8a, 0xed, 0x24, - 0x64, 0x49, 0x14, 0x11, 0xca, 0x0f, 0x48, 0x2d, 0x27, 0xdc, 0x9d, 0x14, 0x7f, 0xbe, 0x86, 0xd1, - 0x77, 0xf0, 0xe1, 0x6d, 0x92, 0xed, 0x93, 0x99, 0xd8, 0x8a, 0xac, 0xc3, 0xbb, 0xb7, 0x88, 0xc7, - 0x64, 0xd6, 0xdf, 0x05, 0xfd, 0xe6, 0x85, 0x2e, 0xd6, 0xa9, 0x94, 0xaa, 0xf5, 0x47, 0x15, 0xda, - 0xb9, 0xa3, 0x8a, 0x06, 0x37, 0x7e, 0x8d, 0x0f, 0xde, 0x7e, 0x84, 0xad, 0xd2, 0xbf, 0xe2, 0x03, - 0x68, 0xcb, 0xf7, 0x1d, 0x90, 0x29, 0x16, 0x13, 0xca, 0xba, 0x86, 0x14, 0x38, 0x21, 0x53, 0xcc, - 0x87, 0xcd, 0xdc, 0x33, 0x1c, 0x38, 0x4f, 0x27, 0xa3, 0x61, 0xee, 0x90, 0xf0, 0xa8, 0x95, 0x1f, - 0x7d, 0x05, 0x9b, 0x81, 0xf3, 0xd2, 0xa6, 0xd8, 0x25, 0x74, 0x6a, 0x33, 0xae, 0x4b, 0xfe, 0x8d, - 0x74, 0xb9, 0x2e, 0x02, 0x13, 0xd2, 0x6c, 0x83, 0x9e, 0x45, 0xae, 0x9e, 0x72, 0x23, 0x17, 0xbe, - 0x99, 0xa2, 0xeb, 0xb7, 0xfc, 0x1e, 0x07, 0xe6, 0x1b, 0x68, 0xca, 0xc3, 0xae, 0x42, 0x6d, 0x34, - 0x38, 0xd4, 0x2b, 0x68, 0x13, 0xda, 0x7b, 0x47, 0x43, 0x7b, 0x7c, 0x30, 0x18, 0x8d, 0xf7, 0x27, - 0xe9, 0x71, 0xe7, 0xd5, 0xae, 0x3c, 0xd5, 0xbd, 0x87, 0x97, 0xff, 0x98, 0x95, 0xcb, 0x2b, 0x53, - 0x79, 0x7d, 0x65, 0x2a, 0x6f, 0xae, 0x4c, 0xe5, 0xef, 0x2b, 0x53, 0xf9, 0xf5, 0xda, 0xac, 0xbc, - 0xbe, 0x36, 0x2b, 0x6f, 0xae, 0xcd, 0xca, 0x4f, 0xaa, 0x1c, 0xe8, 0x7f, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x85, 0xf1, 0x64, 0x2c, 0xe4, 0x08, 0x00, 0x00, + // 1003 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x5f, 0x6f, 0xdb, 0x54, + 0x14, 0x8f, 0xf3, 0xcf, 0xf1, 0x49, 0xd2, 0x7a, 0x97, 0x09, 0x59, 0x13, 0x98, 0xe0, 0x31, 0xd4, + 0x01, 0x73, 0xa5, 0x42, 0x25, 0x04, 0x0f, 0x68, 0x4d, 0x5b, 0xd6, 0xa9, 0x4d, 0xb6, 0x44, 0xdb, + 0x03, 0x2f, 0x96, 0x71, 0x6e, 0x53, 0x53, 0xdb, 0xd7, 0xbd, 0xd7, 0x4e, 0x96, 0x7d, 0x0a, 0x3e, + 0x13, 0x4f, 0xe5, 0x6d, 0x6f, 0x4c, 0x42, 0x42, 0xd0, 0x7e, 0x0f, 0x84, 0xee, 0xb5, 0x9d, 0xd8, + 0x8d, 0x37, 0xfa, 0x76, 0x74, 0xfe, 0xfc, 0x7c, 0xce, 0xef, 0x77, 0xee, 0x31, 0x68, 0x94, 0xd8, + 0xce, 0x59, 0xf8, 0xf3, 0xb6, 0x4b, 0x1e, 0x9d, 0x12, 0xea, 0xdb, 0x11, 0x33, 0x43, 0x4a, 0x22, + 0x82, 0xee, 0x38, 0xc4, 0x39, 0x17, 0x51, 0x33, 0xcd, 0xb9, 0x77, 0x77, 0x4a, 0xa6, 0x44, 0x44, + 0xb7, 0xb9, 0x95, 0x24, 0x1a, 0xff, 0x36, 0xa0, 0x73, 0x34, 0x3c, 0x74, 0x3d, 0x7c, 0x28, 0x00, + 0xd0, 0x13, 0x68, 0x26, 0x50, 0x9a, 0xd4, 0x93, 0xb6, 0x36, 0x76, 0xbe, 0x30, 0xd7, 0xa0, 0xcc, + 0x7c, 0x81, 0xb9, 0x32, 0xf7, 0xea, 0x97, 0x7f, 0x7d, 0x52, 0x19, 0xa5, 0xf5, 0x68, 0x17, 0x6a, + 0x0e, 0x9b, 0x69, 0xd5, 0x9e, 0xb4, 0xd5, 0xde, 0xf9, 0xb8, 0x04, 0xa6, 0x3f, 0x7e, 0x39, 0x0c, + 0x23, 0x97, 0x04, 0x2c, 0xad, 0xe4, 0xf9, 0xe8, 0x08, 0x14, 0x7f, 0xc1, 0x2e, 0x3c, 0x8b, 0xc4, + 0x91, 0x56, 0x13, 0xc5, 0x9f, 0x97, 0x14, 0x9f, 0x2c, 0xc6, 0xcf, 0x8f, 0x87, 0x71, 0x74, 0xea, + 0x7a, 0xb8, 0x88, 0xd2, 0x12, 0xe5, 0xc3, 0x38, 0x42, 0x3f, 0x80, 0x1c, 0x4e, 0x2d, 0x87, 0x84, + 0x0b, 0xad, 0x2e, 0x80, 0x7a, 0x25, 0x40, 0xcf, 0xa6, 0x7d, 0x12, 0x2e, 0x8a, 0x10, 0xcd, 0x50, + 0x38, 0xd1, 0x13, 0x80, 0xa4, 0x97, 0x49, 0xec, 0x87, 0x9a, 0x22, 0x30, 0xee, 0x97, 0x36, 0xc3, + 0x2e, 0x3c, 0x9e, 0x53, 0x84, 0x49, 0x06, 0xd9, 0x8f, 0xfd, 0x30, 0x6d, 0x45, 0xc0, 0x34, 0xdf, + 0xd3, 0xca, 0xfe, 0x1a, 0x46, 0x33, 0x14, 0x4e, 0xf4, 0x2d, 0xd4, 0xed, 0x19, 0x25, 0x5a, 0x4b, + 0x54, 0xeb, 0x25, 0xd5, 0x8f, 0x67, 0x94, 0x14, 0x6b, 0x45, 0x05, 0x1a, 0x43, 0xdb, 0x21, 0x7e, + 0x48, 0x31, 0x63, 0x2e, 0x09, 0xb4, 0x86, 0x90, 0xf5, 0xcb, 0xff, 0x93, 0xb5, 0xbf, 0x2a, 0x49, + 0xd1, 0xf2, 0x28, 0xe8, 0x21, 0x74, 0x99, 0x3d, 0xc3, 0x16, 0xc5, 0xbf, 0x60, 0x27, 0xc2, 0x13, + 0x4d, 0xee, 0x49, 0x5b, 0xad, 0x34, 0xb3, 0xc3, 0x43, 0xa3, 0x34, 0x62, 0x60, 0x80, 0xdc, 0x7e, + 0xb5, 0x41, 0x7e, 0x11, 0x9c, 0x07, 0x64, 0x1e, 0xa8, 0x15, 0x24, 0x43, 0xad, 0x3f, 0x7e, 0xa9, + 0x4a, 0x48, 0x85, 0xce, 0x49, 0xaa, 0x1a, 0x17, 0x54, 0xad, 0xa2, 0x2e, 0x28, 0x4b, 0x56, 0xd5, + 0x1a, 0x02, 0x68, 0x26, 0x42, 0xa9, 0xf5, 0xc4, 0xe6, 0xa4, 0xa8, 0x0d, 0xd4, 0x82, 0x3a, 0x9f, + 0x5b, 0x6d, 0x1a, 0xbb, 0xd0, 0xce, 0xf5, 0x2c, 0x02, 0x71, 0x44, 0xd4, 0x0a, 0xb7, 0x06, 0x24, + 0xc0, 0xaa, 0xc4, 0xad, 0x1f, 0x5f, 0xbb, 0xa1, 0x5a, 0xe5, 0xd6, 0x1e, 0xb7, 0x6a, 0xc6, 0x9f, + 0x12, 0xc0, 0x6a, 0x11, 0xd1, 0x3d, 0x68, 0x38, 0xc4, 0xf7, 0x6d, 0xb1, 0xfd, 0x8d, 0x74, 0x9e, + 0xc4, 0x85, 0x74, 0x90, 0xb9, 0x81, 0x83, 0x48, 0x2c, 0x75, 0x16, 0xcd, 0x9c, 0x9c, 0x93, 0x20, + 0xf6, 0x3c, 0x0b, 0x07, 0x0e, 0x99, 0xb8, 0xc1, 0x54, 0x6c, 0xaf, 0x22, 0xb2, 0xa4, 0x51, 0x87, + 0x87, 0x0e, 0xd2, 0x08, 0xd2, 0xa0, 0xce, 0xce, 0xdd, 0x50, 0xac, 0x65, 0x37, 0x53, 0x8b, 0x7b, + 0x04, 0xb1, 0x11, 0x75, 0x9d, 0xc8, 0xba, 0x88, 0x49, 0x84, 0x99, 0xd0, 0x6b, 0x45, 0xac, 0x08, + 0x3d, 0x17, 0x11, 0xf4, 0x29, 0x28, 0x94, 0xcc, 0x2d, 0xcf, 0xf5, 0xdd, 0x48, 0x6c, 0x55, 0x2d, + 0x7b, 0x01, 0x94, 0xcc, 0x8f, 0xb9, 0xd7, 0xf8, 0x1e, 0x36, 0x9e, 0xd9, 0xf4, 0x22, 0xc6, 0x51, + 0x36, 0xe0, 0x5a, 0x93, 0xd2, 0xbb, 0x9a, 0x34, 0x7e, 0xab, 0xc1, 0x07, 0x25, 0xcf, 0x8c, 0x43, + 0xf0, 0xef, 0x32, 0x1c, 0xda, 0xd4, 0x8e, 0x08, 0x2d, 0x70, 0xd5, 0xa1, 0x64, 0x3e, 0xce, 0x22, + 0xe8, 0x11, 0x6c, 0x9e, 0xba, 0xd8, 0x9b, 0xe4, 0x92, 0xf3, 0xd4, 0x6d, 0x88, 0xe0, 0x2a, 0x7d, + 0x00, 0x32, 0x0e, 0x1c, 0x8f, 0x30, 0x2c, 0xb8, 0xdb, 0xd8, 0x31, 0x6f, 0xf7, 0xf2, 0xcd, 0x83, + 0xa4, 0x2a, 0x53, 0x24, 0x05, 0x41, 0x3d, 0x68, 0xa5, 0x26, 0x15, 0x54, 0x67, 0xdf, 0x5d, 0x7a, + 0xd1, 0x7d, 0x80, 0x33, 0x9b, 0x59, 0x98, 0x39, 0x76, 0x88, 0x0b, 0x5c, 0x2b, 0x67, 0x36, 0x3b, + 0x10, 0x6e, 0xf4, 0x11, 0x34, 0xd3, 0x84, 0x66, 0x0e, 0x24, 0xf5, 0x2d, 0xb5, 0x94, 0xcb, 0xb4, + 0x2c, 0x72, 0xdd, 0x7a, 0xe7, 0x42, 0x14, 0xb4, 0x84, 0x52, 0x2d, 0x4d, 0x90, 0xd3, 0x31, 0x91, + 0x02, 0x8d, 0x01, 0x9e, 0x61, 0xaa, 0x56, 0xf8, 0x63, 0x78, 0xec, 0xcd, 0xed, 0x05, 0x53, 0x25, + 0xd4, 0x81, 0x56, 0x42, 0x88, 0xed, 0xa9, 0xd5, 0xa7, 0xf5, 0x96, 0xa2, 0x82, 0xc1, 0xa0, 0x5b, + 0xb8, 0x70, 0xc8, 0x00, 0x65, 0x82, 0xc5, 0x77, 0x70, 0x51, 0xb9, 0x95, 0x9b, 0x8f, 0xc4, 0xbb, + 0x13, 0x5a, 0x29, 0xd9, 0x48, 0xdc, 0x83, 0x3e, 0x03, 0xf0, 0xed, 0x57, 0x23, 0x32, 0x1f, 0xbb, + 0xaf, 0x13, 0x91, 0xb2, 0xf2, 0x9c, 0xdf, 0xf8, 0x5d, 0xe2, 0x5f, 0xcd, 0x1d, 0xb3, 0x1b, 0x75, + 0x52, 0x79, 0x5d, 0x91, 0x85, 0x6a, 0x19, 0x0b, 0xe8, 0x6b, 0x40, 0xee, 0x34, 0x20, 0x14, 0x5b, + 0x71, 0xc0, 0xe2, 0x30, 0x24, 0x94, 0x5f, 0x9f, 0x5a, 0x4e, 0xb8, 0x3b, 0x49, 0xfc, 0xc5, 0x2a, + 0x8c, 0xbe, 0x83, 0x0f, 0xd7, 0x8b, 0x2c, 0x8f, 0x4c, 0xc5, 0x56, 0x64, 0x13, 0xde, 0x5d, 0x2b, + 0x3c, 0x26, 0x53, 0x63, 0x17, 0xd4, 0x9b, 0xe7, 0xbd, 0xd8, 0xa7, 0x54, 0xaa, 0xd6, 0x1f, 0x55, + 0x68, 0xe7, 0x2e, 0x32, 0xea, 0xdf, 0xf8, 0xaf, 0x3e, 0x78, 0xff, 0x05, 0x37, 0x4b, 0x7f, 0xa9, + 0x0f, 0xa0, 0x9d, 0x1e, 0x07, 0x9f, 0x4c, 0xb0, 0x60, 0x28, 0x9b, 0x1a, 0x92, 0xc0, 0x09, 0x99, + 0x60, 0x4e, 0x36, 0x73, 0xce, 0xb0, 0x6f, 0x3f, 0x1d, 0x0f, 0x07, 0xb9, 0x2b, 0xc4, 0xb3, 0x96, + 0x7e, 0xf4, 0x15, 0x6c, 0xfa, 0xf6, 0x2b, 0x8b, 0x62, 0x87, 0xd0, 0x89, 0xc5, 0xb8, 0x2e, 0xf9, + 0x37, 0xd2, 0xe5, 0xba, 0x88, 0x98, 0x90, 0x66, 0x1b, 0xd4, 0x2c, 0x73, 0xf9, 0x94, 0x1b, 0xb9, + 0xf4, 0xcd, 0x24, 0xba, 0x7a, 0xcb, 0xb7, 0xb8, 0x4e, 0xdf, 0x40, 0x33, 0xfd, 0x2b, 0xc8, 0x50, + 0x1b, 0xf6, 0x0f, 0xd5, 0x0a, 0xda, 0x84, 0xf6, 0xde, 0xd1, 0xc0, 0x1a, 0x1d, 0xf4, 0x87, 0xa3, + 0xfd, 0x71, 0xf2, 0x67, 0xe0, 0xdd, 0x2e, 0x3d, 0xd5, 0xbd, 0x87, 0x97, 0xff, 0xe8, 0x95, 0xcb, + 0x2b, 0x5d, 0x7a, 0x73, 0xa5, 0x4b, 0x6f, 0xaf, 0x74, 0xe9, 0xef, 0x2b, 0x5d, 0xfa, 0xf5, 0x5a, + 0xaf, 0xbc, 0xb9, 0xd6, 0x2b, 0x6f, 0xaf, 0xf5, 0xca, 0x4f, 0x72, 0x4a, 0xe8, 0x7f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xbd, 0x9c, 0x7e, 0x1f, 0x21, 0x09, 0x00, 0x00, } func (m *IOFileFormat) Marshal() (dAtA []byte, err error) { @@ -745,6 +781,36 @@ func (m *CSVOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ParquetOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ParquetOptions) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ParquetOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.NullEncoding != nil { + i -= len(*m.NullEncoding) + copy(dAtA[i:], *m.NullEncoding) + i = encodeVarintIoFormats(dAtA, i, uint64(len(*m.NullEncoding))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *MySQLOutfileOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1007,6 +1073,19 @@ func (m *CSVOptions) Size() (n int) { return n } +func (m *ParquetOptions) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NullEncoding != nil { + l = len(*m.NullEncoding) + n += 1 + l + sovIoFormats(uint64(l)) + } + return n +} + func (m *MySQLOutfileOptions) Size() (n int) { if m == nil { return 0 @@ -1572,6 +1651,89 @@ func (m *CSVOptions) Unmarshal(dAtA []byte) error { } return nil } +func (m *ParquetOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIoFormats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ParquetOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ParquetOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NullEncoding", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIoFormats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthIoFormats + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIoFormats + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.NullEncoding = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIoFormats(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIoFormats + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MySQLOutfileOptions) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/roachpb/io-formats.proto b/pkg/roachpb/io-formats.proto index 6be67b21add0..08d67076a980 100644 --- a/pkg/roachpb/io-formats.proto +++ b/pkg/roachpb/io-formats.proto @@ -64,6 +64,11 @@ message CSVOptions { optional int64 row_limit = 6 [(gogoproto.nullable) = false]; } +message ParquetOptions { + // null_encoding, if not nil, is the string which identifies a NULL. Can be the empty string. + optional string null_encoding = 1 [(gogoproto.nullable) = true]; +} + // MySQLOutfileOptions describe the format of mysql's outfile. message MySQLOutfileOptions { enum Enclose { diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index ea2db24a1bff..e764658a177e 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -3881,7 +3881,7 @@ func (dsp *DistSQLPlanner) createPlanForWindow( } // createPlanForExport creates a physical plan for EXPORT. -// We add a new stage of CSVWriter processors to the input plan. +// We add a new stage of [filetype]Writer processors to the input plan. func (dsp *DistSQLPlanner) createPlanForExport( planCtx *PlanningCtx, n *exportNode, ) (*PhysicalPlan, error) { @@ -3889,15 +3889,33 @@ func (dsp *DistSQLPlanner) createPlanForExport( if err != nil { return nil, err } - core := execinfrapb.ProcessorCoreUnion{CSVWriter: &execinfrapb.CSVWriterSpec{ - Destination: n.destination, - NamePattern: n.fileNamePattern, - Options: n.csvOpts, - ChunkRows: int64(n.chunkRows), - ChunkSize: n.chunkSize, - CompressionCodec: n.fileCompression, - UserProto: planCtx.planner.User().EncodeProto(), - }} + + var core execinfrapb.ProcessorCoreUnion + + if n.csvOpts != nil{ + core.CSVWriter = &execinfrapb.CSVWriterSpec{ + Destination: n.destination, + NamePattern: n.fileNamePattern, + Options: *n.csvOpts, + ChunkRows: int64(n.chunkRows), + ChunkSize: n.chunkSize, + CompressionCodec: n.fileCompression, + UserProto: planCtx.planner.User().EncodeProto(), + } + } else if n.parquetOpts != nil{ + core.ParquetWriter = &execinfrapb.ParquetWriterSpec{ + Destination: n.destination, + NamePattern: n.fileNamePattern, + Options: *n.parquetOpts, + ChunkRows: int64(n.chunkRows), + ChunkSize: n.chunkSize, + CompressionCodec: n.fileCompression, + UserProto: planCtx.planner.User().EncodeProto(), + } + } else{ + return nil, errors.Errorf("PARQUET AND CSV OPTS BOTH EMPTY?!?") + } + resTypes := make([]*types.T, len(colinfo.ExportColumns)) for i := range colinfo.ExportColumns { diff --git a/pkg/sql/execinfrapb/api.go b/pkg/sql/execinfrapb/api.go index 2dabbe97feea..6ffc0cf30979 100644 --- a/pkg/sql/execinfrapb/api.go +++ b/pkg/sql/execinfrapb/api.go @@ -72,6 +72,11 @@ func (m *CSVWriterSpec) User() security.SQLUsername { return m.UserProto.Decode() } +// User accesses the user field. +func (m *ParquetWriterSpec) User() security.SQLUsername { + return m.UserProto.Decode() +} + // User accesses the user field. func (m *ReadImportDataSpec) User() security.SQLUsername { return m.UserProto.Decode() diff --git a/pkg/sql/execinfrapb/processors.pb.go b/pkg/sql/execinfrapb/processors.pb.go index 654da5d71c56..c9feae455a53 100644 --- a/pkg/sql/execinfrapb/processors.pb.go +++ b/pkg/sql/execinfrapb/processors.pb.go @@ -139,6 +139,7 @@ type ProcessorCoreUnion struct { Filterer *FiltererSpec `protobuf:"bytes,34,opt,name=filterer" json:"filterer,omitempty"` StreamIngestionData *StreamIngestionDataSpec `protobuf:"bytes,35,opt,name=streamIngestionData" json:"streamIngestionData,omitempty"` StreamIngestionFrontier *StreamIngestionFrontierSpec `protobuf:"bytes,36,opt,name=streamIngestionFrontier" json:"streamIngestionFrontier,omitempty"` + ParquetWriter *ParquetWriterSpec `protobuf:"bytes,37,opt,name=ParquetWriter" json:"ParquetWriter,omitempty"` } func (m *ProcessorCoreUnion) Reset() { *m = ProcessorCoreUnion{} } @@ -325,88 +326,90 @@ func init() { func init() { proto.RegisterFile("sql/execinfrapb/processors.proto", fileDescriptor_d7eecd35e2274091) } var fileDescriptor_d7eecd35e2274091 = []byte{ - // 1295 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x97, 0xdf, 0x72, 0xd3, 0x46, - 0x14, 0xc6, 0xe3, 0x20, 0x12, 0x7b, 0x9d, 0x90, 0xb0, 0x09, 0xb0, 0x4d, 0x5b, 0x27, 0x75, 0xa1, - 0x75, 0x29, 0x98, 0x96, 0xa1, 0xbd, 0x60, 0xda, 0x69, 0xb1, 0x53, 0x26, 0x4a, 0x69, 0xa0, 0x72, - 0x0a, 0x33, 0xdc, 0xb8, 0x1b, 0x69, 0xe3, 0x08, 0x64, 0xad, 0xd8, 0x5d, 0x13, 0xa0, 0xb7, 0x7d, - 0x80, 0x3e, 0x42, 0x1f, 0xa0, 0x0f, 0x92, 0x4b, 0x2e, 0xb9, 0x62, 0x5a, 0xe7, 0x45, 0x3a, 0x7b, - 0x56, 0x96, 0x64, 0x27, 0x96, 0x72, 0xe3, 0x91, 0x57, 0xdf, 0xf7, 0xdb, 0xdd, 0x73, 0xce, 0xfe, - 0x11, 0xda, 0x90, 0x2f, 0x82, 0x5b, 0xec, 0x15, 0x73, 0xfd, 0x70, 0x5f, 0xd0, 0x68, 0xef, 0x56, - 0x24, 0xb8, 0xcb, 0xa4, 0xe4, 0x42, 0x36, 0x23, 0xc1, 0x15, 0xc7, 0xc4, 0xe5, 0xee, 0x73, 0xc1, - 0xa9, 0x7b, 0xd0, 0x94, 0x2f, 0x82, 0xa6, 0xe7, 0x4b, 0x25, 0x5f, 0x04, 0x62, 0x10, 0xae, 0xad, - 0x4d, 0x7a, 0x3d, 0xaa, 0xa8, 0x71, 0xad, 0x5d, 0x9b, 0xce, 0xed, 0xee, 0x51, 0xc9, 0x62, 0xd9, - 0xd5, 0x1c, 0x99, 0xee, 0xcd, 0xa8, 0x1a, 0x79, 0xb0, 0x41, 0xf0, 0xbc, 0xeb, 0xf3, 0x58, 0x79, - 0x23, 0x47, 0xe9, 0x1e, 0xd0, 0xb0, 0xc7, 0xf6, 0x19, 0xf3, 0xe4, 0x19, 0xd4, 0x8a, 0xee, 0x05, - 0xac, 0x2b, 0x15, 0x55, 0x23, 0xf5, 0x25, 0xad, 0x56, 0xaf, 0x23, 0x26, 0xcd, 0x6f, 0xdc, 0xbc, - 0xda, 0xe3, 0x3d, 0x0e, 0x8f, 0xb7, 0xf4, 0x93, 0x69, 0xad, 0xff, 0x69, 0xa1, 0xc5, 0x47, 0x23, - 0x5a, 0x27, 0x62, 0x2e, 0x6e, 0xa3, 0xf3, 0x7e, 0x18, 0x0d, 0x14, 0x29, 0x6d, 0x9c, 0x6b, 0x54, - 0x6f, 0x7f, 0xde, 0x9c, 0x16, 0xd7, 0xa6, 0xad, 0x65, 0x9d, 0xd7, 0xa1, 0xab, 0x7d, 0x2d, 0xeb, - 0xe8, 0xfd, 0xfa, 0x8c, 0x63, 0xbc, 0xf8, 0x3e, 0xb2, 0x5c, 0x2e, 0x18, 0x99, 0xdd, 0x28, 0x35, - 0xaa, 0xb7, 0x6f, 0x4c, 0x67, 0x24, 0x7d, 0xb7, 0xb9, 0x60, 0xbf, 0x85, 0x3e, 0x0f, 0x63, 0x10, - 0xf8, 0x71, 0x1b, 0x59, 0x11, 0x97, 0x8a, 0x58, 0xc0, 0xf9, 0x22, 0x87, 0xc3, 0xa5, 0x8a, 0x59, - 0x99, 0xd1, 0x80, 0x19, 0x6f, 0xa1, 0x39, 0x3e, 0x50, 0x7a, 0x4a, 0xe7, 0x60, 0x4a, 0xd7, 0xa7, - 0x63, 0x1e, 0x82, 0xce, 0xe1, 0x03, 0xc5, 0x44, 0x86, 0x13, 0xfb, 0xf1, 0x75, 0x54, 0x96, 0x8a, - 0xf6, 0x58, 0xd7, 0xf7, 0xc8, 0xf9, 0x8d, 0x52, 0xe3, 0x7c, 0x6b, 0x49, 0xbf, 0x1f, 0xbe, 0x5f, - 0x9f, 0xef, 0xe8, 0x76, 0x7b, 0xd3, 0x99, 0x07, 0x81, 0xed, 0xe1, 0x6f, 0xd1, 0x42, 0x92, 0x26, - 0xad, 0x9f, 0x03, 0xfd, 0x4a, 0xac, 0xaf, 0x26, 0x13, 0xb7, 0x37, 0x9d, 0x6a, 0x22, 0xb4, 0x3d, - 0xfc, 0x3d, 0x5a, 0x10, 0x4c, 0x0e, 0x02, 0xd5, 0x85, 0xec, 0x91, 0x79, 0x18, 0xf3, 0xda, 0xc4, - 0x98, 0x25, 0xeb, 0x37, 0x4d, 0x76, 0x77, 0x9d, 0xaa, 0xd1, 0xef, 0xea, 0xbf, 0xf8, 0x0e, 0x5a, - 0x61, 0x52, 0xf9, 0x7d, 0xaa, 0x98, 0xd7, 0x15, 0xfc, 0xb0, 0xeb, 0xf2, 0x41, 0xa8, 0x48, 0x79, - 0xa3, 0xd4, 0xb0, 0xe2, 0xd9, 0x5c, 0x4c, 0x04, 0x0e, 0x3f, 0x6c, 0xeb, 0xd7, 0xf5, 0x7f, 0x56, - 0x11, 0x3e, 0x99, 0x0a, 0x7c, 0x17, 0x59, 0x21, 0xe7, 0x11, 0x29, 0x41, 0xf8, 0x3f, 0x9b, 0x1e, - 0xb7, 0x1d, 0xce, 0x23, 0x6d, 0xd3, 0x31, 0x73, 0xc0, 0x83, 0x7f, 0x46, 0x55, 0xa8, 0x4d, 0x87, - 0x51, 0x8f, 0x89, 0xb8, 0x12, 0x72, 0x32, 0xb8, 0x9b, 0x8a, 0x81, 0x92, 0x75, 0xe3, 0x2d, 0x84, - 0x9e, 0x71, 0x3f, 0x8c, 0x59, 0xe7, 0x80, 0xd5, 0x98, 0xce, 0xda, 0x4e, 0xb4, 0x80, 0xca, 0x78, - 0xf1, 0x77, 0x68, 0x4e, 0x72, 0xa1, 0x98, 0x88, 0x6b, 0xea, 0xea, 0x74, 0x4a, 0x07, 0x74, 0x40, - 0x88, 0x3d, 0x7a, 0x1c, 0xb4, 0xd7, 0x13, 0xac, 0x47, 0x15, 0x17, 0x50, 0x02, 0xb9, 0xe3, 0xb8, - 0x97, 0x68, 0xcd, 0x38, 0x52, 0x2f, 0x6e, 0xa1, 0xb2, 0x16, 0xfa, 0xa1, 0xab, 0xc8, 0x7c, 0x51, - 0x78, 0x37, 0x63, 0x25, 0x50, 0x12, 0x9f, 0x0e, 0x71, 0x9f, 0x89, 0x1e, 0xd3, 0xd3, 0x65, 0x02, - 0x72, 0x9c, 0x1b, 0xe2, 0x5f, 0x52, 0xb1, 0x09, 0x71, 0xc6, 0xad, 0xa7, 0x76, 0x40, 0xe5, 0x41, - 0xcc, 0xaa, 0x14, 0x4d, 0x6d, 0x2b, 0xd1, 0x9a, 0xa9, 0xa5, 0x5e, 0xfc, 0x23, 0x9a, 0x7b, 0x49, - 0x83, 0x01, 0x93, 0x04, 0x15, 0x51, 0x1e, 0x83, 0x2e, 0xa9, 0x9c, 0xd8, 0xa7, 0xc7, 0xb2, 0x47, - 0xdd, 0xe7, 0xfb, 0x7e, 0x10, 0x30, 0x41, 0xaa, 0x45, 0x94, 0x56, 0xa2, 0x35, 0x63, 0x49, 0xbd, - 0xf8, 0x01, 0x42, 0x82, 0x51, 0xcf, 0xee, 0x47, 0x5c, 0x28, 0xb2, 0x58, 0xb4, 0x1d, 0x39, 0x89, - 0x76, 0x93, 0x2a, 0x6a, 0x68, 0xa9, 0x1f, 0xff, 0x84, 0x2a, 0xed, 0xce, 0xe3, 0x27, 0xc2, 0xd7, - 0xf5, 0xb3, 0x0a, 0xb0, 0x9c, 0xfd, 0x31, 0x91, 0x02, 0x27, 0x75, 0xe2, 0x1f, 0xd0, 0x7c, 0x87, - 0xf6, 0x23, 0x3d, 0xb7, 0x25, 0x80, 0x5c, 0xcb, 0x29, 0x42, 0x23, 0x04, 0xc4, 0xc8, 0x85, 0x9f, - 0xa2, 0x65, 0xf3, 0x98, 0x16, 0x18, 0x59, 0x06, 0x52, 0xb3, 0x88, 0x34, 0x51, 0x92, 0x27, 0x38, - 0xf8, 0x77, 0x84, 0xfb, 0x4c, 0x51, 0x7d, 0x46, 0xee, 0x32, 0xa9, 0x3a, 0x2c, 0xd4, 0x4b, 0x0e, - 0x03, 0xfd, 0xab, 0xbc, 0xda, 0x9a, 0xf4, 0x00, 0xff, 0x14, 0x16, 0xde, 0x47, 0xab, 0xd9, 0x56, - 0x87, 0xb9, 0xcc, 0x7f, 0xc9, 0x04, 0x59, 0x81, 0x3e, 0x6e, 0x9f, 0xad, 0x8f, 0x91, 0x0b, 0x7a, - 0x39, 0x95, 0x87, 0x77, 0xd0, 0xc2, 0x1b, 0xbf, 0xf7, 0x86, 0xf6, 0xe2, 0x9a, 0xbe, 0x04, 0xfc, - 0x9c, 0xdd, 0xff, 0x69, 0x46, 0x0d, 0xdc, 0x31, 0xbf, 0xae, 0xca, 0x48, 0xf0, 0x67, 0xcc, 0x55, - 0x1d, 0xa6, 0xc8, 0xe5, 0xa2, 0xaa, 0x7c, 0x94, 0x68, 0x4d, 0x1d, 0xa5, 0x5e, 0xbd, 0xf8, 0x0f, - 0xfd, 0xd0, 0xe3, 0x87, 0x4c, 0x90, 0x2b, 0x45, 0x8b, 0xff, 0x49, 0xac, 0x34, 0x8b, 0x7f, 0xe4, - 0xc3, 0xbf, 0xa2, 0xc5, 0x80, 0xbb, 0x34, 0x78, 0x14, 0xd0, 0x70, 0x87, 0x7b, 0x8c, 0x10, 0x00, - 0x7d, 0x39, 0x1d, 0xf4, 0x20, 0x2b, 0x07, 0xda, 0x38, 0x41, 0x97, 0x95, 0xb9, 0x7c, 0x64, 0xca, - 0xea, 0x83, 0xa2, 0xb2, 0x6a, 0x4f, 0x38, 0x4c, 0x59, 0x4d, 0x72, 0xf0, 0x2e, 0xba, 0x60, 0xda, - 0xee, 0x0b, 0x1e, 0x2a, 0x9f, 0x09, 0xb2, 0x56, 0xb4, 0x18, 0xdb, 0x63, 0x7a, 0xe0, 0x4e, 0x30, - 0x74, 0x4a, 0xb8, 0xf0, 0xfc, 0x90, 0x06, 0xbe, 0x7a, 0x4d, 0x3e, 0x2c, 0x4a, 0xc9, 0xc3, 0x44, - 0x6b, 0x52, 0x92, 0x7a, 0x75, 0x38, 0xf5, 0x15, 0xcd, 0xe1, 0x87, 0xf1, 0xf2, 0xfe, 0xa8, 0x28, - 0x9c, 0xad, 0xac, 0xdc, 0x84, 0x73, 0x8c, 0xa0, 0xc3, 0xe9, 0x87, 0x2f, 0x99, 0x50, 0xcc, 0xbb, - 0xef, 0x07, 0x8a, 0x09, 0x26, 0xc8, 0xc7, 0x45, 0xe1, 0xb4, 0x27, 0x1c, 0x26, 0x9c, 0x93, 0x1c, - 0x1d, 0xce, 0x51, 0x5b, 0x5c, 0xdd, 0xb5, 0xa2, 0x70, 0xda, 0x63, 0x7a, 0x13, 0xce, 0x71, 0xc6, - 0x68, 0xdf, 0x1d, 0x44, 0x7a, 0xf7, 0x23, 0xeb, 0x67, 0xd9, 0x77, 0x8d, 0x36, 0xdd, 0x77, 0xcd, - 0x7f, 0xfc, 0x04, 0x2d, 0xc9, 0x28, 0xf0, 0xd5, 0xbd, 0xd0, 0xeb, 0xb8, 0x54, 0xe9, 0x80, 0x6e, - 0x00, 0xee, 0x66, 0xce, 0x06, 0x35, 0x6e, 0x00, 0xe6, 0x24, 0x45, 0x9f, 0x79, 0x82, 0x49, 0xc5, - 0x05, 0x83, 0x31, 0x7e, 0x52, 0x74, 0xe6, 0x39, 0xa9, 0xd8, 0x9c, 0x79, 0x19, 0xb7, 0x5e, 0x87, - 0xfb, 0xa3, 0xcc, 0xd4, 0x8b, 0xd6, 0xe1, 0x58, 0x46, 0x12, 0x1f, 0x76, 0xd1, 0x8a, 0x54, 0x82, - 0xd1, 0xbe, 0x1d, 0xf6, 0xf4, 0xc5, 0x8a, 0x87, 0x30, 0xb0, 0x4f, 0x01, 0xf7, 0x75, 0xce, 0x6c, - 0x4f, 0x9a, 0x80, 0x7c, 0x1a, 0x0d, 0x73, 0x74, 0x65, 0xa2, 0x39, 0x59, 0x46, 0x57, 0xa1, 0xa3, - 0x6f, 0xce, 0xdc, 0xd1, 0xd8, 0x7a, 0x9a, 0x46, 0xbd, 0x6b, 0x1d, 0xfd, 0xbd, 0x5e, 0xda, 0xb6, - 0xca, 0x17, 0x96, 0x97, 0xb6, 0xad, 0xf2, 0xc5, 0x65, 0xbc, 0x6d, 0x95, 0xe7, 0x96, 0xe7, 0xb7, - 0xad, 0xf2, 0xc2, 0xf2, 0x62, 0xfd, 0x02, 0x5a, 0xc8, 0xde, 0xf8, 0xea, 0x7f, 0xa0, 0x8b, 0x27, - 0x36, 0x17, 0xdc, 0x40, 0x0b, 0x0e, 0x3f, 0xec, 0xf0, 0x81, 0x70, 0x99, 0xed, 0xbd, 0x82, 0x4b, - 0xe4, 0x62, 0x7c, 0x05, 0x1d, 0x7b, 0x83, 0xeb, 0xa8, 0xb2, 0x33, 0xe8, 0xc3, 0xe7, 0x84, 0x84, - 0x8b, 0xe2, 0x48, 0x96, 0x36, 0x63, 0x82, 0xac, 0x1d, 0xda, 0x67, 0x70, 0xf7, 0xab, 0x8c, 0xae, - 0xf7, 0xba, 0xa5, 0x7e, 0x07, 0x5d, 0x3e, 0xfd, 0xf0, 0xc1, 0x6b, 0x68, 0xd6, 0xf7, 0xa0, 0xdf, - 0x4a, 0x0b, 0xc5, 0x17, 0xef, 0x59, 0x7b, 0xd3, 0x99, 0xf5, 0xbd, 0xfa, 0x16, 0x22, 0xd3, 0x8e, - 0x13, 0x7c, 0x03, 0x21, 0x09, 0x94, 0xae, 0xef, 0x49, 0xf8, 0x0e, 0xaa, 0xb4, 0x16, 0x87, 0xef, - 0xd7, 0x2b, 0x86, 0x6d, 0x6f, 0x4a, 0xa7, 0x62, 0x04, 0xb6, 0x27, 0x5b, 0x37, 0x8f, 0xfe, 0xab, - 0xcd, 0x1c, 0x0d, 0x6b, 0xa5, 0xb7, 0xc3, 0x5a, 0xe9, 0xdd, 0xb0, 0x56, 0xfa, 0x77, 0x58, 0x2b, - 0xfd, 0x75, 0x5c, 0x9b, 0x79, 0x7b, 0x5c, 0x9b, 0x79, 0x77, 0x5c, 0x9b, 0x79, 0x5a, 0xcd, 0x7c, - 0xbb, 0xfd, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x0c, 0xe0, 0x99, 0xbf, 0xca, 0x0e, 0x00, 0x00, + // 1314 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x97, 0xd1, 0x76, 0xdb, 0x34, + 0x18, 0xc7, 0x9b, 0xce, 0x6b, 0x13, 0xa5, 0x5d, 0x3b, 0xb5, 0xdb, 0x44, 0x81, 0xb4, 0x84, 0x0d, + 0xc2, 0xd8, 0x32, 0xd8, 0x19, 0x5c, 0xec, 0xc0, 0x81, 0x25, 0x65, 0xa7, 0x2e, 0xa3, 0x2b, 0x4e, + 0xd9, 0xce, 0xd9, 0x4d, 0x50, 0x6d, 0x35, 0xf5, 0xe6, 0x58, 0xae, 0xa4, 0xac, 0xdb, 0xb8, 0xe5, + 0x01, 0x78, 0x04, 0x1e, 0xa7, 0x97, 0xbb, 0xdc, 0xd5, 0x0e, 0xb4, 0x0f, 0x02, 0x47, 0x9f, 0x1c, + 0xdb, 0x49, 0x1b, 0xbb, 0x37, 0x39, 0x8e, 0xfc, 0xff, 0xff, 0x24, 0x7d, 0xfa, 0x3e, 0x49, 0x46, + 0x6b, 0xf2, 0x20, 0xb8, 0xc3, 0x5e, 0x31, 0xd7, 0x0f, 0xf7, 0x04, 0x8d, 0x76, 0xef, 0x44, 0x82, + 0xbb, 0x4c, 0x4a, 0x2e, 0x64, 0x33, 0x12, 0x5c, 0x71, 0x4c, 0x5c, 0xee, 0xbe, 0x10, 0x9c, 0xba, + 0xfb, 0x4d, 0x79, 0x10, 0x34, 0x3d, 0x5f, 0x2a, 0x79, 0x10, 0x88, 0x41, 0xb8, 0xb2, 0x32, 0xee, + 0xf5, 0xa8, 0xa2, 0xc6, 0xb5, 0x72, 0x63, 0x32, 0xb7, 0xbb, 0x4b, 0x25, 0x8b, 0x65, 0xd7, 0x73, + 0x64, 0xba, 0x37, 0xa3, 0x6a, 0xe4, 0xc1, 0x06, 0xc1, 0x8b, 0xae, 0xcf, 0x63, 0xe5, 0xad, 0x1c, + 0xa5, 0xbb, 0x4f, 0xc3, 0x1e, 0xdb, 0x63, 0xcc, 0x93, 0xe7, 0x50, 0x2b, 0xba, 0x1b, 0xb0, 0xae, + 0x54, 0x54, 0x0d, 0xd5, 0x57, 0xb4, 0x5a, 0xbd, 0x8e, 0x98, 0x34, 0xbf, 0x71, 0xf3, 0x72, 0x8f, + 0xf7, 0x38, 0x3c, 0xde, 0xd1, 0x4f, 0xa6, 0xb5, 0xfe, 0xa7, 0x85, 0xe6, 0xb7, 0x87, 0xb4, 0x4e, + 0xc4, 0x5c, 0xdc, 0x46, 0x17, 0xfd, 0x30, 0x1a, 0x28, 0x52, 0x5a, 0xbb, 0xd0, 0xa8, 0xde, 0xfd, + 0xbc, 0x39, 0x29, 0xae, 0x4d, 0x5b, 0xcb, 0x3a, 0xaf, 0x43, 0x57, 0xfb, 0x5a, 0xd6, 0xd1, 0xfb, + 0xd5, 0x29, 0xc7, 0x78, 0xf1, 0x43, 0x64, 0xb9, 0x5c, 0x30, 0x32, 0xbd, 0x56, 0x6a, 0x54, 0xef, + 0xde, 0x9a, 0xcc, 0x48, 0xfa, 0x6e, 0x73, 0xc1, 0x7e, 0x0b, 0x7d, 0x1e, 0xc6, 0x20, 0xf0, 0xe3, + 0x36, 0xb2, 0x22, 0x2e, 0x15, 0xb1, 0x80, 0xf3, 0x45, 0x0e, 0x87, 0x4b, 0x15, 0xb3, 0x32, 0xa3, + 0x01, 0x33, 0xde, 0x40, 0x33, 0x7c, 0xa0, 0xf4, 0x94, 0x2e, 0xc0, 0x94, 0x6e, 0x4e, 0xc6, 0x3c, + 0x06, 0x9d, 0xc3, 0x07, 0x8a, 0x89, 0x0c, 0x27, 0xf6, 0xe3, 0x9b, 0xa8, 0x2c, 0x15, 0xed, 0xb1, + 0xae, 0xef, 0x91, 0x8b, 0x6b, 0xa5, 0xc6, 0xc5, 0xd6, 0x82, 0x7e, 0x7f, 0xfc, 0x7e, 0x75, 0xb6, + 0xa3, 0xdb, 0xed, 0x75, 0x67, 0x16, 0x04, 0xb6, 0x87, 0xbf, 0x45, 0x73, 0xc9, 0x32, 0x69, 0xfd, + 0x0c, 0xe8, 0x97, 0x62, 0x7d, 0x35, 0x99, 0xb8, 0xbd, 0xee, 0x54, 0x13, 0xa1, 0xed, 0xe1, 0xef, + 0xd1, 0x9c, 0x60, 0x72, 0x10, 0xa8, 0x2e, 0xac, 0x1e, 0x99, 0x85, 0x31, 0xaf, 0x8c, 0x8d, 0x59, + 0xb2, 0x7e, 0xd3, 0xac, 0xee, 0x8e, 0x53, 0x35, 0xfa, 0x1d, 0xfd, 0x17, 0xdf, 0x43, 0x4b, 0x4c, + 0x2a, 0xbf, 0x4f, 0x15, 0xf3, 0xba, 0x82, 0x1f, 0x76, 0x5d, 0x3e, 0x08, 0x15, 0x29, 0xaf, 0x95, + 0x1a, 0x56, 0x3c, 0x9b, 0xcb, 0x89, 0xc0, 0xe1, 0x87, 0x6d, 0xfd, 0xba, 0xfe, 0xdf, 0x32, 0xc2, + 0xa7, 0x97, 0x02, 0xdf, 0x47, 0x56, 0xc8, 0x79, 0x44, 0x4a, 0x10, 0xfe, 0xcf, 0x26, 0xc7, 0x6d, + 0x8b, 0xf3, 0x48, 0xdb, 0x74, 0xcc, 0x1c, 0xf0, 0xe0, 0x9f, 0x51, 0x15, 0x72, 0xd3, 0x61, 0xd4, + 0x63, 0x22, 0xce, 0x84, 0x9c, 0x15, 0xdc, 0x49, 0xc5, 0x40, 0xc9, 0xba, 0xf1, 0x06, 0x42, 0xcf, + 0xb9, 0x1f, 0xc6, 0xac, 0x0b, 0xc0, 0x6a, 0x4c, 0x66, 0x6d, 0x26, 0x5a, 0x40, 0x65, 0xbc, 0xf8, + 0x3b, 0x34, 0x23, 0xb9, 0x50, 0x4c, 0xc4, 0x39, 0x75, 0x7d, 0x32, 0xa5, 0x03, 0x3a, 0x20, 0xc4, + 0x1e, 0x3d, 0x0e, 0xda, 0xeb, 0x09, 0xd6, 0xa3, 0x8a, 0x0b, 0x48, 0x81, 0xdc, 0x71, 0x3c, 0x48, + 0xb4, 0x66, 0x1c, 0xa9, 0x17, 0xb7, 0x50, 0x59, 0x0b, 0xfd, 0xd0, 0x55, 0x64, 0xb6, 0x28, 0xbc, + 0xeb, 0xb1, 0x12, 0x28, 0x89, 0x4f, 0x87, 0xb8, 0xcf, 0x44, 0x8f, 0xe9, 0xe9, 0x32, 0x01, 0x6b, + 0x9c, 0x1b, 0xe2, 0x5f, 0x52, 0xb1, 0x09, 0x71, 0xc6, 0xad, 0xa7, 0xb6, 0x4f, 0xe5, 0x7e, 0xcc, + 0xaa, 0x14, 0x4d, 0x6d, 0x23, 0xd1, 0x9a, 0xa9, 0xa5, 0x5e, 0xfc, 0x23, 0x9a, 0x79, 0x49, 0x83, + 0x01, 0x93, 0x04, 0x15, 0x51, 0x9e, 0x80, 0x2e, 0xc9, 0x9c, 0xd8, 0xa7, 0xc7, 0xb2, 0x4b, 0xdd, + 0x17, 0x7b, 0x7e, 0x10, 0x30, 0x41, 0xaa, 0x45, 0x94, 0x56, 0xa2, 0x35, 0x63, 0x49, 0xbd, 0xf8, + 0x11, 0x42, 0x82, 0x51, 0xcf, 0xee, 0x47, 0x5c, 0x28, 0x32, 0x5f, 0xb4, 0x1d, 0x39, 0x89, 0x76, + 0x9d, 0x2a, 0x6a, 0x68, 0xa9, 0x1f, 0xff, 0x84, 0x2a, 0xed, 0xce, 0x93, 0xa7, 0xc2, 0xd7, 0xf9, + 0xb3, 0x0c, 0xb0, 0x9c, 0xfd, 0x31, 0x91, 0x02, 0x27, 0x75, 0xe2, 0x1f, 0xd0, 0x6c, 0x87, 0xf6, + 0x23, 0x3d, 0xb7, 0x05, 0x80, 0xdc, 0xc8, 0x49, 0x42, 0x23, 0x04, 0xc4, 0xd0, 0x85, 0x9f, 0xa1, + 0x45, 0xf3, 0x98, 0x26, 0x18, 0x59, 0x04, 0x52, 0xb3, 0x88, 0x34, 0x96, 0x92, 0xa7, 0x38, 0xf8, + 0x77, 0x84, 0xfb, 0x4c, 0x51, 0x7d, 0x46, 0xee, 0x30, 0xa9, 0x3a, 0x2c, 0xd4, 0x25, 0x87, 0x81, + 0xfe, 0x55, 0x5e, 0x6e, 0x8d, 0x7b, 0x80, 0x7f, 0x06, 0x0b, 0xef, 0xa1, 0xe5, 0x6c, 0xab, 0xc3, + 0x5c, 0xe6, 0xbf, 0x64, 0x82, 0x2c, 0x41, 0x1f, 0x77, 0xcf, 0xd7, 0xc7, 0xd0, 0x05, 0xbd, 0x9c, + 0xc9, 0xc3, 0x5b, 0x68, 0xee, 0x8d, 0xdf, 0x7b, 0x43, 0x7b, 0x71, 0x4e, 0x5f, 0x01, 0x7e, 0xce, + 0xee, 0xff, 0x2c, 0xa3, 0x06, 0xee, 0x88, 0x5f, 0x67, 0x65, 0x24, 0xf8, 0x73, 0xe6, 0xaa, 0x0e, + 0x53, 0xe4, 0x6a, 0x51, 0x56, 0x6e, 0x27, 0x5a, 0x93, 0x47, 0xa9, 0x57, 0x17, 0xff, 0xa1, 0x1f, + 0x7a, 0xfc, 0x90, 0x09, 0x72, 0xad, 0xa8, 0xf8, 0x9f, 0xc6, 0x4a, 0x53, 0xfc, 0x43, 0x1f, 0xfe, + 0x15, 0xcd, 0x07, 0xdc, 0xa5, 0xc1, 0x76, 0x40, 0xc3, 0x2d, 0xee, 0x31, 0x42, 0x00, 0xf4, 0xe5, + 0x64, 0xd0, 0xa3, 0xac, 0x1c, 0x68, 0xa3, 0x04, 0x9d, 0x56, 0xe6, 0xf2, 0x91, 0x49, 0xab, 0x0f, + 0x8a, 0xd2, 0xaa, 0x3d, 0xe6, 0x30, 0x69, 0x35, 0xce, 0xc1, 0x3b, 0xe8, 0x92, 0x69, 0x7b, 0x28, + 0x78, 0xa8, 0x7c, 0x26, 0xc8, 0x4a, 0x51, 0x31, 0xb6, 0x47, 0xf4, 0xc0, 0x1d, 0x63, 0xe8, 0x25, + 0xe1, 0xc2, 0xf3, 0x43, 0x1a, 0xf8, 0xea, 0x35, 0xf9, 0xb0, 0x68, 0x49, 0x1e, 0x27, 0x5a, 0xb3, + 0x24, 0xa9, 0x57, 0x87, 0x53, 0x5f, 0xd1, 0x1c, 0x7e, 0x18, 0x97, 0xf7, 0x47, 0x45, 0xe1, 0x6c, + 0x65, 0xe5, 0x26, 0x9c, 0x23, 0x04, 0x1d, 0x4e, 0x3f, 0x7c, 0xc9, 0x84, 0x62, 0xde, 0x43, 0x3f, + 0x50, 0x4c, 0x30, 0x41, 0x3e, 0x2e, 0x0a, 0xa7, 0x3d, 0xe6, 0x30, 0xe1, 0x1c, 0xe7, 0xe8, 0x70, + 0x0e, 0xdb, 0xe2, 0xec, 0xae, 0x15, 0x85, 0xd3, 0x1e, 0xd1, 0x9b, 0x70, 0x8e, 0x32, 0x86, 0xfb, + 0xee, 0x20, 0xd2, 0xbb, 0x1f, 0x59, 0x3d, 0xcf, 0xbe, 0x6b, 0xb4, 0xe9, 0xbe, 0x6b, 0xfe, 0xe3, + 0xa7, 0x68, 0x41, 0x46, 0x81, 0xaf, 0x1e, 0x84, 0x5e, 0xc7, 0xa5, 0x4a, 0x07, 0x74, 0x0d, 0x70, + 0xb7, 0x73, 0x36, 0xa8, 0x51, 0x03, 0x30, 0xc7, 0x29, 0xfa, 0xcc, 0x13, 0x4c, 0x2a, 0x2e, 0x18, + 0x8c, 0xf1, 0x93, 0xa2, 0x33, 0xcf, 0x49, 0xc5, 0xe6, 0xcc, 0xcb, 0xb8, 0x75, 0x1d, 0xee, 0x0d, + 0x57, 0xa6, 0x5e, 0x54, 0x87, 0x23, 0x2b, 0x92, 0xf8, 0xb0, 0x8b, 0x96, 0xa4, 0x12, 0x8c, 0xf6, + 0xed, 0xb0, 0xa7, 0x2f, 0x56, 0x3c, 0x84, 0x81, 0x7d, 0x0a, 0xb8, 0xaf, 0x73, 0x66, 0x7b, 0xda, + 0x04, 0xe4, 0xb3, 0x68, 0x98, 0xa3, 0x6b, 0x63, 0xcd, 0x49, 0x19, 0x5d, 0x87, 0x8e, 0xbe, 0x39, + 0x77, 0x47, 0x23, 0xf5, 0x34, 0x89, 0xaa, 0xcb, 0x61, 0x9b, 0x8a, 0x83, 0x01, 0x53, 0x71, 0x39, + 0xdc, 0x28, 0x2a, 0x87, 0x11, 0xb9, 0x29, 0x87, 0x91, 0xa6, 0xfb, 0xd6, 0xd1, 0xdf, 0xab, 0xa5, + 0x4d, 0xab, 0x7c, 0x69, 0x71, 0x61, 0xd3, 0x2a, 0x5f, 0x5e, 0xc4, 0x9b, 0x56, 0x79, 0x66, 0x71, + 0x76, 0xd3, 0x2a, 0xcf, 0x2d, 0xce, 0xd7, 0x2f, 0xa1, 0xb9, 0xec, 0x25, 0xb2, 0xfe, 0x07, 0xba, + 0x7c, 0x6a, 0xbf, 0xc2, 0x0d, 0x34, 0xe7, 0xf0, 0xc3, 0x0e, 0x1f, 0x08, 0x97, 0xd9, 0xde, 0x2b, + 0xb8, 0x97, 0xce, 0xc7, 0xb7, 0xda, 0x91, 0x37, 0xb8, 0x8e, 0x2a, 0x5b, 0x83, 0x3e, 0x7c, 0xa1, + 0x48, 0xb8, 0x7b, 0x0e, 0x65, 0x69, 0x33, 0x26, 0xc8, 0xda, 0xa2, 0x7d, 0x06, 0xd7, 0xc9, 0xca, + 0xf0, 0x8b, 0x41, 0xb7, 0xd4, 0xef, 0xa1, 0xab, 0x67, 0x9f, 0x67, 0x78, 0x05, 0x4d, 0xfb, 0x1e, + 0xf4, 0x5b, 0x69, 0xa1, 0xf8, 0x2e, 0x3f, 0x6d, 0xaf, 0x3b, 0xd3, 0xbe, 0x57, 0xdf, 0x40, 0x64, + 0xd2, 0x09, 0x85, 0x6f, 0x21, 0x24, 0x81, 0xd2, 0xf5, 0x3d, 0x09, 0x9f, 0x56, 0x95, 0xd6, 0xfc, + 0xf1, 0xfb, 0xd5, 0x8a, 0x61, 0xdb, 0xeb, 0xd2, 0xa9, 0x18, 0x81, 0xed, 0xc9, 0xd6, 0xed, 0xa3, + 0x7f, 0x6b, 0x53, 0x47, 0xc7, 0xb5, 0xd2, 0xdb, 0xe3, 0x5a, 0xe9, 0xdd, 0x71, 0xad, 0xf4, 0xcf, + 0x71, 0xad, 0xf4, 0xd7, 0x49, 0x6d, 0xea, 0xed, 0x49, 0x6d, 0xea, 0xdd, 0x49, 0x6d, 0xea, 0x59, + 0x35, 0xf3, 0x39, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2c, 0xdc, 0x03, 0x68, 0x1d, 0x0f, + 0x00, 0x00, } func (m *ProcessorSpec) Marshal() (dAtA []byte, err error) { @@ -523,6 +526,20 @@ func (m *ProcessorCoreUnion) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ParquetWriter != nil { + { + size, err := m.ParquetWriter.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProcessors(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xaa + } if m.StreamIngestionFrontier != nil { { size, err := m.StreamIngestionFrontier.MarshalToSizedBuffer(dAtA[:i]) @@ -1246,6 +1263,10 @@ func (m *ProcessorCoreUnion) Size() (n int) { l = m.StreamIngestionFrontier.Size() n += 2 + l + sovProcessors(uint64(l)) } + if m.ParquetWriter != nil { + l = m.ParquetWriter.Size() + n += 2 + l + sovProcessors(uint64(l)) + } return n } @@ -1400,6 +1421,9 @@ func (this *ProcessorCoreUnion) GetValue() interface{} { if this.StreamIngestionFrontier != nil { return this.StreamIngestionFrontier } + if this.ParquetWriter != nil { + return this.ParquetWriter + } return nil } @@ -1469,6 +1493,8 @@ func (this *ProcessorCoreUnion) SetValue(value interface{}) bool { this.StreamIngestionData = vt case *StreamIngestionFrontierSpec: this.StreamIngestionFrontier = vt + case *ParquetWriterSpec: + this.ParquetWriter = vt default: return false } @@ -2930,6 +2956,42 @@ func (m *ProcessorCoreUnion) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 37: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ParquetWriter", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessors + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProcessors + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProcessors + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ParquetWriter == nil { + m.ParquetWriter = &ParquetWriterSpec{} + } + if err := m.ParquetWriter.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipProcessors(dAtA[iNdEx:]) diff --git a/pkg/sql/execinfrapb/processors.proto b/pkg/sql/execinfrapb/processors.proto index 6cb1f5911a1e..30028248fd21 100644 --- a/pkg/sql/execinfrapb/processors.proto +++ b/pkg/sql/execinfrapb/processors.proto @@ -123,6 +123,7 @@ message ProcessorCoreUnion { optional FiltererSpec filterer = 34; optional StreamIngestionDataSpec streamIngestionData = 35; optional StreamIngestionFrontierSpec streamIngestionFrontier = 36; + optional ParquetWriterSpec ParquetWriter = 37; reserved 6, 12; } diff --git a/pkg/sql/execinfrapb/processors_bulk_io.pb.go b/pkg/sql/execinfrapb/processors_bulk_io.pb.go index c8e3912540ee..99ed718c9ce9 100644 --- a/pkg/sql/execinfrapb/processors_bulk_io.pb.go +++ b/pkg/sql/execinfrapb/processors_bulk_io.pb.go @@ -10,6 +10,11 @@ package execinfrapb import ( encoding_binary "encoding/binary" fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + time "time" + _ "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" github_com_cockroachdb_cockroach_pkg_jobs_jobspb "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" roachpb "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -20,10 +25,6 @@ import ( _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" - io "io" - math "math" - math_bits "math/bits" - time "time" ) // Reference imports to suppress errors if they are not otherwise used. @@ -660,7 +661,9 @@ func (*SplitAndScatterSpec_RestoreEntryChunk) Descriptor() ([]byte, []int) { func (m *SplitAndScatterSpec_RestoreEntryChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *SplitAndScatterSpec_RestoreEntryChunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *SplitAndScatterSpec_RestoreEntryChunk) XXX_Marshal( + b []byte, deterministic bool, +) ([]byte, error) { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { diff --git a/pkg/sql/execinfrapb/processors_bulk_io.proto b/pkg/sql/execinfrapb/processors_bulk_io.proto index d009574076ed..a00c469994d1 100644 --- a/pkg/sql/execinfrapb/processors_bulk_io.proto +++ b/pkg/sql/execinfrapb/processors_bulk_io.proto @@ -284,6 +284,29 @@ message CSVWriterSpec { optional string user_proto = 6 [(gogoproto.nullable) = false, (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/security.SQLUsernameProto"]; } +// ParquetWriterSpec is the specification for a processor that consumes rows and +// writes them to CSV files at uri. It outputs a row per file written with +// the file name, row count and byte size. +message ParquetWriterSpec { + // destination as a cloud.ExternalStorage URI pointing to an export store + // location (directory). + optional string destination = 1 [(gogoproto.nullable) = false]; + optional string name_pattern = 2 [(gogoproto.nullable) = false]; + optional roachpb.ParquetOptions options = 3 [(gogoproto.nullable) = false]; + //optional roachpb.CSVOptions options = 3 [(gogoproto.nullable) = false]; + // chunk_rows is num rows to write per file. 0 = no limit. + optional int64 chunk_rows = 4 [(gogoproto.nullable) = false]; + // chunk_size is the target byte size per file. + optional int64 chunk_size = 7 [(gogoproto.nullable) = false]; + + // compression_codec specifies compression used for exported file. + optional FileCompression compression_codec = 5 [(gogoproto.nullable) = false]; + + // User who initiated the export. This is used to check access privileges + // when using FileTable ExternalStorage. + optional string user_proto = 6 [(gogoproto.nullable) = false, (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/security.SQLUsernameProto"]; +} + // BulkRowWriterSpec is the specification for a processor that consumes rows and // writes them to a target table using AddSSTable. It outputs a BulkOpSummary. message BulkRowWriterSpec { diff --git a/pkg/sql/export.go b/pkg/sql/export.go index 87bf403592cc..800315a18bd4 100644 --- a/pkg/sql/export.go +++ b/pkg/sql/export.go @@ -13,6 +13,7 @@ package sql import ( "context" "fmt" + "github.com/cockroachdb/cockroach/pkg/util" "strconv" "strings" @@ -25,7 +26,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" "github.com/cockroachdb/errors" ) @@ -41,7 +41,8 @@ type exportNode struct { // fileNamePattern represents the file naming pattern for the // export, typically to be appended to the destination URI fileNamePattern string - csvOpts roachpb.CSVOptions + csvOpts *roachpb.CSVOptions + parquetOpts *roachpb.ParquetOptions chunkRows int chunkSize int64 fileCompression execinfrapb.FileCompression @@ -84,8 +85,9 @@ var exportOptionExpectValues = map[string]KVStringOptValidate{ const exportChunkSizeDefault = int64(32 << 20) // 32 MB const exportChunkRowsDefault = 100000 const exportFilePatternPart = "%part%" -const exportFilePatternDefault = exportFilePatternPart + ".csv" const exportCompressionCodec = "gzip" +const csvSuffix = "csv" +const parquetSuffix = "parquet" // featureExportEnabled is used to enable and disable the EXPORT feature. var featureExportEnabled = settings.RegisterBoolSetting( @@ -118,7 +120,7 @@ func (ef *execFactory) ConstructExport( return nil, errors.Errorf("EXPORT cannot be used inside a transaction") } - if fileFormat != "CSV" { + if fileFormat != "CSV" && fileFormat != "PARQUET" { return nil, errors.Errorf("unsupported export format: %q", fileFormat) } @@ -152,17 +154,49 @@ func (ef *execFactory) ConstructExport( return nil, err } - csvOpts := roachpb.CSVOptions{} + var exportFilePattern string - if override, ok := optVals[exportOptionDelimiter]; ok { - csvOpts.Comma, err = util.GetSingleRune(override) - if err != nil { - return nil, pgerror.New(pgcode.InvalidParameterValue, "invalid delimiter") + // evaluate csvOpts + var csvOpts *roachpb.CSVOptions + if err := func () error { + if fileFormat == "CSV" { + csvOpts = &roachpb.CSVOptions{} + } else{ + return nil + } + if override, ok := optVals[exportOptionDelimiter]; ok { + csvOpts.Comma, err = util.GetSingleRune(override) + if err != nil { + return pgerror.New(pgcode.InvalidParameterValue, "invalid delimiter") + } } + + if override, ok := optVals[exportOptionNullAs]; ok { + csvOpts.NullEncoding = &override + } + exportFilePattern = exportFilePatternPart+"."+csvSuffix + + return nil + }(); err != nil { + return nil, err } - if override, ok := optVals[exportOptionNullAs]; ok { - csvOpts.NullEncoding = &override + // evaluate parquetOpts + var parquetOpts *roachpb.ParquetOptions + if err := func () error { + if fileFormat == "PARQUET" { + parquetOpts = &roachpb.ParquetOptions{} + } else { + return nil + } + if override, ok := optVals[exportOptionNullAs]; ok { + parquetOpts.NullEncoding = &override + } + + exportFilePattern = exportFilePatternPart + "." + parquetSuffix + return nil + }(); err != nil { + return nil, err } chunkRows := exportChunkRowsDefault @@ -200,13 +234,13 @@ func (ef *execFactory) ConstructExport( } exportID := ef.planner.stmt.QueryID.String() - namePattern := fmt.Sprintf("export%s-%s", exportID, exportFilePatternDefault) - + namePattern := fmt.Sprintf("export%s-%s", exportID, exportFilePattern) return &exportNode{ source: input.(planNode), destination: string(*destination), fileNamePattern: namePattern, csvOpts: csvOpts, + parquetOpts: parquetOpts, chunkRows: chunkRows, chunkSize: chunkSize, fileCompression: codec, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index e2b14391ab3a..486e10624aad 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -3136,6 +3136,7 @@ import_stmt: // // Formats: // CSV +// Parquet // // Options: // delimiter = '...' [CSV-specific] From 93bb83ac14ddfaf01bd252a3a1ce0728b3c18f2a Mon Sep 17 00:00:00 2001 From: Jane Xing Date: Thu, 28 Oct 2021 10:06:16 -0500 Subject: [PATCH 154/205] sql: support "{}" format for array column in COPY FROM STDIN WITH CSV In postgreSQL, user can use the `"{}"` format to insert an array column. Currently, Cockroach does not supports this format, but it allows the `"ARRAY[]"` format. This commit adds support for the `"{}"` format with `COPY FROM STDIN WITH CSV` syntax. Usage: ``` movr=> CREATE TABLE c (d INT ARRAY); CREATE TABLE movr=> COPY c(d) FROM STDIN WITH CSV; Enter data to be copied followed by a newline. End with a backslash and a period on a line by itself, or an EOF signal. >> "{1,2}" >> "{3,4,5}" >> \. COPY 2 movr=> SELECT * FROM c; d ------- {1,2} {3,4,5} (2 rows) ``` Fixes #70728 Release note (bug fix): support `"{}"` format for array column in COPY FROM STDIN WITH CSV --- pkg/sql/copy.go | 13 +++++++-- pkg/sql/pgwire/testdata/pgtest/copy | 41 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/pkg/sql/copy.go b/pkg/sql/copy.go index a46fcd6ebce4..8dabac401c64 100644 --- a/pkg/sql/copy.go +++ b/pkg/sql/copy.go @@ -463,7 +463,7 @@ func (c *copyMachine) readCSVTuple(ctx context.Context, record []string) error { exprs[i] = tree.DNull continue } - d, err := rowenc.ParseDatumStringAs(c.resultColumns[i].Typ, s, c.parsingEvalCtx) + d, _, err := tree.ParseAndRequireString(c.resultColumns[i].Typ, s, c.parsingEvalCtx) if err != nil { return err } @@ -719,7 +719,16 @@ func (c *copyMachine) readTextTuple(ctx context.Context, line []byte) error { types.UuidFamily: s = decodeCopy(s) } - d, err := rowenc.ParseDatumStringAsWithRawBytes(c.resultColumns[i].Typ, s, c.parsingEvalCtx) + + var d tree.Datum + var err error + switch c.resultColumns[i].Typ.Family() { + case types.BytesFamily: + d = tree.NewDBytes(tree.DBytes(s)) + default: + d, _, err = tree.ParseAndRequireString(c.resultColumns[i].Typ, s, c.parsingEvalCtx) + } + if err != nil { return err } diff --git a/pkg/sql/pgwire/testdata/pgtest/copy b/pkg/sql/pgwire/testdata/pgtest/copy index db99e8e31ea6..843d9a5eb54c 100644 --- a/pkg/sql/pgwire/testdata/pgtest/copy +++ b/pkg/sql/pgwire/testdata/pgtest/copy @@ -586,3 +586,44 @@ ReadyForQuery {"Type":"DataRow","Values":[{"text":"2"},{"text":"2021-09-20 06:05:04-05"}]} {"Type":"CommandComplete","CommandTag":"SELECT 2"} {"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "DROP TABLE IF EXISTS c"} +---- + +until ignore=NoticeResponse +ReadyForQuery +---- +{"Type":"CommandComplete","CommandTag":"DROP TABLE"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "CREATE TABLE c (d INT ARRAY);"} +---- + +until +ReadyForQuery +---- +{"Type":"CommandComplete","CommandTag":"CREATE TABLE"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "COPY c(d) FROM STDIN WITH CSV"} +CopyData {"Data": "\"{0,1}\"\n"} +CopyData {"Data": "\"{2,3,4,5,6}\"\n"} +CopyData {"Data": "\\.\n"} +CopyDone +Query {"String": "SELECT * FROM c"} +---- + +until ignore=RowDescription +ReadyForQuery +ReadyForQuery +---- +{"Type":"CopyInResponse","ColumnFormatCodes":[0]} +{"Type":"CommandComplete","CommandTag":"COPY 2"} +{"Type":"ReadyForQuery","TxStatus":"I"} +{"Type":"DataRow","Values":[{"text":"{0,1}"}]} +{"Type":"DataRow","Values":[{"text":"{2,3,4,5,6}"}]} +{"Type":"CommandComplete","CommandTag":"SELECT 2"} +{"Type":"ReadyForQuery","TxStatus":"I"} From 7258c14118e6ee763ea5b8c723f4049e28322875 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Mon, 1 Nov 2021 20:27:16 +0000 Subject: [PATCH 155/205] build: update README to avoid vendor's origin/master The directions for fixing a rebase conflict on origin master had people resetting their vendor directory to `origin/master`, but that is rarely the right ref to reset to. Overall, I feel like the advice here might be too specific. The _general_ strategy of ditching your vendor changes and rebuilding them is a pretty good one. But the easiest way to do that depends on what your branch looks like. Release note: None --- build/README.md | 67 ++++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/build/README.md b/build/README.md index 6a9aeae47c74..770394a0823e 100644 --- a/build/README.md +++ b/build/README.md @@ -143,27 +143,27 @@ The `bazelbuilder` image is used exclusively for performing builds using Bazel. # Dependencies -Dependencies are managed using `go mod`. We use `go mod vendor` so that we can import and use -non-Go files (e.g. protobuf files) using the [modvendor](https://github.com/goware/modvendor) -script. Adding or updating a dependecy is a two step process: 1) import the dependency in a go +Dependencies are managed using `go mod`. We use `go mod vendor` so that we can import and use +non-Go files (e.g. protobuf files) using the [modvendor](https://github.com/goware/modvendor) +script. Adding or updating a dependecy is a two step process: 1) import the dependency in a go file on your local branch, 2) push a commit containing this import to the `vendored` git submodule. ## Working Locally with Dependencies ### Installing a Dependency -1. In `cockroachdb/cockroach`, switch to the local branch you plan to import the external package +1. In `cockroachdb/cockroach`, switch to the local branch you plan to import the external package into -2. Run `go get -u `. To get a specific version, run `go get -u +2. Run `go get -u `. To get a specific version, run `go get -u @`. You should see changes in `go.mod` when running `git diff`. -3. Import the dependency to a go file in `cockorachdb/cockroach`. You may use an anonymous - import, e.g. `import _ "golang.org/api/compute/v1"`, if you haven't written any code that - references the dependency. This ensures cockroach's make file will properly add the package(s) to the vendor directory. Note that IDEs may bicker that +3. Import the dependency to a go file in `cockorachdb/cockroach`. You may use an anonymous + import, e.g. `import _ "golang.org/api/compute/v1"`, if you haven't written any code that + references the dependency. This ensures cockroach's make file will properly add the package(s) to the vendor directory. Note that IDEs may bicker that these import's paths don't exist. That's ok! 4. Run `go mod tidy` to ensure stale dependencies are removed. 5. Run `make vendor_rebuild` to add the package to the vendor directory. Note this command will only - add packages you have imported in the codebase (and any of the package's dependencies), so you + add packages you have imported in the codebase (and any of the package's dependencies), so you may want to add import statements for each package you plan to use (i.e. repeat step 3 a couple times). -6. Run `cd vendor && git diff && cd ..` to ensure the vendor directory contains the package(s) +6. Run `cd vendor && git diff && cd ..` to ensure the vendor directory contains the package(s) you imported 7. Run `make buildshort` to ensure your code compiles. 8. Run `./dev generate bazel` to regenerate DEPS.bzl with the updated Go dependency information. @@ -171,13 +171,13 @@ file on your local branch, 2) push a commit containing this import to the `vendo ### Updating a Dependency Follow the instructions for [Installing a Dependency](#installing-a-dependency). Note: -- If you're only importing a new package from an existing module in `go.mod`, you don't need to - re-download the module, step 2 above. -- If you're only updating the package version, you probably don't need to update the import +- If you're only importing a new package from an existing module in `go.mod`, you don't need to + re-download the module, step 2 above. +- If you're only updating the package version, you probably don't need to update the import statements, step 3 above. -When [pushing the dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule), you may either checkout a new branch, or create a new commit in the original branch you used to publicize the vendor - dependency. +When [pushing the dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule), you may either checkout a new branch, or create a new commit in the original branch you used to publicize the vendor + dependency. ### Removing a dependency When a dependency has been removed, run `go mod tidy` and then `make vendor_rebuild`. @@ -199,16 +199,16 @@ changes, follow the steps below. - `cd` into `vendor`, and ... + Checkout a **new** named branch + Run `git add .` - + Commit all changes, with a nice short message. There's no explicit policy related to commit - messages in the vendored submodule. + + Commit all changes, with a nice short message. There's no explicit policy related to commit + messages in the vendored submodule. -- At this point the `git status` in your `cockroachdb/cockroach` checkout will report `new commits` +- At this point the `git status` in your `cockroachdb/cockroach` checkout will report `new commits` for `vendor` instead of `modified content`. -- Back in your `cockroachdb/cockroach` branch, commit your code changes and the new `vendor` +- Back in your `cockroachdb/cockroach` branch, commit your code changes and the new `vendor` submodule ref. - Before the `cockroachdb/cockroach` commit can be submitted in a pull request, the submodule commit - it references must be available on `github.com/cockroachdb/vendored`. So, when you're ready to + it references must be available on `github.com/cockroachdb/vendored`. So, when you're ready to publicize your vendor changes, push the `vendored` commit to remote: + Organization members can push their named branches there directly, via: @@ -225,30 +225,33 @@ hashes in `vendored`, there is little significance to the `master` branch in previously referenced commit as their parent, regardless of what `master` happens to be. -It is critical that any ref in vendored that is referenced from `cockroachdb/cockroach` remain -available in vendored in perpetuity. One way to ensure this is to leave the vendored branch that +It is critical that any ref in vendored that is referenced from `cockroachdb/cockroach` remain +available in vendored in perpetuity. One way to ensure this is to leave the vendored branch that you pushed your changes to in place. -If you would like to delete your feature branch in the vendored repository, you must first ensure -that another branch in vendored contains the commit referenced by `cockroachdb/cockroach`. You can +If you would like to delete your feature branch in the vendored repository, you must first ensure +that another branch in vendored contains the commit referenced by `cockroachdb/cockroach`. You can update the master branch in vendored to point at the git SHA currently referenced in `cockroachdb/cockroach`. ### Conflicting Submodule Changes -If you pull/rebase from `cockroach/cockroachdb` and encounter a conflict in the vendor directory, -it is often easiest to take the master branch's vendored directory and then recreate your vendor -changes on top of it. For example:: -1. Remove your local changes to `vendored` by resetting local `vendored`'s head to `origin/master`'s -`vendored` dir: - + In vendor: `git reset --hard origin/master` +If you pull/rebase from `cockroach/cockroachdb` and encounter a conflict in the vendor directory, +it is often easiest to take the master branch's vendored directory and then recreate your vendor +changes on top of it. For example: + +1. Remove your local changes to `vendored` by resetting your local + vendor directory to the commit currently used by `origin/master` on + `cockroachdb/cockroach`. + + Get reference: `git ls-tree origin/master vendor | awk '{print $3}'` + + Reset to it: `cd vendor && git reset --hard REF` 2. In `cockroach/cockroachdb`, amend the commit that contained the dirty vendor pointer. 3. Try pulling/rebasing again, and if that works, rebuild your local vendor repo with `go mod tidy` and `make vendor_rebuild` 4. Push the clean vendor changes to the remote vendor submodule, following the [Pushing the Dependency to the `vendored` submodule](#pushing-the-dependency-to-the-vendored-submodule) -Note: you may also observe conflicts in `go.mod` and `go.sum`. Resolve the conflict like -any vanilla conflict on `cockroach/cockroachdb`, preferring master's +Note: you may also observe conflicts in `go.mod` and `go.sum`. Resolve the conflict like +any vanilla conflict on `cockroach/cockroachdb`, preferring master's version. Then, `make vendor_rebuild` to re-add your local changes to `go. mod` and `go.sum`. ### Recovering from a broken vendor directory From 2846b0955cbcafd75b2387ca6334b0d22cd06b70 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Mon, 25 Oct 2021 16:15:13 +1100 Subject: [PATCH 156/205] sql: implement custom session options * Extend the SQL syntax to support '.' in session variable names. * Extend the protobuf to support custom option names. * Change `getSessionVar` to be able to get custom option names. * Verified in PSQL custom session variables do NOT appear in SHOW ALL or pg_catalog.pg_settings. Release note (sql change): Custom session options can now be used, i.e. any session variable that has `.` in the name. --- docs/generated/sql/bnf/stmt_block.bnf | 4 + pkg/sql/BUILD.bazel | 1 + pkg/sql/conn_executor.go | 4 + pkg/sql/delegate/show_var.go | 4 + pkg/sql/exec_util.go | 11 +- .../testdata/logic_test/alter_role_set | 17 +- pkg/sql/logictest/testdata/logic_test/set | 56 ++- .../logictest/testdata/logic_test/set_local | 33 ++ pkg/sql/opaque.go | 3 + pkg/sql/parser/sql.y | 15 + pkg/sql/parser/testdata/set | 32 ++ pkg/sql/pgwire/auth.go | 1 - pkg/sql/pgwire/conn_test.go | 8 +- pkg/sql/pgwire/server.go | 8 +- pkg/sql/pgwire/testdata/pgtest/set | 31 ++ pkg/sql/plan_columns.go | 4 + pkg/sql/sessiondata/session_data.go | 10 +- .../local_only_session_data.pb.go | 412 ++++++++++++------ .../local_only_session_data.proto | 3 + pkg/sql/show_var.go | 55 +++ .../session_migration/session_migration | 3 +- pkg/sql/vars.go | 32 ++ pkg/sql/walk.go | 1 + 23 files changed, 599 insertions(+), 149 deletions(-) create mode 100644 pkg/sql/show_var.go diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 3f5edfba22f9..a83cc725a808 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -1556,6 +1556,7 @@ insert_column_item ::= session_var ::= 'identifier' + | 'identifier' session_var_parts | 'ALL' | 'DATABASE' | 'NAMES' @@ -2118,6 +2119,9 @@ like_table_option_list ::= column_name ::= name +session_var_parts ::= + ( '.' 'identifier' ) ( ( '.' 'identifier' ) )* + attrs ::= ( '.' unrestricted_name ) ( ( '.' unrestricted_name ) )* diff --git a/pkg/sql/BUILD.bazel b/pkg/sql/BUILD.bazel index add74b174ddf..baf7330b0662 100644 --- a/pkg/sql/BUILD.bazel +++ b/pkg/sql/BUILD.bazel @@ -195,6 +195,7 @@ go_library( "show_stats.go", "show_trace.go", "show_trace_replica.go", + "show_var.go", "show_zone_config.go", "sort.go", "split.go", diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index 60dcb9fd5f66..12101eb2fc7f 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -684,8 +684,12 @@ func (s *Server) newSessionData(args SessionArgs) *sessiondata.SessionData { LocalOnlySessionData: sessiondatapb.LocalOnlySessionData{ ResultsBufferSize: args.ConnResultsBufferSize, IsSuperuser: args.IsSuperuser, + CustomOptions: make(map[string]string), }, } + for k, v := range args.CustomOptionSessionDefaults { + sd.CustomOptions[k] = v + } s.populateMinimalSessionData(sd) return sd } diff --git a/pkg/sql/delegate/show_var.go b/pkg/sql/delegate/show_var.go index 8ddff935016c..e9e24d4700d3 100644 --- a/pkg/sql/delegate/show_var.go +++ b/pkg/sql/delegate/show_var.go @@ -41,6 +41,10 @@ func (d *delegator) delegateShowVar(n *tree.ShowVar) (tree.Statement, error) { } if _, ok := ValidVars[name]; !ok { + // Custom options go to planNode. + if strings.Contains(name, ".") { + return nil, nil + } return nil, pgerror.Newf(pgcode.UndefinedObject, "unrecognized configuration parameter %q", origName) } diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index 2dd9b178dacf..47556a77095d 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -1750,9 +1750,10 @@ type SessionDefaults map[string]string // SessionArgs contains arguments for serving a client connection. type SessionArgs struct { - User security.SQLUsername - IsSuperuser bool - SessionDefaults SessionDefaults + User security.SQLUsername + IsSuperuser bool + SessionDefaults SessionDefaults + CustomOptionSessionDefaults SessionDefaults // RemoteAddr is the client's address. This is nil iff this is an internal // client. RemoteAddr net.Addr @@ -2807,6 +2808,10 @@ func (m *sessionDataMutator) SetLocation(loc *time.Location) { m.bufferParamStatusUpdate("TimeZone", sessionDataTimeZoneFormat(loc)) } +func (m *sessionDataMutator) SetCustomOption(name, val string) { + m.data.CustomOptions[name] = val +} + func (m *sessionDataMutator) SetReadOnly(val bool) { // The read-only state is special; it's set as a session variable (SET // transaction_read_only=<>), but it represents per-txn state, not diff --git a/pkg/sql/logictest/testdata/logic_test/alter_role_set b/pkg/sql/logictest/testdata/logic_test/alter_role_set index 816a7e3c32cd..e42ab7bbea45 100644 --- a/pkg/sql/logictest/testdata/logic_test/alter_role_set +++ b/pkg/sql/logictest/testdata/logic_test/alter_role_set @@ -10,7 +10,8 @@ statement ok ALTER ROLE test_set_role SET application_name = 'a'; ALTER ROLE test_set_role IN DATABASE test_set_db SET application_name = 'b'; ALTER ROLE ALL IN DATABASE test_set_db SET application_name = 'c'; -ALTER ROLE ALL SET application_name = 'd' +ALTER ROLE ALL SET application_name = 'd'; +ALTER ROLE test_set_role SET custom_option.setting = 'e' # Verify that the defaults were stored. query OTT colnames @@ -18,7 +19,7 @@ SELECT database_id, role_name, settings FROM system.database_role_settings ORDER ---- database_id role_name settings 0 · {application_name=d} -0 test_set_role {application_name=a} +0 test_set_role {application_name=a,custom_option.setting=e} 53 · {application_name=c} 53 test_set_role {application_name=b} @@ -32,7 +33,7 @@ ORDER BY 1, 2 ---- setdatabase setrole datname rolname setconfig 0 0 NULL NULL {application_name=d} -0 265380634 NULL test_set_role {application_name=a} +0 265380634 NULL test_set_role {application_name=a,custom_option.setting=e} 53 0 test_set_db NULL {application_name=c} 53 265380634 test_set_db test_set_role {application_name=b} @@ -44,7 +45,7 @@ query T SELECT settings FROM system.database_role_settings WHERE database_id = 0 AND role_name = 'test_set_role' ---- -{application_name=a,backslash_quote=safe_encoding} +{application_name=a,custom_option.setting=e,backslash_quote=safe_encoding} statement ok ALTER ROLE test_set_role SET application_name = 'f' @@ -54,7 +55,7 @@ query T SELECT settings FROM system.database_role_settings WHERE database_id = 0 AND role_name = 'test_set_role' ---- -{backslash_quote=safe_encoding,application_name=f} +{custom_option.setting=e,backslash_quote=safe_encoding,application_name=f} statement ok ALTER ROLE test_set_role SET serial_normalization = 'sql_sequence'; @@ -65,7 +66,7 @@ query T SELECT settings FROM system.database_role_settings WHERE database_id = 0 AND role_name = 'test_set_role' ---- -{backslash_quote=safe_encoding,serial_normalization=sql_sequence} +{custom_option.setting=e,backslash_quote=safe_encoding,serial_normalization=sql_sequence} # Resetting something that isn't there anymore is fine. statement ok @@ -187,7 +188,7 @@ query OTT colnames SELECT database_id, role_name, settings FROM system.database_role_settings ORDER BY 1, 2 ---- database_id role_name settings -0 test_set_role {backslash_quote=safe_encoding,serial_normalization=sql_sequence} +0 test_set_role {custom_option.setting=e,backslash_quote=safe_encoding,serial_normalization=sql_sequence} 53 · {application_name=c} 53 test_set_role {application_name=b} @@ -199,7 +200,7 @@ query OTT colnames SELECT database_id, role_name, settings FROM system.database_role_settings ORDER BY 1, 2 ---- database_id role_name settings -0 test_set_role {backslash_quote=safe_encoding,serial_normalization=sql_sequence} +0 test_set_role {custom_option.setting=e,backslash_quote=safe_encoding,serial_normalization=sql_sequence} statement ok DROP ROLE test_set_role diff --git a/pkg/sql/logictest/testdata/logic_test/set b/pkg/sql/logictest/testdata/logic_test/set index 5aee32edcca9..d51c3562a9b7 100644 --- a/pkg/sql/logictest/testdata/logic_test/set +++ b/pkg/sql/logictest/testdata/logic_test/set @@ -414,15 +414,6 @@ SHOW idle_in_transaction_session_timeout statement ok SET idle_in_transaction_session_timeout = 0 -# Test that composite variable names get rejected properly, especially -# when "tracing" is used as prefix. - -statement error unrecognized configuration parameter "blah.blah" -SET blah.blah = 123 - -statement error unrecognized configuration parameter "tracing.blah" -SET tracing.blah = 123 - statement error invalid value for parameter "ssl_renegotiation_limit" SET ssl_renegotiation_limit = 123 @@ -626,3 +617,50 @@ SET LC_NUMERIC = 'en_US.UTF-8' statement error invalid value for parameter "lc_time": "en_US.UTF-8" SET LC_TIME = 'en_US.UTF-8' + +# Test custom session variables + +statement ok +SET custom_option.set_SQL = 'abc'; +SELECT set_config('custom_option.set_config', 'def', false); +RESET custom_option.use_default; +SET tracing.custom = 'ijk' + +# Custom options are case insensitive. +query T colnames +SHOW Custom_option.set_sql +---- +custom_option.set_sql +abc + +query T +SHOW custom_option.set_config +---- +def + +query T +SELECT current_setting('custom_option.use_default') +---- +· + +statement ok +RESET custom_option.set_config + +query T +SHOW custom_option.set_config +---- +· + +# Ensure it does not show up on SHOW ALL or pg_settings. +query T +SELECT variable FROM [SHOW ALL] WHERE variable LIKE 'custom_option.%' +---- + +query T +SELECT name FROM pg_catalog.pg_settings WHERE name LIKE 'custom_option.%' +---- + +query T +SHOW tracing.custom +---- +ijk diff --git a/pkg/sql/logictest/testdata/logic_test/set_local b/pkg/sql/logictest/testdata/logic_test/set_local index 6e5b15bef3c5..fb3aee85e2c6 100644 --- a/pkg/sql/logictest/testdata/logic_test/set_local +++ b/pkg/sql/logictest/testdata/logic_test/set_local @@ -508,3 +508,36 @@ query TT SELECT * FROM tbl ---- 2020-08-25 15:16:17.123456 +0000 UTC 1 day 15:16:17.123456 + +# Verify that custom session variables are handled correctly. +statement ok +SET custom_option.local_setting = 'abc'; +SET custom_option.session_setting = 'abc' + +statement ok +BEGIN; +SET LOCAL custom_option.local_setting = 'def'; +SET custom_option.session_setting = 'def' + +query T +SHOW custom_option.local_setting +---- +def + +query T +SHOW custom_option.session_setting +---- +def + +statement ok +COMMIT + +query T +SHOW custom_option.local_setting +---- +abc + +query T +SHOW custom_option.session_setting +---- +def diff --git a/pkg/sql/opaque.go b/pkg/sql/opaque.go index 086bfc89d060..8f5d4db90364 100644 --- a/pkg/sql/opaque.go +++ b/pkg/sql/opaque.go @@ -206,6 +206,8 @@ func planOpaque(ctx context.Context, p *planner, stmt tree.Statement) (planNode, return p.ShowTableStats(ctx, n) case *tree.ShowTraceForSession: return p.ShowTrace(ctx, n) + case *tree.ShowVar: + return p.ShowVar(ctx, n) case *tree.ShowZoneConfig: return p.ShowZoneConfig(ctx, n) case *tree.ShowFingerprints: @@ -293,6 +295,7 @@ func init() { &tree.ShowTraceForSession{}, &tree.ShowZoneConfig{}, &tree.ShowFingerprints{}, + &tree.ShowVar{}, &tree.Truncate{}, // CCL statements (without Export which has an optimizer operator). diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index e2b14391ab3a..3c50c6dc3c2a 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -1216,6 +1216,7 @@ func (u *sqlSymUnion) setVar() *tree.SetVar { %type expr_list opt_expr_list tuple1_ambiguous_values tuple1_unambiguous_values %type <*tree.Tuple> expr_tuple1_ambiguous expr_tuple_unambiguous %type attrs +%type <[]string> session_var_parts %type target_list %type set_clause_list %type <*tree.UpdateExpr> set_clause multiple_set_clause @@ -4978,6 +4979,10 @@ show_session_stmt: session_var: IDENT +| IDENT session_var_parts + { + $$ = $1 + "." + strings.Join($2.strs(), ".") + } // Although ALL, SESSION_USER, DATABASE, LC_COLLATE, and LC_CTYPE are // identifiers for the purpose of SHOW, they lex as separate token types, so // they need separate rules. @@ -4994,6 +4999,16 @@ session_var: | TIME ZONE { $$ = "timezone" } | TIME error // SHOW HELP: SHOW SESSION +session_var_parts: + '.' IDENT + { + $$.val = []string{$2} + } +| session_var_parts '.' IDENT + { + $$.val = append($1.strs(), $3) + } + // %Help: SHOW STATISTICS - display table statistics (experimental) // %Category: Experimental // %Text: SHOW STATISTICS [USING JSON] FOR TABLE diff --git a/pkg/sql/parser/testdata/set b/pkg/sql/parser/testdata/set index f98fdac3ff3b..4b4eb3c5fc8a 100644 --- a/pkg/sql/parser/testdata/set +++ b/pkg/sql/parser/testdata/set @@ -547,3 +547,35 @@ SET LOCAL intervalstyle = 'postgres' -- normalized! SET LOCAL intervalstyle = ('postgres') -- fully parenthesized SET LOCAL intervalstyle = '_' -- literals removed SET LOCAL intervalstyle = 'postgres' -- identifiers removed + +parse +SET a.b = 'd' +---- +SET "a.b" = 'd' -- normalized! +SET "a.b" = ('d') -- fully parenthesized +SET "a.b" = '_' -- literals removed +SET "a.b" = 'd' -- identifiers removed + +parse +SET a.b.c = 'd' +---- +SET "a.b.c" = 'd' -- normalized! +SET "a.b.c" = ('d') -- fully parenthesized +SET "a.b.c" = '_' -- literals removed +SET "a.b.c" = 'd' -- identifiers removed + +parse +RESET a.b.c +---- +RESET "a.b.c" -- normalized! +RESET "a.b.c" -- fully parenthesized +RESET "a.b.c" -- literals removed +RESET "a.b.c" -- identifiers removed + +parse +SHOW a.b.c +---- +SHOW "a.b.c" -- normalized! +SHOW "a.b.c" -- fully parenthesized +SHOW "a.b.c" -- literals removed +SHOW "a.b.c" -- identifiers removed diff --git a/pkg/sql/pgwire/auth.go b/pkg/sql/pgwire/auth.go index 8ed79ca2c407..9b6d673dd8b6 100644 --- a/pkg/sql/pgwire/auth.go +++ b/pkg/sql/pgwire/auth.go @@ -167,7 +167,6 @@ func (c *conn) handleAuthentication( if _, ok := c.sessionArgs.SessionDefaults[keyVal[0]]; !ok { c.sessionArgs.SessionDefaults[keyVal[0]] = keyVal[1] } - } } diff --git a/pkg/sql/pgwire/conn_test.go b/pkg/sql/pgwire/conn_test.go index 8412d212b4e7..1178059bdeb4 100644 --- a/pkg/sql/pgwire/conn_test.go +++ b/pkg/sql/pgwire/conn_test.go @@ -1561,7 +1561,8 @@ func TestSetSessionArguments(t *testing.T) { "--default-transaction-isolation=read\\ uncommitted "+ "-capplication_name=test "+ "--DateStyle=ymd\\ ,\\ iso\\ "+ - "-c intervalstyle%3DISO_8601") + "-c intervalstyle%3DISO_8601 "+ + "-ccustom_option.custom_option=test2") pgURL.RawQuery = q.Encode() noBufferDB, err := gosql.Open("postgres", pgURL.String()) @@ -1612,6 +1613,11 @@ func TestSetSessionArguments(t *testing.T) { } require.Equal(t, expectedFoundOptions, foundOptions) + // Custom session options don't show up on SHOW ALL + var customOption string + require.NoError(t, conn.QueryRow(ctx, "SHOW custom_option.custom_option").Scan(&customOption)) + require.Equal(t, "test2", customOption) + if err := conn.Close(ctx); err != nil { t.Fatal(err) } diff --git a/pkg/sql/pgwire/server.go b/pkg/sql/pgwire/server.go index ec6450a411a8..9002f81b6fcf 100644 --- a/pkg/sql/pgwire/server.go +++ b/pkg/sql/pgwire/server.go @@ -730,8 +730,9 @@ func parseClientProvidedSessionParameters( trustClientProvidedRemoteAddr bool, ) (sql.SessionArgs, error) { args := sql.SessionArgs{ - SessionDefaults: make(map[string]string), - RemoteAddr: origRemoteAddr, + SessionDefaults: make(map[string]string), + CustomOptionSessionDefaults: make(map[string]string), + RemoteAddr: origRemoteAddr, } foundBufferSize := false @@ -854,7 +855,8 @@ func loadParameter(ctx context.Context, key, value string, args *sql.SessionArgs switch { case exists && configurable: args.SessionDefaults[key] = value - + case sql.IsCustomOptionSessionVariable(key): + args.CustomOptionSessionDefaults[key] = value case !exists: if _, ok := sql.UnsupportedVars[key]; ok { counter := sqltelemetry.UnimplementedClientStatusParameterCounter(key) diff --git a/pkg/sql/pgwire/testdata/pgtest/set b/pkg/sql/pgwire/testdata/pgtest/set index 962ab01c3e0f..bb741f9f838c 100644 --- a/pkg/sql/pgwire/testdata/pgtest/set +++ b/pkg/sql/pgwire/testdata/pgtest/set @@ -8,3 +8,34 @@ ReadyForQuery {"Type":"ParameterStatus","Name":"IntervalStyle","Value":"postgres"} {"Type":"CommandComplete","CommandTag":"RESET"} {"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "SET custom_option.session_setting = 'abc'"} +---- + +until +ReadyForQuery +---- +{"Type":"CommandComplete","CommandTag":"SET"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +send +Query {"String": "SHOW custom_option.session_setting"} +---- + +# CockroachDB has the command tag SHOW 1, but postgres uses SHOW. +until crdb_only +ReadyForQuery +---- +{"Type":"RowDescription","Fields":[{"Name":"custom_option.session_setting","TableOID":0,"TableAttributeNumber":0,"DataTypeOID":25,"DataTypeSize":-1,"TypeModifier":-1,"Format":0}]} +{"Type":"DataRow","Values":[{"text":"abc"}]} +{"Type":"CommandComplete","CommandTag":"SHOW 1"} +{"Type":"ReadyForQuery","TxStatus":"I"} + +until noncrdb_only +ReadyForQuery +---- +{"Type":"RowDescription","Fields":[{"Name":"custom_option.session_setting","TableOID":0,"TableAttributeNumber":0,"DataTypeOID":25,"DataTypeSize":-1,"TypeModifier":-1,"Format":0}]} +{"Type":"DataRow","Values":[{"text":"abc"}]} +{"Type":"CommandComplete","CommandTag":"SHOW"} +{"Type":"ReadyForQuery","TxStatus":"I"} diff --git a/pkg/sql/plan_columns.go b/pkg/sql/plan_columns.go index da28ac210883..5d10c14d07cd 100644 --- a/pkg/sql/plan_columns.go +++ b/pkg/sql/plan_columns.go @@ -157,6 +157,10 @@ func getPlanColumns(plan planNode, mut bool) colinfo.ResultColumns { case *recursiveCTENode: return getPlanColumns(n.initial, mut) + case *showVarNode: + return colinfo.ResultColumns{ + {Name: n.name, Typ: types.String}, + } case *rowSourceToPlanNode: return n.planCols } diff --git a/pkg/sql/sessiondata/session_data.go b/pkg/sql/sessiondata/session_data.go index a86e467b5fec..8729826d2e4d 100644 --- a/pkg/sql/sessiondata/session_data.go +++ b/pkg/sql/sessiondata/session_data.go @@ -52,11 +52,19 @@ type SessionData struct { // Clone returns a clone of SessionData. func (s *SessionData) Clone() *SessionData { - // Currently clone does a shallow copy of everything - we can get away with it + var newCustomOptions map[string]string + if s.CustomOptions != nil { + newCustomOptions = make(map[string]string, len(s.CustomOptions)) + for k, v := range s.CustomOptions { + newCustomOptions[k] = v + } + } + // Other options in SessionData are shallow cloned - we can get away with it // as all the slices/maps does a copy if it mutates OR are operations that // affect the whole SessionDataStack (e.g. setting SequenceState should be the // setting the same value across all copied SessionData). ret := *s + ret.CustomOptions = newCustomOptions return &ret } diff --git a/pkg/sql/sessiondatapb/local_only_session_data.pb.go b/pkg/sql/sessiondatapb/local_only_session_data.pb.go index e9faf5304c2d..7fc6b2e79ef1 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.pb.go +++ b/pkg/sql/sessiondatapb/local_only_session_data.pb.go @@ -219,6 +219,9 @@ type LocalOnlySessionData struct { NullOrderedLast bool `protobuf:"varint,55,opt,name=null_ordered_last,json=nullOrderedLast,proto3" json:"null_ordered_last,omitempty"` // DisablePlanGists indicates whether we should disable automatic gists. DisablePlanGists bool `protobuf:"varint,56,opt,name=disable_plan_gists,json=disablePlanGists,proto3" json:"disable_plan_gists,omitempty"` + // CustomOptions contains a map of all custom session settings. + // These session variables have at least one period in their name. + CustomOptions map[string]string `protobuf:"bytes,57,rep,name=custom_options,json=customOptions,proto3" json:"custom_options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (m *LocalOnlySessionData) Reset() { *m = LocalOnlySessionData{} } @@ -297,6 +300,7 @@ var xxx_messageInfo_SequenceCacheEntry proto.InternalMessageInfo func init() { proto.RegisterType((*LocalOnlySessionData)(nil), "cockroach.sql.sessiondatapb.LocalOnlySessionData") + proto.RegisterMapType((map[string]string)(nil), "cockroach.sql.sessiondatapb.LocalOnlySessionData.CustomOptionsEntry") proto.RegisterMapType((SequenceCache)(nil), "cockroach.sql.sessiondatapb.LocalOnlySessionData.SequenceCacheEntry") proto.RegisterType((*SequenceCacheEntry)(nil), "cockroach.sql.sessiondatapb.SequenceCacheEntry") } @@ -306,128 +310,131 @@ func init() { } var fileDescriptor_21ead158cf36da28 = []byte{ - // 1930 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x57, 0xdd, 0x73, 0x1b, 0xb7, - 0x11, 0x37, 0xe3, 0x24, 0xb5, 0x21, 0xcb, 0x96, 0x2e, 0x72, 0x74, 0xb2, 0x6c, 0x51, 0xb2, 0x9d, - 0x58, 0x8e, 0x13, 0x32, 0x71, 0xd2, 0xd4, 0x4d, 0xa7, 0x1f, 0xd1, 0x57, 0xfc, 0xa1, 0x44, 0xf2, - 0x51, 0x8e, 0xa7, 0x69, 0x67, 0x30, 0xd0, 0xdd, 0x92, 0x44, 0x04, 0x02, 0x47, 0x00, 0x27, 0x89, - 0x7a, 0xe8, 0xdf, 0xd0, 0x99, 0x3e, 0xf7, 0xff, 0xf1, 0x63, 0x1e, 0xf3, 0xa4, 0x69, 0xe5, 0xff, - 0xc2, 0x4f, 0x9d, 0x5d, 0xdc, 0x91, 0x94, 0xad, 0xa4, 0x6f, 0xe4, 0xfe, 0x3e, 0x0e, 0xd8, 0xdb, - 0x5d, 0xe0, 0x58, 0xd3, 0xf5, 0x55, 0xd3, 0x81, 0x73, 0xd2, 0xe8, 0x4c, 0x78, 0x91, 0xef, 0x36, - 0x95, 0x49, 0x85, 0xe2, 0x46, 0xab, 0x01, 0x2f, 0x01, 0x8e, 0x48, 0x23, 0xb7, 0xc6, 0x9b, 0x68, - 0x3e, 0x35, 0xe9, 0x9e, 0x35, 0x22, 0xed, 0x36, 0x5c, 0x5f, 0x35, 0x4e, 0x49, 0xaf, 0xcd, 0x74, - 0x4c, 0xc7, 0x10, 0xaf, 0x89, 0xbf, 0x82, 0xe4, 0xe6, 0x8b, 0xeb, 0x6c, 0x66, 0x13, 0x4d, 0xb7, - 0xb4, 0x1a, 0xb4, 0x82, 0x60, 0x4d, 0x78, 0x11, 0x7d, 0xcc, 0x22, 0x27, 0xf6, 0x81, 0x7b, 0xb1, - 0xab, 0xc0, 0xf1, 0xdc, 0x42, 0x5b, 0x1e, 0xc6, 0xb5, 0xc5, 0xda, 0xf2, 0xc5, 0x64, 0x0a, 0x91, - 0x1d, 0x02, 0xb6, 0x29, 0x1e, 0xfd, 0x8d, 0xcd, 0x9b, 0xdc, 0xcb, 0x9e, 0x3c, 0x02, 0xcb, 0xdb, - 0x7b, 0x3c, 0x15, 0x2e, 0x15, 0x19, 0x38, 0xae, 0x64, 0x4f, 0xfa, 0xf8, 0xad, 0xc5, 0xda, 0xf2, - 0xf9, 0x95, 0xeb, 0x27, 0xc7, 0xf5, 0x78, 0xab, 0xa2, 0x6d, 0x3c, 0x59, 0x2d, 0x49, 0x9b, 0xc8, - 0x49, 0xe2, 0xa1, 0xc1, 0xc6, 0xde, 0x29, 0x24, 0xfa, 0x82, 0x5d, 0x72, 0xbe, 0xe7, 0xb9, 0x97, - 0x3d, 0x30, 0x85, 0x8f, 0xcf, 0x93, 0xdb, 0xf4, 0xab, 0xe3, 0xfa, 0x24, 0x86, 0x1a, 0x6b, 0x85, - 0x15, 0x5e, 0x1a, 0x9d, 0x4c, 0x20, 0x6d, 0x27, 0xb0, 0xa2, 0x87, 0x6c, 0x56, 0x66, 0x0a, 0xb8, - 0xd4, 0xc3, 0x54, 0x55, 0x06, 0x6f, 0xff, 0x92, 0xc1, 0x0c, 0x2a, 0x1e, 0xe9, 0x32, 0x0f, 0x95, - 0x13, 0x67, 0xb7, 0x2a, 0x27, 0x6f, 0x85, 0x76, 0x22, 0x45, 0xf2, 0x1b, 0xae, 0xef, 0xfc, 0x92, - 0x6b, 0x3d, 0xb8, 0xee, 0x8c, 0xb4, 0xaf, 0x3d, 0xe0, 0x4b, 0x36, 0xab, 0x8d, 0x97, 0x29, 0xf0, - 0x4c, 0xba, 0x5c, 0x09, 0x7c, 0xb9, 0xfb, 0x60, 0xa5, 0x1f, 0xc4, 0xef, 0x2e, 0xd6, 0x96, 0x27, - 0x93, 0xab, 0x01, 0x5e, 0x0b, 0x68, 0xab, 0x04, 0xa3, 0x06, 0x7b, 0xcf, 0x82, 0xb1, 0x19, 0x58, - 0xfe, 0xa3, 0x91, 0xba, 0xca, 0xf6, 0x6f, 0x70, 0x21, 0xc9, 0x74, 0x09, 0x3d, 0x46, 0x24, 0x24, - 0xf2, 0x53, 0x36, 0x93, 0x41, 0x5b, 0x14, 0xca, 0x73, 0x7f, 0xa8, 0x79, 0x6e, 0xa5, 0xa1, 0x87, - 0x5c, 0x20, 0x41, 0x54, 0x62, 0x3b, 0x87, 0x7a, 0xbb, 0x44, 0xa2, 0xcf, 0xd8, 0xd5, 0x71, 0x85, - 0x05, 0x91, 0x51, 0xf5, 0xc5, 0x17, 0x17, 0x6b, 0xcb, 0x17, 0xc6, 0x25, 0x09, 0x88, 0x0c, 0x6b, - 0x28, 0x5a, 0x61, 0x0b, 0xe3, 0x92, 0xc2, 0x01, 0x6f, 0x1b, 0xa5, 0xcc, 0x01, 0x58, 0xd2, 0xbb, - 0x98, 0x91, 0xf6, 0xda, 0x48, 0xfb, 0xcc, 0xc1, 0x46, 0x49, 0x41, 0x1b, 0x17, 0x6d, 0xb1, 0xdb, - 0xb9, 0xb0, 0x5e, 0x0a, 0xa5, 0x06, 0x98, 0x13, 0x6f, 0xe5, 0x6e, 0xe1, 0x21, 0xe3, 0xb9, 0x12, - 0xda, 0x61, 0x04, 0x8b, 0x2f, 0x8b, 0x27, 0xc8, 0x69, 0x69, 0xc8, 0x5d, 0x1b, 0x51, 0xb7, 0x91, - 0xb9, 0x56, 0x12, 0xa3, 0x07, 0x6c, 0x54, 0x5e, 0xb4, 0xa4, 0xae, 0x74, 0xde, 0x74, 0xac, 0xe8, - 0xb9, 0xf8, 0x12, 0x99, 0xbc, 0x3f, 0xc4, 0x9f, 0x39, 0x78, 0x38, 0x44, 0xa3, 0xbf, 0xb0, 0x1b, - 0xa7, 0x95, 0xbd, 0x42, 0x79, 0xc9, 0x53, 0xa3, 0xb8, 0xf3, 0xc2, 0xbb, 0x78, 0x92, 0xe4, 0x73, - 0xe3, 0xf2, 0x6f, 0x91, 0xb2, 0x6a, 0x54, 0x0b, 0x09, 0xd1, 0x57, 0x6c, 0x8e, 0xda, 0x56, 0xfa, - 0x01, 0xaf, 0x58, 0x19, 0x77, 0x20, 0x6c, 0xda, 0x8d, 0x2f, 0x93, 0x7a, 0xb6, 0x22, 0x54, 0xdd, - 0x91, 0xb5, 0x08, 0x8e, 0x96, 0xd8, 0x25, 0x27, 0xda, 0xc0, 0x8b, 0x3c, 0x13, 0x1e, 0x5c, 0x7c, - 0x85, 0xe8, 0x13, 0x18, 0x7b, 0x16, 0x42, 0xd1, 0x5f, 0xd9, 0x3c, 0x36, 0x27, 0x58, 0xae, 0x8c, - 0xd9, 0x2b, 0xf2, 0xb2, 0x14, 0xda, 0x06, 0x1b, 0xd1, 0xc5, 0x53, 0xa8, 0x58, 0x99, 0x3f, 0x39, - 0xae, 0xcf, 0x6e, 0x13, 0x6d, 0x93, 0x58, 0x54, 0x15, 0x1b, 0xc6, 0x6e, 0x3c, 0x71, 0xc9, 0x6c, - 0x7e, 0x16, 0xb0, 0xe7, 0xb0, 0xbe, 0x8e, 0x64, 0xe7, 0x48, 0x74, 0xc8, 0x93, 0x83, 0x0e, 0x59, - 0x9f, 0xa6, 0x45, 0x4c, 0x07, 0x08, 0xf9, 0xeb, 0x01, 0x88, 0xbe, 0x66, 0x37, 0x2c, 0xf4, 0x0b, - 0x69, 0x81, 0xc3, 0x61, 0xae, 0x64, 0x2a, 0x3d, 0x16, 0x59, 0x4f, 0xd8, 0x01, 0xdf, 0x83, 0x81, - 0x8b, 0xa3, 0xf0, 0xe6, 0x4b, 0xd2, 0x7a, 0xc9, 0xd9, 0x0e, 0x94, 0x27, 0x30, 0x70, 0xd8, 0x0a, - 0x6d, 0x63, 0x53, 0xe0, 0x38, 0x62, 0x72, 0x23, 0xb5, 0xe7, 0x16, 0x9c, 0x17, 0xd6, 0xc7, 0xef, - 0x91, 0xf8, 0x2a, 0xc1, 0xad, 0x0a, 0x4d, 0x02, 0x18, 0x3d, 0x60, 0x73, 0x02, 0x2b, 0x08, 0x07, - 0x55, 0x2e, 0x2c, 0x70, 0xe1, 0x30, 0xd9, 0x54, 0x30, 0xf1, 0x4c, 0x50, 0x12, 0x61, 0x3b, 0xe0, - 0x5f, 0xbb, 0xad, 0xdc, 0x63, 0x8d, 0xe0, 0x26, 0x3d, 0xf4, 0xf2, 0x6a, 0xd0, 0x55, 0x9b, 0xbc, - 0x1a, 0x36, 0x89, 0x50, 0x98, 0x74, 0xd5, 0x26, 0xb7, 0xd8, 0x6d, 0xd9, 0x2b, 0x37, 0x97, 0x1a, - 0x55, 0xf4, 0x34, 0xa7, 0xfa, 0xc3, 0xbe, 0x96, 0xba, 0x33, 0x34, 0x78, 0x3f, 0xd4, 0x66, 0xc5, - 0x5d, 0x25, 0xea, 0xf6, 0x18, 0xb3, 0x32, 0x7c, 0xce, 0xee, 0x9a, 0x7d, 0xb0, 0x56, 0x66, 0x55, - 0x71, 0x59, 0xe8, 0xe0, 0x60, 0x39, 0x32, 0x1a, 0x78, 0x6a, 0x74, 0x5b, 0x8e, 0x5c, 0x63, 0x72, - 0xbd, 0x5d, 0x09, 0xa8, 0xd2, 0x12, 0xa2, 0xff, 0x60, 0x34, 0xac, 0x12, 0xb9, 0x32, 0xfe, 0x33, - 0xbb, 0xde, 0x15, 0xae, 0xcb, 0x5d, 0x57, 0xd8, 0x0c, 0x32, 0x2e, 0x75, 0x06, 0x87, 0x63, 0x5b, - 0x9c, 0x0b, 0x95, 0x8b, 0x9c, 0x56, 0xa0, 0x3c, 0x0a, 0x8c, 0xca, 0xe0, 0xf7, 0x6c, 0x0e, 0x5b, - 0x8d, 0xf2, 0xda, 0x2e, 0x94, 0x0a, 0x39, 0xe2, 0x2e, 0x15, 0xda, 0xc5, 0xd7, 0x42, 0xdb, 0x54, - 0x84, 0x8d, 0x42, 0x29, 0x4a, 0x54, 0x0b, 0xd1, 0xe8, 0x0f, 0xec, 0xda, 0x30, 0x4b, 0x0e, 0x14, - 0xa4, 0x9e, 0x2a, 0x32, 0xd4, 0x71, 0x3c, 0x1f, 0xaa, 0xbe, 0x62, 0xb4, 0x88, 0xb0, 0x61, 0x6c, - 0xa8, 0xe9, 0x68, 0x99, 0x4d, 0x49, 0xed, 0xc0, 0x7a, 0xde, 0x16, 0xce, 0xf3, 0x5c, 0xf8, 0x6e, - 0x7c, 0x9d, 0x24, 0x97, 0x43, 0x7c, 0x43, 0x38, 0xbf, 0x2d, 0x7c, 0x37, 0x7a, 0xc8, 0x96, 0x84, - 0xf2, 0x60, 0xab, 0x37, 0xe1, 0x07, 0x39, 0xf0, 0x0e, 0x68, 0xb0, 0x42, 0x0d, 0xf7, 0x79, 0x83, - 0xa4, 0x37, 0x88, 0x18, 0x5e, 0xc3, 0xce, 0x20, 0x87, 0x6f, 0x02, 0xab, 0xda, 0xeb, 0x27, 0x2c, - 0x72, 0x03, 0x9d, 0x76, 0xad, 0xd1, 0xa6, 0x70, 0x3c, 0x35, 0x3d, 0x1c, 0xa5, 0x0b, 0xa1, 0x0a, - 0xc6, 0x90, 0x55, 0x02, 0xa2, 0x0f, 0xd9, 0x95, 0x60, 0xcf, 0x1d, 0xf4, 0x29, 0x23, 0x71, 0x9d, - 0xb8, 0x93, 0x21, 0xdc, 0x82, 0x3e, 0x26, 0x22, 0xda, 0x61, 0x77, 0x4a, 0x5e, 0xa1, 0x65, 0xbf, - 0x00, 0x7e, 0x20, 0x7d, 0xd7, 0x14, 0x3e, 0xbc, 0x0c, 0x7c, 0xbb, 0xce, 0x5b, 0x21, 0xb5, 0x77, - 0xf1, 0x12, 0xe9, 0x6f, 0x05, 0xfa, 0x33, 0x62, 0x3f, 0x0f, 0x64, 0x7a, 0x2d, 0xab, 0x23, 0x6a, - 0xf4, 0x47, 0x36, 0xef, 0x7c, 0xb1, 0xcb, 0x53, 0xe1, 0x85, 0x32, 0x9d, 0xd7, 0x6b, 0xf7, 0x26, - 0x39, 0xc5, 0x48, 0x59, 0x0d, 0x8c, 0xd3, 0x25, 0xfc, 0x94, 0x7d, 0x00, 0x87, 0x39, 0x58, 0xd9, - 0x03, 0xed, 0x85, 0xc2, 0xcd, 0xe6, 0x34, 0x5e, 0xcb, 0x2c, 0x5a, 0x38, 0xb0, 0x12, 0xc7, 0xcd, - 0x2d, 0x3a, 0xee, 0x6f, 0x8e, 0x93, 0x57, 0x4b, 0x6e, 0x48, 0x64, 0x52, 0x32, 0xa3, 0xbf, 0xb3, - 0x7b, 0xa9, 0xc9, 0x07, 0xa7, 0x5b, 0xe1, 0xa0, 0x0b, 0x9a, 0x67, 0x20, 0xb5, 0x07, 0xab, 0x40, - 0xec, 0x63, 0x8c, 0x96, 0x1a, 0xdf, 0xa6, 0x15, 0xde, 0x41, 0xc9, 0x78, 0x4b, 0x3c, 0xef, 0x82, - 0x5e, 0x3b, 0xc5, 0xa7, 0x85, 0xe3, 0x08, 0xad, 0xb2, 0xed, 0x2d, 0x88, 0x1e, 0xb7, 0x80, 0x95, - 0x43, 0xc7, 0x6b, 0xfc, 0x41, 0x28, 0xa6, 0x32, 0xef, 0x84, 0x27, 0x23, 0x38, 0x1c, 0x92, 0xae, - 0x50, 0xde, 0xf1, 0xdd, 0xa2, 0x8d, 0x73, 0xd2, 0xc9, 0x23, 0x88, 0x3f, 0xac, 0x0e, 0x49, 0x82, - 0x56, 0x08, 0x69, 0xc9, 0x23, 0xc0, 0xa3, 0x22, 0xb7, 0x26, 0x17, 0x1d, 0xe1, 0xf1, 0xc8, 0xcf, - 0x0b, 0xcf, 0xe9, 0x1c, 0x95, 0xba, 0x13, 0xdf, 0x09, 0x35, 0x3f, 0xc4, 0x1f, 0x21, 0xbc, 0x55, - 0xa2, 0xd1, 0xbf, 0x6a, 0xec, 0x54, 0xaa, 0xe8, 0xe4, 0x72, 0x7d, 0x45, 0x43, 0x88, 0x12, 0xd2, - 0x33, 0x19, 0xc4, 0xcb, 0x74, 0x4f, 0xd8, 0x38, 0x39, 0xae, 0xd7, 0xd7, 0xc7, 0xd8, 0x78, 0x76, - 0xb5, 0x9e, 0x6e, 0x6e, 0x97, 0xdc, 0x6f, 0x4d, 0x06, 0xaf, 0xfe, 0x3f, 0x25, 0xa9, 0xc3, 0x6b, - 0x04, 0xd7, 0x57, 0xe3, 0x84, 0x68, 0x83, 0x4d, 0xe2, 0x3a, 0x38, 0x2e, 0x84, 0x9e, 0x7f, 0x97, - 0x9e, 0x7f, 0xf3, 0xe4, 0xb8, 0x3e, 0x51, 0x1a, 0x96, 0xcf, 0xba, 0x52, 0xfe, 0x5d, 0x3f, 0x84, - 0x94, 0xbc, 0x27, 0x50, 0xd8, 0xea, 0x2b, 0xf2, 0x79, 0xce, 0xe6, 0x1c, 0x58, 0x29, 0x14, 0xd7, - 0xc6, 0xf6, 0x84, 0x92, 0x47, 0x94, 0xdf, 0xe0, 0xf9, 0x11, 0x79, 0xce, 0xbf, 0x3a, 0xae, 0xcf, - 0xb6, 0x88, 0xf4, 0xdd, 0x38, 0x87, 0xcc, 0x66, 0xdd, 0xd9, 0x40, 0xb4, 0xc5, 0x66, 0x35, 0x1c, - 0x70, 0x97, 0x76, 0xa1, 0x27, 0x78, 0xda, 0x15, 0xba, 0x03, 0x36, 0xd8, 0xde, 0x23, 0xdb, 0xf8, - 0xd5, 0x71, 0x7d, 0xe6, 0x3b, 0x38, 0x68, 0x11, 0x63, 0x35, 0x10, 0xc8, 0x73, 0x46, 0x9f, 0x11, - 0x8d, 0xfe, 0xc1, 0x2e, 0x3b, 0xe8, 0x17, 0xa0, 0x53, 0xe0, 0xa9, 0x48, 0xbb, 0x10, 0x7f, 0xbc, - 0x78, 0x7e, 0x79, 0xe2, 0xfe, 0x5a, 0xe3, 0x57, 0xee, 0xc7, 0x8d, 0xb3, 0x6e, 0xc1, 0x8d, 0x56, - 0xe9, 0xb3, 0x8a, 0x36, 0xeb, 0xda, 0xdb, 0x41, 0xb8, 0xe0, 0x9d, 0x8a, 0x27, 0x93, 0x6e, 0xfc, - 0x6f, 0x74, 0x8f, 0x4d, 0xe7, 0x4a, 0xa4, 0x80, 0xef, 0x64, 0xd8, 0x93, 0x9f, 0x50, 0xe9, 0x4c, - 0x0d, 0x81, 0xaa, 0x17, 0x73, 0x16, 0x55, 0x17, 0xc9, 0xc2, 0x81, 0xe5, 0x74, 0x2d, 0x8f, 0x1b, - 0xd8, 0x78, 0x2b, 0x2b, 0xaf, 0x8e, 0xeb, 0x7f, 0xea, 0x48, 0xdf, 0x2d, 0x76, 0x1b, 0xa9, 0xe9, - 0x35, 0x87, 0xcb, 0xcf, 0x76, 0x47, 0xbf, 0x9b, 0xf9, 0x5e, 0xa7, 0xe9, 0x20, 0x2d, 0xf0, 0xc2, - 0xd6, 0x68, 0x3d, 0xdd, 0x7c, 0xe6, 0xc0, 0x6a, 0xd1, 0x83, 0x6d, 0x74, 0x4a, 0xa6, 0x4a, 0x77, - 0x8c, 0x52, 0x24, 0x6a, 0xb2, 0x19, 0xba, 0xcb, 0x99, 0x03, 0xc7, 0xb1, 0x7b, 0x3d, 0x68, 0xae, - 0x4c, 0x27, 0x6e, 0x86, 0x8e, 0xf0, 0x87, 0x3a, 0x31, 0x07, 0xee, 0x79, 0x40, 0x36, 0x4d, 0xe7, - 0x4c, 0x01, 0x58, 0x1b, 0x7f, 0x7a, 0x96, 0x60, 0xdd, 0xda, 0xe8, 0x2e, 0x9b, 0x1e, 0x0a, 0xe8, - 0xca, 0x88, 0xf6, 0x9f, 0x11, 0xfb, 0x72, 0xc9, 0xc6, 0x7b, 0x1e, 0x7a, 0xbf, 0x41, 0x45, 0xe3, - 0xfb, 0x6f, 0x50, 0xd1, 0xf5, 0x3e, 0xbb, 0x2a, 0x0a, 0x6f, 0xb8, 0x85, 0xae, 0xe9, 0x8d, 0x9f, - 0xb4, 0x9f, 0x53, 0x6a, 0xdf, 0x43, 0x30, 0x29, 0xb1, 0x2a, 0xbb, 0x4b, 0xec, 0x92, 0x74, 0xdc, - 0x15, 0x39, 0x58, 0xcc, 0x6e, 0xfc, 0x45, 0xb8, 0x3f, 0x49, 0xd7, 0xaa, 0x42, 0xb8, 0x3b, 0x25, - 0x6c, 0x07, 0xc2, 0x09, 0x87, 0x93, 0x9c, 0x56, 0x13, 0xff, 0x76, 0xb1, 0xb6, 0x5c, 0x4b, 0xa6, - 0x09, 0xc3, 0xc3, 0x0d, 0xc7, 0x39, 0x2e, 0x07, 0x87, 0xaf, 0xd4, 0x3f, 0xe2, 0x89, 0x66, 0xc1, - 0xdb, 0x01, 0xae, 0xd8, 0xd8, 0xd1, 0xf0, 0xfd, 0x32, 0x0c, 0xdf, 0x40, 0x49, 0x90, 0xb1, 0x4e, - 0x84, 0x6a, 0x49, 0x1f, 0xb1, 0x69, 0x8d, 0x4f, 0xa2, 0xa1, 0x02, 0x19, 0x57, 0xc2, 0xf9, 0xf8, - 0x77, 0x24, 0xba, 0x82, 0xc0, 0x56, 0x88, 0x6f, 0x0a, 0xe7, 0xf1, 0x23, 0xac, 0xbc, 0xeb, 0xd2, - 0x0c, 0xe1, 0x1d, 0xec, 0xef, 0xf8, 0x41, 0x28, 0xa5, 0x12, 0xc1, 0x66, 0xff, 0x06, 0xe3, 0xd7, - 0xfa, 0x2c, 0x7a, 0xb3, 0x5e, 0xa3, 0x29, 0x76, 0x7e, 0x0f, 0x06, 0xf4, 0xe5, 0x36, 0x99, 0xe0, - 0xcf, 0x68, 0x9d, 0xbd, 0xb3, 0x2f, 0x54, 0x01, 0xf4, 0x59, 0x36, 0x71, 0xbf, 0xf9, 0xab, 0x6d, - 0xf1, 0xa6, 0x63, 0x12, 0xd4, 0x5f, 0xbd, 0xf5, 0xa0, 0xf6, 0xf8, 0xed, 0x0b, 0xb3, 0x53, 0xf1, - 0xe3, 0xb7, 0x2f, 0x2c, 0x4e, 0x2d, 0xdd, 0xfc, 0x77, 0xed, 0xcc, 0xe7, 0x7f, 0xc0, 0x2e, 0x53, - 0x13, 0x66, 0x7c, 0x1f, 0x2c, 0x1a, 0x97, 0x4b, 0x99, 0x0c, 0xd1, 0xef, 0x43, 0x30, 0xba, 0xc5, - 0x26, 0xd3, 0xc2, 0x5a, 0x6c, 0x99, 0xd1, 0xe2, 0xce, 0x27, 0x97, 0xca, 0xe0, 0xf7, 0x18, 0x8b, - 0xae, 0xb3, 0x8b, 0x52, 0xa7, 0x96, 0x1a, 0x28, 0x7c, 0x06, 0x26, 0xa3, 0x40, 0x74, 0x83, 0x31, - 0x5d, 0xf4, 0x82, 0xdc, 0x85, 0x8f, 0xbc, 0xe4, 0xa2, 0x2e, 0x7a, 0xa4, 0x75, 0x2b, 0xcd, 0x17, - 0xff, 0x5d, 0x38, 0xf7, 0xe2, 0x64, 0xa1, 0xf6, 0xd3, 0xc9, 0x42, 0xed, 0xe7, 0x93, 0x85, 0xda, - 0x7f, 0x4e, 0x16, 0x6a, 0xff, 0x7c, 0xb9, 0x70, 0xee, 0xa7, 0x97, 0x0b, 0xe7, 0x7e, 0x7e, 0xb9, - 0x70, 0xee, 0x87, 0xc9, 0x53, 0x5b, 0xdf, 0x7d, 0x97, 0x7a, 0xf1, 0xf3, 0xff, 0x05, 0x00, 0x00, - 0xff, 0xff, 0x9f, 0x8b, 0xd4, 0xd9, 0x88, 0x0f, 0x00, 0x00, + // 1981 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x5f, 0x53, 0x1c, 0xc7, + 0x11, 0xd7, 0x59, 0xb6, 0x23, 0x06, 0x81, 0x60, 0x8d, 0xcc, 0x02, 0x82, 0x03, 0xfd, 0xb1, 0x90, + 0x65, 0x83, 0x2d, 0x3b, 0x8e, 0xec, 0x54, 0x12, 0x9b, 0x03, 0xac, 0x3f, 0xd8, 0xa0, 0x3d, 0x64, + 0x55, 0x9c, 0x54, 0x4d, 0x0d, 0xbb, 0x7d, 0x77, 0x63, 0x76, 0x67, 0xf6, 0x66, 0x66, 0x81, 0xe3, + 0x21, 0x9f, 0x21, 0x55, 0x79, 0xce, 0xf7, 0xf1, 0xa3, 0x1f, 0xfd, 0x44, 0x25, 0xe8, 0x43, 0xa4, + 0x4a, 0x4f, 0xa9, 0xee, 0xd9, 0xbd, 0x3b, 0x04, 0x76, 0xaa, 0xf2, 0x76, 0xd7, 0xbf, 0x5f, 0xff, + 0xa6, 0xa7, 0xa7, 0xbb, 0x67, 0x96, 0xad, 0xda, 0x6e, 0xba, 0x6a, 0xc1, 0x5a, 0xa9, 0x55, 0x22, + 0x9c, 0xc8, 0xf7, 0x56, 0x53, 0x1d, 0x8b, 0x94, 0x6b, 0x95, 0xf6, 0x78, 0x09, 0x70, 0x44, 0x56, + 0x72, 0xa3, 0x9d, 0x0e, 0xe6, 0x62, 0x1d, 0xef, 0x1b, 0x2d, 0xe2, 0xce, 0x8a, 0xed, 0xa6, 0x2b, + 0x67, 0x5c, 0x67, 0xa7, 0xda, 0xba, 0xad, 0x89, 0xb7, 0x8a, 0xbf, 0xbc, 0xcb, 0xcd, 0xff, 0xcc, + 0xb3, 0xa9, 0x2d, 0x14, 0xdd, 0x56, 0x69, 0xaf, 0xe9, 0x1d, 0xd6, 0x85, 0x13, 0xc1, 0x07, 0x2c, + 0xb0, 0xe2, 0x00, 0xb8, 0x13, 0x7b, 0x29, 0x58, 0x9e, 0x1b, 0x68, 0xc9, 0xa3, 0xb0, 0xb6, 0x58, + 0x5b, 0x1e, 0x89, 0x26, 0x10, 0xd9, 0x25, 0x60, 0x87, 0xec, 0xc1, 0x5f, 0xd8, 0x9c, 0xce, 0x9d, + 0xcc, 0xe4, 0x31, 0x18, 0xde, 0xda, 0xe7, 0xb1, 0xb0, 0xb1, 0x48, 0xc0, 0xf2, 0x54, 0x66, 0xd2, + 0x85, 0x6f, 0x2c, 0xd6, 0x96, 0x2f, 0xaf, 0xdd, 0x38, 0x3d, 0xa9, 0x87, 0xdb, 0x15, 0x6d, 0xf3, + 0x69, 0xa3, 0x24, 0x6d, 0x21, 0x27, 0x0a, 0xfb, 0x02, 0x9b, 0xfb, 0x67, 0x90, 0xe0, 0x53, 0x76, + 0xd5, 0xba, 0xcc, 0x71, 0x27, 0x33, 0xd0, 0x85, 0x0b, 0x2f, 0x93, 0xda, 0xe4, 0xab, 0x93, 0xfa, + 0x18, 0x9a, 0x56, 0xd6, 0x0b, 0x23, 0x9c, 0xd4, 0x2a, 0x1a, 0x45, 0xda, 0xae, 0x67, 0x05, 0x8f, + 0xd8, 0xb4, 0x4c, 0x52, 0xe0, 0x52, 0xf5, 0x53, 0x55, 0x09, 0xbc, 0xf9, 0x4b, 0x02, 0x53, 0xe8, + 0xf1, 0x58, 0x95, 0x79, 0xa8, 0x94, 0x38, 0xbb, 0x55, 0x29, 0x39, 0x23, 0x94, 0x15, 0x31, 0x92, + 0xcf, 0xa9, 0xbe, 0xf5, 0x4b, 0xaa, 0x75, 0xaf, 0xba, 0x3b, 0xf0, 0x7d, 0x6d, 0x81, 0xcf, 0xd8, + 0xb4, 0xd2, 0x4e, 0xc6, 0xc0, 0x13, 0x69, 0xf3, 0x54, 0xe0, 0xe1, 0x1e, 0x80, 0x91, 0xae, 0x17, + 0xbe, 0xbd, 0x58, 0x5b, 0x1e, 0x8b, 0xae, 0x7b, 0x78, 0xdd, 0xa3, 0xcd, 0x12, 0x0c, 0x56, 0xd8, + 0x3b, 0x06, 0xb4, 0x49, 0xc0, 0xf0, 0x1f, 0xb4, 0x54, 0x55, 0xb6, 0x7f, 0x83, 0x81, 0x44, 0x93, + 0x25, 0xf4, 0x04, 0x11, 0x9f, 0xc8, 0x8f, 0xd8, 0x54, 0x02, 0x2d, 0x51, 0xa4, 0x8e, 0xbb, 0x23, + 0xc5, 0x73, 0x23, 0x35, 0x2d, 0x72, 0x85, 0x1c, 0x82, 0x12, 0xdb, 0x3d, 0x52, 0x3b, 0x25, 0x12, + 0x7c, 0xcc, 0xae, 0x0f, 0x7b, 0x18, 0x10, 0x09, 0x55, 0x5f, 0x38, 0xb2, 0x58, 0x5b, 0xbe, 0x32, + 0xec, 0x12, 0x81, 0x48, 0xb0, 0x86, 0x82, 0x35, 0xb6, 0x30, 0xec, 0x52, 0x58, 0xe0, 0x2d, 0x9d, + 0xa6, 0xfa, 0x10, 0x0c, 0xf9, 0xdb, 0x90, 0x91, 0xef, 0xec, 0xc0, 0xf7, 0xb9, 0x85, 0xcd, 0x92, + 0x82, 0x32, 0x36, 0xd8, 0x66, 0xb7, 0x73, 0x61, 0x9c, 0x14, 0x69, 0xda, 0xc3, 0x9c, 0x38, 0x23, + 0xf7, 0x0a, 0x07, 0x09, 0xcf, 0x53, 0xa1, 0x2c, 0x5a, 0xb0, 0xf8, 0x92, 0x70, 0x94, 0x94, 0x96, + 0xfa, 0xdc, 0xf5, 0x01, 0x75, 0x07, 0x99, 0xeb, 0x25, 0x31, 0x78, 0xc8, 0x06, 0xe5, 0x45, 0x21, + 0x75, 0xa4, 0x75, 0xba, 0x6d, 0x44, 0x66, 0xc3, 0xab, 0x24, 0xf2, 0x6e, 0x1f, 0x7f, 0x6e, 0xe1, + 0x51, 0x1f, 0x0d, 0xbe, 0x64, 0xf3, 0x67, 0x3d, 0xb3, 0x22, 0x75, 0x92, 0xc7, 0x3a, 0xe5, 0xd6, + 0x09, 0x67, 0xc3, 0x31, 0x72, 0x9f, 0x19, 0x76, 0xff, 0x06, 0x29, 0x0d, 0x9d, 0x36, 0x91, 0x10, + 0x7c, 0xc1, 0x66, 0xa8, 0x6d, 0xa5, 0xeb, 0xf1, 0x8a, 0x95, 0x70, 0x0b, 0xc2, 0xc4, 0x9d, 0x70, + 0x9c, 0xbc, 0xa7, 0x2b, 0x42, 0xd5, 0x1d, 0x49, 0x93, 0xe0, 0x60, 0x89, 0x5d, 0xb5, 0xa2, 0x05, + 0xbc, 0xc8, 0x13, 0xe1, 0xc0, 0x86, 0xd7, 0x88, 0x3e, 0x8a, 0xb6, 0xe7, 0xde, 0x14, 0xfc, 0x99, + 0xcd, 0x61, 0x73, 0x82, 0xe1, 0xa9, 0xd6, 0xfb, 0x45, 0x5e, 0x96, 0x42, 0x4b, 0x63, 0x23, 0xda, + 0x70, 0x02, 0x3d, 0xd6, 0xe6, 0x4e, 0x4f, 0xea, 0xd3, 0x3b, 0x44, 0xdb, 0x22, 0x16, 0x55, 0xc5, + 0xa6, 0x36, 0x9b, 0x4f, 0x6d, 0x34, 0x9d, 0x5f, 0x04, 0xec, 0x5b, 0xac, 0xaf, 0x63, 0xd9, 0x3e, + 0x16, 0x6d, 0xd2, 0xe4, 0xa0, 0x7c, 0xd6, 0x27, 0x29, 0x88, 0x49, 0x0f, 0x21, 0x7f, 0xc3, 0x03, + 0xc1, 0x57, 0x6c, 0xde, 0x40, 0xb7, 0x90, 0x06, 0x38, 0x1c, 0xe5, 0xa9, 0x8c, 0xa5, 0xc3, 0x22, + 0xcb, 0x84, 0xe9, 0xf1, 0x7d, 0xe8, 0xd9, 0x30, 0xf0, 0x27, 0x5f, 0x92, 0x36, 0x4a, 0xce, 0x8e, + 0xa7, 0x3c, 0x85, 0x9e, 0xc5, 0x56, 0x68, 0x69, 0x13, 0x03, 0xc7, 0x11, 0x93, 0x6b, 0xa9, 0x1c, + 0x37, 0x60, 0x9d, 0x30, 0x2e, 0x7c, 0x87, 0x9c, 0xaf, 0x13, 0xdc, 0xac, 0xd0, 0xc8, 0x83, 0xc1, + 0x43, 0x36, 0x23, 0xb0, 0x82, 0x70, 0x50, 0xe5, 0xc2, 0x00, 0x17, 0x16, 0x93, 0x4d, 0x05, 0x13, + 0x4e, 0x79, 0x4f, 0x22, 0xec, 0x78, 0xfc, 0x2b, 0xbb, 0x9d, 0x3b, 0xac, 0x11, 0xdc, 0xa4, 0x83, + 0x2c, 0xaf, 0x06, 0x5d, 0xb5, 0xc9, 0xeb, 0x7e, 0x93, 0x08, 0xf9, 0x49, 0x57, 0x6d, 0x72, 0x9b, + 0xdd, 0x96, 0x59, 0xb9, 0xb9, 0x58, 0xa7, 0x45, 0xa6, 0x38, 0xd5, 0x1f, 0xf6, 0xb5, 0x54, 0xed, + 0xbe, 0xc0, 0xbb, 0xbe, 0x36, 0x2b, 0x6e, 0x83, 0xa8, 0x3b, 0x43, 0xcc, 0x4a, 0xf0, 0x05, 0xbb, + 0xa7, 0x0f, 0xc0, 0x18, 0x99, 0x54, 0xc5, 0x65, 0xa0, 0x8d, 0x83, 0xe5, 0x58, 0x2b, 0xe0, 0xb1, + 0x56, 0x2d, 0x39, 0x50, 0x0d, 0x49, 0xf5, 0x76, 0xe5, 0x40, 0x95, 0x16, 0x11, 0xfd, 0x7b, 0xad, + 0xa0, 0x41, 0xe4, 0x4a, 0xf8, 0x4f, 0xec, 0x46, 0x47, 0xd8, 0x0e, 0xb7, 0x1d, 0x61, 0x12, 0x48, + 0xb8, 0x54, 0x09, 0x1c, 0x0d, 0x6d, 0x71, 0xc6, 0x57, 0x2e, 0x72, 0x9a, 0x9e, 0xf2, 0xd8, 0x33, + 0x2a, 0x81, 0xcf, 0xd9, 0x0c, 0xb6, 0x1a, 0xe5, 0xb5, 0x55, 0xa4, 0xa9, 0xcf, 0x11, 0xb7, 0xb1, + 0x50, 0x36, 0x9c, 0xf5, 0x6d, 0x53, 0x11, 0x36, 0x8b, 0x34, 0xa5, 0x44, 0x35, 0x11, 0x0d, 0x7e, + 0xcf, 0x66, 0xfb, 0x59, 0xb2, 0x90, 0x42, 0xec, 0xa8, 0x22, 0x7d, 0x1d, 0x87, 0x73, 0xbe, 0xea, + 0x2b, 0x46, 0x93, 0x08, 0x9b, 0xda, 0xf8, 0x9a, 0x0e, 0x96, 0xd9, 0x84, 0x54, 0x16, 0x8c, 0xe3, + 0x2d, 0x61, 0x1d, 0xcf, 0x85, 0xeb, 0x84, 0x37, 0xc8, 0x65, 0xdc, 0xdb, 0x37, 0x85, 0x75, 0x3b, + 0xc2, 0x75, 0x82, 0x47, 0x6c, 0x49, 0xa4, 0x0e, 0x4c, 0x75, 0x12, 0xae, 0x97, 0x03, 0x6f, 0x83, + 0x02, 0x23, 0xd2, 0xfe, 0x3e, 0xe7, 0xc9, 0x75, 0x9e, 0x88, 0xfe, 0x18, 0x76, 0x7b, 0x39, 0x7c, + 0xed, 0x59, 0xd5, 0x5e, 0x3f, 0x64, 0x81, 0xed, 0xa9, 0xb8, 0x63, 0xb4, 0xd2, 0x85, 0xe5, 0xb1, + 0xce, 0x70, 0x94, 0x2e, 0xf8, 0x2a, 0x18, 0x42, 0x1a, 0x04, 0x04, 0xef, 0xb1, 0x6b, 0x5e, 0x9e, + 0x5b, 0xe8, 0x52, 0x46, 0xc2, 0x3a, 0x71, 0xc7, 0xbc, 0xb9, 0x09, 0x5d, 0x4c, 0x44, 0xb0, 0xcb, + 0xee, 0x96, 0xbc, 0x42, 0xc9, 0x6e, 0x01, 0xfc, 0x50, 0xba, 0x8e, 0x2e, 0x9c, 0x3f, 0x0c, 0x3c, + 0x5d, 0xeb, 0x8c, 0x90, 0xca, 0xd9, 0x70, 0x89, 0xfc, 0x6f, 0x79, 0xfa, 0x73, 0x62, 0xbf, 0xf0, + 0x64, 0x3a, 0x96, 0xc6, 0x80, 0x1a, 0xfc, 0x81, 0xcd, 0x59, 0x57, 0xec, 0xf1, 0x58, 0x38, 0x91, + 0xea, 0xf6, 0xeb, 0xb5, 0x7b, 0x93, 0x94, 0x42, 0xa4, 0x34, 0x3c, 0xe3, 0x6c, 0x09, 0x3f, 0x63, + 0x77, 0xe0, 0x28, 0x07, 0x23, 0x33, 0x50, 0x4e, 0xa4, 0xb8, 0xd9, 0x9c, 0xc6, 0x6b, 0x99, 0x45, + 0x03, 0x87, 0x46, 0xe2, 0xb8, 0xb9, 0x45, 0xd7, 0xfd, 0xcd, 0x61, 0x72, 0xa3, 0xe4, 0xfa, 0x44, + 0x46, 0x25, 0x33, 0xf8, 0x2b, 0xbb, 0x1f, 0xeb, 0xbc, 0x77, 0xb6, 0x15, 0x0e, 0x3b, 0xa0, 0x78, + 0x02, 0x52, 0x39, 0x30, 0x29, 0x88, 0x03, 0xb4, 0x51, 0xa8, 0xe1, 0x6d, 0x8a, 0xf0, 0x2e, 0xba, + 0x0c, 0xb7, 0xc4, 0x8b, 0x0e, 0xa8, 0xf5, 0x33, 0x7c, 0x0a, 0x1c, 0x47, 0x68, 0x95, 0x6d, 0x67, + 0x40, 0x64, 0xdc, 0x00, 0x56, 0x0e, 0x5d, 0xaf, 0xe1, 0x1d, 0x5f, 0x4c, 0x65, 0xde, 0x09, 0x8f, + 0x06, 0xb0, 0xbf, 0x24, 0x6d, 0x91, 0x3a, 0xcb, 0xf7, 0x8a, 0x16, 0xce, 0x49, 0x2b, 0x8f, 0x21, + 0x7c, 0xaf, 0xba, 0x24, 0x09, 0x5a, 0x23, 0xa4, 0x29, 0x8f, 0x01, 0xaf, 0x8a, 0xdc, 0xe8, 0x5c, + 0xb4, 0x85, 0xc3, 0x2b, 0x3f, 0x2f, 0x1c, 0xa7, 0x7b, 0x54, 0xaa, 0x76, 0x78, 0xd7, 0xd7, 0x7c, + 0x1f, 0x7f, 0x8c, 0xf0, 0x76, 0x89, 0x06, 0xff, 0xa8, 0xb1, 0x33, 0xa9, 0xa2, 0x9b, 0xcb, 0x76, + 0x53, 0x1a, 0x42, 0x94, 0x90, 0x4c, 0x27, 0x10, 0x2e, 0xd3, 0x3b, 0x61, 0xf3, 0xf4, 0xa4, 0x5e, + 0xdf, 0x18, 0x62, 0xe3, 0xdd, 0xd5, 0x7c, 0xb6, 0xb5, 0x53, 0x72, 0xbf, 0xd1, 0x09, 0xbc, 0xfa, + 0xdf, 0x94, 0xa8, 0x0e, 0xaf, 0x11, 0x6c, 0x37, 0x1d, 0x26, 0x04, 0x9b, 0x6c, 0x0c, 0xe3, 0xe0, + 0x18, 0x08, 0xad, 0x7f, 0x8f, 0xd6, 0xbf, 0x79, 0x7a, 0x52, 0x1f, 0x2d, 0x05, 0xcb, 0xb5, 0xae, + 0x95, 0x7f, 0x37, 0x8e, 0x20, 0x26, 0xed, 0x51, 0x74, 0x6c, 0x76, 0x53, 0xd2, 0x79, 0xc1, 0x66, + 0x2c, 0x18, 0x29, 0x52, 0xae, 0xb4, 0xc9, 0x44, 0x2a, 0x8f, 0x29, 0xbf, 0x5e, 0xf3, 0x7d, 0xd2, + 0x9c, 0x7b, 0x75, 0x52, 0x9f, 0x6e, 0x12, 0xe9, 0xdb, 0x61, 0x0e, 0x89, 0x4d, 0xdb, 0x8b, 0x81, + 0x60, 0x9b, 0x4d, 0x2b, 0x38, 0xe4, 0x36, 0xee, 0x40, 0x26, 0x78, 0xdc, 0x11, 0xaa, 0x0d, 0xc6, + 0xcb, 0xde, 0x27, 0xd9, 0xf0, 0xd5, 0x49, 0x7d, 0xea, 0x5b, 0x38, 0x6c, 0x12, 0xa3, 0xe1, 0x09, + 0xa4, 0x39, 0xa5, 0x2e, 0xb0, 0x06, 0x7f, 0x63, 0xe3, 0x16, 0xba, 0x05, 0xa8, 0x18, 0x78, 0x2c, + 0xe2, 0x0e, 0x84, 0x1f, 0x2c, 0x5e, 0x5e, 0x1e, 0x7d, 0xb0, 0xbe, 0xf2, 0x2b, 0xef, 0xe3, 0x95, + 0x8b, 0x5e, 0xc1, 0x2b, 0xcd, 0x52, 0xa7, 0x81, 0x32, 0x1b, 0xca, 0x99, 0x9e, 0x7f, 0xe0, 0x9d, + 0xb1, 0x47, 0x63, 0x76, 0xf8, 0x6f, 0x70, 0x9f, 0x4d, 0xe6, 0xa9, 0x88, 0x01, 0xcf, 0xa4, 0xdf, + 0x93, 0x1f, 0x52, 0xe9, 0x4c, 0xf4, 0x81, 0xaa, 0x17, 0x73, 0x16, 0x54, 0x0f, 0xc9, 0xc2, 0x82, + 0xe1, 0xf4, 0x2c, 0x0f, 0x57, 0xb0, 0xf1, 0xd6, 0xd6, 0x5e, 0x9d, 0xd4, 0xff, 0xd8, 0x96, 0xae, + 0x53, 0xec, 0xad, 0xc4, 0x3a, 0x5b, 0xed, 0x87, 0x9f, 0xec, 0x0d, 0x7e, 0xaf, 0xe6, 0xfb, 0xed, + 0x55, 0x0b, 0x71, 0x81, 0x0f, 0xb6, 0x95, 0xe6, 0xb3, 0xad, 0xe7, 0x16, 0x8c, 0x12, 0x19, 0xec, + 0xa0, 0x52, 0x34, 0x51, 0xaa, 0xa3, 0x95, 0x2c, 0xc1, 0x2a, 0x9b, 0xa2, 0xb7, 0x9c, 0x3e, 0xb4, + 0x1c, 0xbb, 0xd7, 0x81, 0xe2, 0xa9, 0x6e, 0x87, 0xab, 0xbe, 0x23, 0xdc, 0x91, 0x8a, 0xf4, 0xa1, + 0x7d, 0xe1, 0x91, 0x2d, 0xdd, 0xbe, 0xd0, 0x01, 0x8c, 0x09, 0x3f, 0xba, 0xc8, 0x61, 0xc3, 0x98, + 0xe0, 0x1e, 0x9b, 0xec, 0x3b, 0xd0, 0x93, 0x11, 0xe5, 0x3f, 0x26, 0xf6, 0x78, 0xc9, 0xc6, 0x77, + 0x1e, 0x6a, 0x9f, 0xa3, 0xa2, 0xf0, 0x83, 0x73, 0x54, 0x54, 0x7d, 0xc0, 0xae, 0x8b, 0xc2, 0x69, + 0x6e, 0xa0, 0xa3, 0xb3, 0xe1, 0x9b, 0xf6, 0x13, 0x4a, 0xed, 0x3b, 0x08, 0x46, 0x25, 0x56, 0x65, + 0x77, 0x89, 0x5d, 0x95, 0x96, 0xdb, 0x22, 0x07, 0x83, 0xd9, 0x0d, 0x3f, 0xf5, 0xef, 0x27, 0x69, + 0x9b, 0x95, 0x09, 0x77, 0x97, 0x0a, 0xd3, 0x06, 0x7f, 0xc3, 0xe1, 0x24, 0xa7, 0x68, 0xc2, 0xdf, + 0x2e, 0xd6, 0x96, 0x6b, 0xd1, 0x24, 0x61, 0x78, 0xb9, 0xe1, 0x38, 0xc7, 0x70, 0x70, 0xf8, 0x4a, + 0xf5, 0x03, 0xde, 0x68, 0x06, 0x9c, 0xe9, 0x61, 0xc4, 0xda, 0x0c, 0x86, 0xef, 0x67, 0x7e, 0xf8, + 0x7a, 0x4a, 0x84, 0x8c, 0x0d, 0x22, 0x54, 0x21, 0xbd, 0xcf, 0x26, 0x15, 0xae, 0x44, 0x43, 0x05, + 0x12, 0x9e, 0x0a, 0xeb, 0xc2, 0xdf, 0x91, 0xd3, 0x35, 0x04, 0xb6, 0xbd, 0x7d, 0x4b, 0x58, 0x87, + 0x1f, 0x61, 0xe5, 0x5b, 0x97, 0x66, 0x08, 0x6f, 0x63, 0x7f, 0x87, 0x0f, 0x7d, 0x29, 0x95, 0x08, + 0x36, 0xfb, 0xd7, 0x68, 0x0f, 0xf6, 0xd9, 0x78, 0x5c, 0x58, 0xa7, 0x33, 0x7a, 0x66, 0x6a, 0x65, + 0xc3, 0xcf, 0xff, 0xdf, 0xba, 0x6f, 0x90, 0xce, 0xb6, 0x97, 0xa1, 0xba, 0x8f, 0xc6, 0xe2, 0x61, + 0xdb, 0x6c, 0x97, 0x05, 0xe7, 0x9b, 0x23, 0x98, 0x60, 0x97, 0xf7, 0xa1, 0x47, 0x9f, 0x89, 0x63, + 0x11, 0xfe, 0x0c, 0x36, 0xd8, 0x5b, 0x07, 0x22, 0x2d, 0x80, 0xbe, 0x01, 0x47, 0x1f, 0xac, 0xfe, + 0x6a, 0x2c, 0xe7, 0x15, 0x23, 0xef, 0xfd, 0xc5, 0x1b, 0x0f, 0x6b, 0xb3, 0x5f, 0xb2, 0xe0, 0x7c, + 0x5c, 0xc3, 0x4b, 0x8e, 0xf8, 0x25, 0xa7, 0x86, 0x97, 0x1c, 0x19, 0x52, 0x78, 0xf2, 0xe6, 0x95, + 0xe9, 0x89, 0xf0, 0xc9, 0x9b, 0x57, 0x16, 0x27, 0x96, 0x6e, 0xfe, 0xb3, 0x76, 0xe1, 0x0e, 0xee, + 0xb0, 0x71, 0x9a, 0x19, 0x09, 0x3f, 0x00, 0x83, 0xa1, 0x95, 0x9b, 0x19, 0xf3, 0xd6, 0xef, 0xbc, + 0x31, 0xb8, 0xc5, 0xc6, 0xe2, 0xc2, 0x18, 0xec, 0xf0, 0xc1, 0x5a, 0x97, 0xa3, 0xab, 0xa5, 0xf1, + 0x3b, 0xb4, 0x05, 0x37, 0xd8, 0x88, 0x54, 0xb1, 0xa1, 0x7e, 0xf7, 0x5f, 0xad, 0xd1, 0xc0, 0x10, + 0xcc, 0x33, 0xa6, 0x8a, 0xcc, 0xbb, 0x5b, 0xff, 0x4d, 0x1a, 0x8d, 0xa8, 0x22, 0x23, 0x5f, 0xbb, + 0xb6, 0xfa, 0xe3, 0xbf, 0x17, 0x2e, 0xfd, 0x78, 0xba, 0x50, 0xfb, 0xe9, 0x74, 0xa1, 0xf6, 0xf3, + 0xe9, 0x42, 0xed, 0x5f, 0xa7, 0x0b, 0xb5, 0xbf, 0xbf, 0x5c, 0xb8, 0xf4, 0xd3, 0xcb, 0x85, 0x4b, + 0x3f, 0xbf, 0x5c, 0xb8, 0xf4, 0xfd, 0xd8, 0x99, 0xe4, 0xed, 0xbd, 0x4d, 0xa3, 0xe3, 0x93, 0xff, + 0x06, 0x00, 0x00, 0xff, 0xff, 0xdd, 0xa0, 0xca, 0x9c, 0x37, 0x10, 0x00, 0x00, } func (m *LocalOnlySessionData) Marshal() (dAtA []byte, err error) { @@ -450,6 +457,32 @@ func (m *LocalOnlySessionData) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.CustomOptions) > 0 { + keysForCustomOptions := make([]string, 0, len(m.CustomOptions)) + for k := range m.CustomOptions { + keysForCustomOptions = append(keysForCustomOptions, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForCustomOptions) + for iNdEx := len(keysForCustomOptions) - 1; iNdEx >= 0; iNdEx-- { + v := m.CustomOptions[string(keysForCustomOptions[iNdEx])] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintLocalOnlySessionData(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(keysForCustomOptions[iNdEx]) + copy(dAtA[i:], keysForCustomOptions[iNdEx]) + i = encodeVarintLocalOnlySessionData(dAtA, i, uint64(len(keysForCustomOptions[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintLocalOnlySessionData(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3 + i-- + dAtA[i] = 0xca + } + } if m.DisablePlanGists { i-- if m.DisablePlanGists { @@ -1232,6 +1265,14 @@ func (m *LocalOnlySessionData) Size() (n int) { if m.DisablePlanGists { n += 3 } + if len(m.CustomOptions) > 0 { + for k, v := range m.CustomOptions { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovLocalOnlySessionData(uint64(len(k))) + 1 + len(v) + sovLocalOnlySessionData(uint64(len(v))) + n += mapEntrySize + 2 + sovLocalOnlySessionData(uint64(mapEntrySize)) + } + } return n } @@ -2477,6 +2518,133 @@ func (m *LocalOnlySessionData) Unmarshal(dAtA []byte) error { } } m.DisablePlanGists = bool(v != 0) + case 57: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CustomOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalOnlySessionData + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CustomOptions == nil { + m.CustomOptions = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalOnlySessionData + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalOnlySessionData + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLocalOnlySessionData + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipLocalOnlySessionData(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLocalOnlySessionData + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.CustomOptions[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLocalOnlySessionData(dAtA[iNdEx:]) diff --git a/pkg/sql/sessiondatapb/local_only_session_data.proto b/pkg/sql/sessiondatapb/local_only_session_data.proto index e82aee942c33..8c3be5b4f798 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.proto +++ b/pkg/sql/sessiondatapb/local_only_session_data.proto @@ -213,6 +213,9 @@ message LocalOnlySessionData { bool null_ordered_last = 55; // DisablePlanGists indicates whether we should disable automatic gists. bool disable_plan_gists = 56; + // CustomOptions contains a map of all custom session settings. + // These session variables have at least one period in their name. + map custom_options = 57; /////////////////////////////////////////////////////////////////////////// // WARNING: consider whether a session parameter you're adding needs to // diff --git a/pkg/sql/show_var.go b/pkg/sql/show_var.go new file mode 100644 index 000000000000..6790083ea0c7 --- /dev/null +++ b/pkg/sql/show_var.go @@ -0,0 +1,55 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package sql + +import ( + "context" + "strings" + + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" +) + +// showVarNode represents a SHOW statement. +// This is reached if contains a period. +type showVarNode struct { + name string + shown bool + val string +} + +func (s *showVarNode) startExec(params runParams) error { + return nil +} + +func (s *showVarNode) Next(params runParams) (bool, error) { + if s.shown { + return false, nil + } + s.shown = true + + _, v, err := getSessionVar(s.name, false /* missingOk */) + if err != nil { + return false, err + } + s.val = v.Get(params.extendedEvalCtx) + return true, nil +} + +func (s *showVarNode) Values() tree.Datums { + return tree.Datums{tree.NewDString(s.val)} +} + +func (s *showVarNode) Close(ctx context.Context) {} + +// ShowVar shows a session variable. +func (p *planner) ShowVar(ctx context.Context, n *tree.ShowVar) (planNode, error) { + return &showVarNode{name: strings.ToLower(n.Name)}, nil +} diff --git a/pkg/sql/testdata/session_migration/session_migration b/pkg/sql/testdata/session_migration/session_migration index 6a9138b1de20..c4bc0ddfc540 100644 --- a/pkg/sql/testdata/session_migration/session_migration +++ b/pkg/sql/testdata/session_migration/session_migration @@ -7,7 +7,8 @@ CREATE DATABASE d; USE d exec SET extra_float_digits = -1; SET enable_zigzag_join = false; -SET search_path = public +SET search_path = public; +SET custom_option.custom_option = 'test' ---- let $x diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index b3cc166c66b0..a41ed76ed1eb 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -1831,6 +1831,9 @@ func init() { res := make([]string, 0, len(varGen)) for vName := range varGen { res = append(res, vName) + if strings.Contains(vName, ".") { + panic(fmt.Sprintf(`no session variables with "." can be created as they are reserved for custom options, found %s`, vName)) + } } sort.Strings(res) return res @@ -1991,6 +1994,13 @@ func IsSessionVariableConfigurable(varName string) (exists, configurable bool) { return exists, v.Set != nil } +// IsCustomOptionSessionVariable returns whether the given varName is a custom +// session variable. +func IsCustomOptionSessionVariable(varName string) bool { + isCustom, _ := getCustomOptionSessionVar(varName) + return isCustom +} + // CheckSessionVariableValueValid returns an error if the value is not valid // for the given variable. It also returns an error if there is no variable with // the given name or if the variable is not configurable. @@ -2026,6 +2036,9 @@ func getSessionVar(name string, missingOk bool) (bool, sessionVar, error) { v, ok := varGen[name] if !ok { + if isCustom, v := getCustomOptionSessionVar(name); isCustom { + return true, v, nil + } if missingOk { return false, sessionVar{}, nil } @@ -2035,6 +2048,25 @@ func getSessionVar(name string, missingOk bool) (bool, sessionVar, error) { return true, v, nil } +func getCustomOptionSessionVar(varName string) (isCustom bool, sv sessionVar) { + if strings.Contains(varName, ".") { + return true, sessionVar{ + Get: func(evalCtx *extendedEvalContext) string { + return evalCtx.SessionData().CustomOptions[varName] + }, + Set: func(ctx context.Context, m sessionDataMutator, val string) error { + // TODO(#72026): do some memory accounting. + m.SetCustomOption(varName, val) + return nil + }, + GlobalDefault: func(sv *settings.Values) string { + return "" + }, + } + } + return false, sessionVar{} +} + // GetSessionVar implements the EvalSessionAccessor interface. func (p *planner) GetSessionVar( _ context.Context, varName string, missingOk bool, diff --git a/pkg/sql/walk.go b/pkg/sql/walk.go index 45ce835c184f..9eaa8b8fcf4c 100644 --- a/pkg/sql/walk.go +++ b/pkg/sql/walk.go @@ -428,6 +428,7 @@ var planNodeNames = map[reflect.Type]string{ reflect.TypeOf(&showFingerprintsNode{}): "show fingerprints", reflect.TypeOf(&showTraceNode{}): "show trace for", reflect.TypeOf(&showTraceReplicaNode{}): "replica trace", + reflect.TypeOf(&showVarNode{}): "show", reflect.TypeOf(&sortNode{}): "sort", reflect.TypeOf(&splitNode{}): "split", reflect.TypeOf(&topKNode{}): "top-k", From 2cadbe8d2687d77b662be1d732f8c392207921a7 Mon Sep 17 00:00:00 2001 From: Adam Storm Date: Tue, 26 Oct 2021 11:32:38 -0400 Subject: [PATCH 157/205] cli: Enable multi-tenant demo by default 71026 added support for cockroach demo to run in multi-tenant mode. In an effort to get broader testing around multi-tenancy, this PR enables multi-tenant mode by default. Of note is that multi-tenant mode had to be disabled for several tests due to missing functionality (gossip commands, zone configurations on tenants, the ability to split ranges, etc.). Release note (cli change): cockroach demo now runs by default in multi-tenant mode. See 71026 for more details. --- pkg/cli/context.go | 1 + pkg/cli/demo.go | 10 +++- pkg/cli/demo_locality_test.go | 13 ++--- pkg/cli/demo_test.go | 49 ++++++++++--------- pkg/cli/democluster/demo_cluster.go | 2 +- .../interactive_tests/test_demo_node_cmds.tcl | 5 +- .../interactive_tests/test_demo_workload.tcl | 4 +- .../test_disable_replication.tcl | 4 +- pkg/workload/workloadsql/workloadsql.go | 5 +- 9 files changed, 55 insertions(+), 38 deletions(-) diff --git a/pkg/cli/context.go b/pkg/cli/context.go index 5e5f11fc2876..a070b155eb56 100644 --- a/pkg/cli/context.go +++ b/pkg/cli/context.go @@ -585,6 +585,7 @@ func setDemoContextDefaults() { demoCtx.SQLPort, _ = strconv.Atoi(base.DefaultPort) demoCtx.HTTPPort, _ = strconv.Atoi(base.DefaultHTTPPort) demoCtx.WorkloadMaxQPS = 25 + demoCtx.Multitenant = true } // stmtDiagCtx captures the command-line parameters of the 'statement-diag' diff --git a/pkg/cli/demo.go b/pkg/cli/demo.go index d08656ec7e03..25abf77bedb2 100644 --- a/pkg/cli/demo.go +++ b/pkg/cli/demo.go @@ -120,6 +120,7 @@ func incrementTelemetryCounters(cmd *cobra.Command) { func checkDemoConfiguration( cmd *cobra.Command, gen workload.Generator, ) (workload.Generator, error) { + f := flagSetForCmd(cmd) if gen == nil && !demoCtx.NoExampleDatabase { // Use a default dataset unless prevented by --no-example-database. gen = defaultGenerator @@ -169,7 +170,7 @@ func checkDemoConfiguration( } // If the geo-partitioned replicas flag was given and the nodes have changed, throw an error. - if flagSetForCmd(cmd).Lookup(cliflags.DemoNodes.Name).Changed { + if f.Lookup(cliflags.DemoNodes.Name).Changed { if demoCtx.NumNodes != 9 { return nil, errors.Newf("--nodes with a value different from 9 cannot be used with %s", geoFlag) } @@ -195,6 +196,13 @@ func checkDemoConfiguration( } } + // If the user has specified the --global flag, disable multi-tenant mode + // unless the user has requested it explicitly. This is required until we + // address #72231. + if demoCtx.SimulateLatency && !f.Lookup(cliflags.DemoMultitenant.Name).Changed { + demoCtx.Multitenant = false + } + return gen, nil } diff --git a/pkg/cli/demo_locality_test.go b/pkg/cli/demo_locality_test.go index a2471a3a8c15..269d5530ecc7 100644 --- a/pkg/cli/demo_locality_test.go +++ b/pkg/cli/demo_locality_test.go @@ -25,10 +25,11 @@ func Example_demo_locality() { defer democluster.TestingForceRandomizeDemoPorts()() + // Disable multi-tenant for this test due to the unsupported gossip commands. testData := [][]string{ - {`demo`, `--nodes`, `3`, `-e`, `select node_id, locality from crdb_internal.gossip_nodes order by node_id`}, - {`demo`, `--nodes`, `9`, `-e`, `select node_id, locality from crdb_internal.gossip_nodes order by node_id`}, - {`demo`, `--nodes`, `3`, `--demo-locality=region=us-east1:region=us-east2:region=us-east3`, + {`demo`, `--nodes`, `3`, `--multitenant=false`, `-e`, `select node_id, locality from crdb_internal.gossip_nodes order by node_id`}, + {`demo`, `--nodes`, `9`, `--multitenant=false`, `-e`, `select node_id, locality from crdb_internal.gossip_nodes order by node_id`}, + {`demo`, `--nodes`, `3`, `--multitenant=false`, `--demo-locality=region=us-east1:region=us-east2:region=us-east3`, `-e`, `select node_id, locality from crdb_internal.gossip_nodes order by node_id`}, } setCLIDefaultsForTests() @@ -45,12 +46,12 @@ func Example_demo_locality() { } // Output: - // demo --nodes 3 -e select node_id, locality from crdb_internal.gossip_nodes order by node_id + // demo --nodes 3 --multitenant=false -e select node_id, locality from crdb_internal.gossip_nodes order by node_id // node_id locality // 1 region=us-east1,az=b // 2 region=us-east1,az=c // 3 region=us-east1,az=d - // demo --nodes 9 -e select node_id, locality from crdb_internal.gossip_nodes order by node_id + // demo --nodes 9 --multitenant=false -e select node_id, locality from crdb_internal.gossip_nodes order by node_id // node_id locality // 1 region=us-east1,az=b // 2 region=us-east1,az=c @@ -61,7 +62,7 @@ func Example_demo_locality() { // 7 region=europe-west1,az=b // 8 region=europe-west1,az=c // 9 region=europe-west1,az=d - // demo --nodes 3 --demo-locality=region=us-east1:region=us-east2:region=us-east3 -e select node_id, locality from crdb_internal.gossip_nodes order by node_id + // demo --nodes 3 --multitenant=false --demo-locality=region=us-east1:region=us-east2:region=us-east3 -e select node_id, locality from crdb_internal.gossip_nodes order by node_id // node_id locality // 1 region=us-east1 // 2 region=us-east2 diff --git a/pkg/cli/demo_test.go b/pkg/cli/demo_test.go index ec1c569c9d97..93ad87d876ee 100644 --- a/pkg/cli/demo_test.go +++ b/pkg/cli/demo_test.go @@ -22,21 +22,22 @@ func Example_demo() { defer democluster.TestingForceRandomizeDemoPorts()() + // Disable multi-tenant, as it requires a CCL binary testData := [][]string{ - {`demo`, `-e`, `show database`}, - {`demo`, `-e`, `show database`, `--no-example-database`}, - {`demo`, `-e`, `show application_name`}, - {`demo`, `--format=table`, `-e`, `show database`}, - {`demo`, `-e`, `select 1 as "1"`, `-e`, `select 3 as "3"`}, - {`demo`, `--echo-sql`, `-e`, `select 1 as "1"`}, - {`demo`, `--set=errexit=0`, `-e`, `select nonexistent`, `-e`, `select 123 as "123"`}, - {`demo`, `startrek`, `-e`, `SELECT database_name, owner FROM [show databases]`}, - {`demo`, `startrek`, `-e`, `SELECT database_name, owner FROM [show databases]`, `--format=table`}, + {`demo`, `--multitenant=false`, `-e`, `show database`}, + {`demo`, `--multitenant=false`, `-e`, `show database`, `--no-example-database`}, + {`demo`, `--multitenant=false`, `-e`, `show application_name`}, + {`demo`, `--multitenant=false`, `--format=table`, `-e`, `show database`}, + {`demo`, `--multitenant=false`, `-e`, `select 1 as "1"`, `-e`, `select 3 as "3"`}, + {`demo`, `--multitenant=false`, `--echo-sql`, `-e`, `select 1 as "1"`}, + {`demo`, `--multitenant=false`, `--set=errexit=0`, `-e`, `select nonexistent`, `-e`, `select 123 as "123"`}, + {`demo`, `--multitenant=false`, `startrek`, `-e`, `SELECT database_name, owner FROM [show databases]`}, + {`demo`, `--multitenant=false`, `startrek`, `-e`, `SELECT database_name, owner FROM [show databases]`, `--format=table`}, // Test that if we start with --insecure we cannot perform // commands that require a secure cluster. - {`demo`, `-e`, `CREATE USER test WITH PASSWORD 'testpass'`}, - {`demo`, `--insecure`, `-e`, `CREATE USER test WITH PASSWORD 'testpass'`}, - {`demo`, `--geo-partitioned-replicas`, `--disable-demo-license`}, + {`demo`, `--multitenant=false`, `-e`, `CREATE USER test WITH PASSWORD 'testpass'`}, + {`demo`, `--multitenant=false`, `--insecure`, `-e`, `CREATE USER test WITH PASSWORD 'testpass'`}, + {`demo`, `--multitenant=false`, `--geo-partitioned-replicas`, `--disable-demo-license`}, } setCLIDefaultsForTests() // We must reset the security asset loader here, otherwise the dummy @@ -52,41 +53,41 @@ func Example_demo() { } // Output: - // demo -e show database + // demo --multitenant=false -e show database // database // movr - // demo -e show database --no-example-database + // demo --multitenant=false -e show database --no-example-database // database // defaultdb - // demo -e show application_name + // demo --multitenant=false -e show application_name // application_name // $ cockroach demo - // demo --format=table -e show database + // demo --multitenant=false --format=table -e show database // database // ------------ // movr // (1 row) - // demo -e select 1 as "1" -e select 3 as "3" + // demo --multitenant=false -e select 1 as "1" -e select 3 as "3" // 1 // 1 // 3 // 3 - // demo --echo-sql -e select 1 as "1" + // demo --multitenant=false --echo-sql -e select 1 as "1" // > select 1 as "1" // 1 // 1 - // demo --set=errexit=0 -e select nonexistent -e select 123 as "123" + // demo --multitenant=false --set=errexit=0 -e select nonexistent -e select 123 as "123" // ERROR: column "nonexistent" does not exist // SQLSTATE: 42703 // 123 // 123 - // demo startrek -e SELECT database_name, owner FROM [show databases] + // demo --multitenant=false startrek -e SELECT database_name, owner FROM [show databases] // database_name owner // defaultdb root // postgres root // startrek demo // system node - // demo startrek -e SELECT database_name, owner FROM [show databases] --format=table + // demo --multitenant=false startrek -e SELECT database_name, owner FROM [show databases] --format=table // database_name | owner // ----------------+-------- // defaultdb | root @@ -94,11 +95,11 @@ func Example_demo() { // startrek | demo // system | node // (4 rows) - // demo -e CREATE USER test WITH PASSWORD 'testpass' + // demo --multitenant=false -e CREATE USER test WITH PASSWORD 'testpass' // CREATE ROLE - // demo --insecure -e CREATE USER test WITH PASSWORD 'testpass' + // demo --multitenant=false --insecure -e CREATE USER test WITH PASSWORD 'testpass' // ERROR: setting or updating a password is not supported in insecure mode // SQLSTATE: 28P01 - // demo --geo-partitioned-replicas --disable-demo-license + // demo --multitenant=false --geo-partitioned-replicas --disable-demo-license // ERROR: enterprise features are needed for this demo (--geo-partitioned-replicas) } diff --git a/pkg/cli/democluster/demo_cluster.go b/pkg/cli/democluster/demo_cluster.go index a032a1f5cb77..9dadae142091 100644 --- a/pkg/cli/democluster/demo_cluster.go +++ b/pkg/cli/democluster/demo_cluster.go @@ -1151,7 +1151,7 @@ func (c *transientCluster) SetupWorkload(ctx context.Context, licenseDone <-chan if c.demoCtx.RunWorkload { var sqlURLs []string for i := range c.servers { - sqlURL, err := c.getNetworkURLForServer(ctx, i, true /* includeAppName */, false /* isTenant */) + sqlURL, err := c.getNetworkURLForServer(ctx, i, true /* includeAppName */, c.demoCtx.Multitenant) if err != nil { return err } diff --git a/pkg/cli/interactive_tests/test_demo_node_cmds.tcl b/pkg/cli/interactive_tests/test_demo_node_cmds.tcl index 63cf6a98310d..d9aab7d102e6 100644 --- a/pkg/cli/interactive_tests/test_demo_node_cmds.tcl +++ b/pkg/cli/interactive_tests/test_demo_node_cmds.tcl @@ -3,8 +3,9 @@ source [file join [file dirname $argv0] common.tcl] start_test "Check \\demo commands work as expected" -# Start a demo with 5 nodes. -spawn $argv demo movr --nodes=5 +# Start a demo with 5 nodes. Set multitenant=false due to unsupported +# gossip commands below. +spawn $argv demo movr --nodes=5 --multitenant=false # Ensure db is movr. eexpect "movr>" diff --git a/pkg/cli/interactive_tests/test_demo_workload.tcl b/pkg/cli/interactive_tests/test_demo_workload.tcl index 9190b3c2af03..667fc104c588 100644 --- a/pkg/cli/interactive_tests/test_demo_workload.tcl +++ b/pkg/cli/interactive_tests/test_demo_workload.tcl @@ -39,7 +39,9 @@ end_test start_test "Check that controlling ranges of the movr dataset works" # Reset the timeout. set timeout 30 -spawn $argv demo movr --num-ranges=6 +# Need to disable multi-tenant mode here, as splitting is not supported. +# See 54254 for more details. +spawn $argv demo movr --num-ranges=6 --multitenant=false eexpect "movr>" diff --git a/pkg/cli/interactive_tests/test_disable_replication.tcl b/pkg/cli/interactive_tests/test_disable_replication.tcl index 87f8e6fe00b8..00c5d33cd5db 100644 --- a/pkg/cli/interactive_tests/test_disable_replication.tcl +++ b/pkg/cli/interactive_tests/test_disable_replication.tcl @@ -7,7 +7,9 @@ send "PS1=':''/# '\r" eexpect ":/# " start_test "Check that demo disables replication properly" -send "$argv demo -e 'show zone configuration for range default'\r" +# Disable multitenant until zone configs are properly enabled in tenants. +# See 67679 for more details. +send "$argv demo --multitenant=false -e 'show zone configuration for range default'\r" eexpect "num_replicas = 1" eexpect ":/# " end_test diff --git a/pkg/workload/workloadsql/workloadsql.go b/pkg/workload/workloadsql/workloadsql.go index e2dbce317b0b..618194fe7a24 100644 --- a/pkg/workload/workloadsql/workloadsql.go +++ b/pkg/workload/workloadsql/workloadsql.go @@ -106,8 +106,9 @@ func Split(ctx context.Context, db *gosql.DB, table workload.Table, concurrency // we can't perform splits. _, err := db.Exec("SHOW RANGES FROM TABLE system.descriptor") if err != nil { - if strings.Contains(err.Error(), "not fully contained in tenant") { - log.Infof(ctx, `not perform workload splits; can't split on tenants'`) + if strings.Contains(err.Error(), "not fully contained in tenant") || + strings.Contains(err.Error(), "operation is unsupported in multi-tenancy mode") { + log.Infof(ctx, `skipping workload splits; can't split on tenants'`) //nolint:returnerrcheck return nil } From e8ac90ed1f4bb4fbb78d6edb3f1bd8d2128c4f2b Mon Sep 17 00:00:00 2001 From: Yahor Yuzefovich Date: Mon, 1 Nov 2021 15:56:08 -0700 Subject: [PATCH 158/205] colrpc: disable some warnings when gRPC stream is interrupted Previously, whenever a gRPC stream between the outbox and the inbox is interrupted for any reason, we would log a scary-looking warning. The warning has been quite confusing for users and CRDB employees alike, and it is quite hard to make the warning more useful because the stream can be interrupted for several reasons (e.g. a user canceling a query which is a graceful shutdown, so nothing should be logged; or a node in the cluster crashes which is an ungraceful shutdown, so logging something is probably warranted), and it is not easy - if possible - to distinguish between these different scenarios. I believe originally (about half a year ago) we were too concerned with the problem of proper cancellation of the DistSQL flows, so we wanted as much logging that could help us during the investigations as possible, and now those logs seem less useful to our team while being confusing to everybody else. This commit makes it so that we log these events only if the verbose tracing is enabled on `outbox` file. Release note: None --- pkg/sql/colflow/colrpc/outbox.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/sql/colflow/colrpc/outbox.go b/pkg/sql/colflow/colrpc/outbox.go index be83658ccb4e..d593e2e10595 100644 --- a/pkg/sql/colflow/colrpc/outbox.go +++ b/pkg/sql/colflow/colrpc/outbox.go @@ -228,12 +228,10 @@ func handleStreamErr( flowCtxCancel, outboxCtxCancel context.CancelFunc, ) { if err == io.EOF { - if log.V(1) { - log.Infof(ctx, "Outbox calling outboxCtxCancel after %s EOF", opName) - } + log.VEventf(ctx, 2, "Outbox calling outboxCtxCancel after %s EOF", opName) outboxCtxCancel() } else { - log.Warningf(ctx, "Outbox calling flowCtxCancel after %s connection error: %+v", opName, err) + log.VEventf(ctx, 1, "Outbox calling flowCtxCancel after %s connection error: %+v", opName, err) flowCtxCancel() } } From d9afaf5d590c0d8fbf73dd53299641cf2f9126fb Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Tue, 2 Nov 2021 10:19:38 +1100 Subject: [PATCH 159/205] sql: account for unset custom options This is a mostly refactor diff to allow for errors in the `sessionVar`'s `Get` function. This allows us to detect if a configuration doesn't exist and error accordingly. Release note: None --- pkg/sql/crdb_internal.go | 5 +- pkg/sql/exec_util.go | 3 + pkg/sql/information_schema.go | 5 +- pkg/sql/logictest/testdata/logic_test/set | 3 + pkg/sql/pg_catalog.go | 5 +- pkg/sql/show_var.go | 4 +- pkg/sql/unsupported_vars.go | 8 +- pkg/sql/vars.go | 370 +++++++++++----------- 8 files changed, 214 insertions(+), 189 deletions(-) diff --git a/pkg/sql/crdb_internal.go b/pkg/sql/crdb_internal.go index bfbb7a803454..758ff5030bab 100644 --- a/pkg/sql/crdb_internal.go +++ b/pkg/sql/crdb_internal.go @@ -1452,7 +1452,10 @@ CREATE TABLE crdb_internal.session_variables ( populate: func(ctx context.Context, p *planner, _ catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error { for _, vName := range varNames { gen := varGen[vName] - value := gen.Get(&p.extendedEvalCtx) + value, err := gen.Get(&p.extendedEvalCtx) + if err != nil { + return err + } if err := addRow( tree.NewDString(vName), tree.NewDString(value), diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index 47556a77095d..5390bdd87f90 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -2809,6 +2809,9 @@ func (m *sessionDataMutator) SetLocation(loc *time.Location) { } func (m *sessionDataMutator) SetCustomOption(name, val string) { + if m.data.CustomOptions == nil { + m.data.CustomOptions = make(map[string]string) + } m.data.CustomOptions[name] = val } diff --git a/pkg/sql/information_schema.go b/pkg/sql/information_schema.go index be8c03152ccf..feabe81d8321 100755 --- a/pkg/sql/information_schema.go +++ b/pkg/sql/information_schema.go @@ -1528,7 +1528,10 @@ var informationSchemaSessionVariables = virtualSchemaTable{ populate: func(ctx context.Context, p *planner, _ catalog.DatabaseDescriptor, addRow func(...tree.Datum) error) error { for _, vName := range varNames { gen := varGen[vName] - value := gen.Get(&p.extendedEvalCtx) + value, err := gen.Get(&p.extendedEvalCtx) + if err != nil { + return err + } if err := addRow( tree.NewDString(vName), tree.NewDString(value), diff --git a/pkg/sql/logictest/testdata/logic_test/set b/pkg/sql/logictest/testdata/logic_test/set index d51c3562a9b7..c5579c33225d 100644 --- a/pkg/sql/logictest/testdata/logic_test/set +++ b/pkg/sql/logictest/testdata/logic_test/set @@ -664,3 +664,6 @@ query T SHOW tracing.custom ---- ijk + +statement error unrecognized configuration parameter "custom_option.does_not_yet_exist" +SHOW custom_option.does_not_yet_exist diff --git a/pkg/sql/pg_catalog.go b/pkg/sql/pg_catalog.go index e3e947ec62d0..8b89dd906802 100644 --- a/pkg/sql/pg_catalog.go +++ b/pkg/sql/pg_catalog.go @@ -2458,7 +2458,10 @@ https://www.postgresql.org/docs/9.5/catalog-pg-settings.html`, if gen.Hidden { continue } - value := gen.Get(&p.extendedEvalCtx) + value, err := gen.Get(&p.extendedEvalCtx) + if err != nil { + return err + } valueDatum := tree.NewDString(value) var bootDatum tree.Datum = tree.DNull var resetDatum tree.Datum = tree.DNull diff --git a/pkg/sql/show_var.go b/pkg/sql/show_var.go index 6790083ea0c7..38cd7f893170 100644 --- a/pkg/sql/show_var.go +++ b/pkg/sql/show_var.go @@ -39,8 +39,8 @@ func (s *showVarNode) Next(params runParams) (bool, error) { if err != nil { return false, err } - s.val = v.Get(params.extendedEvalCtx) - return true, nil + s.val, err = v.Get(params.extendedEvalCtx) + return true, err } func (s *showVarNode) Values() tree.Datums { diff --git a/pkg/sql/unsupported_vars.go b/pkg/sql/unsupported_vars.go index 51879d5e6ba2..881541d69208 100644 --- a/pkg/sql/unsupported_vars.go +++ b/pkg/sql/unsupported_vars.go @@ -19,8 +19,8 @@ import "github.com/cockroachdb/cockroach/pkg/settings" var DummyVars = map[string]sessionVar{ "enable_seqscan": makeDummyBooleanSessionVar( "enable_seqscan", - func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableSeqScan) + func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableSeqScan), nil }, func(m sessionDataMutator, v bool) { m.SetEnableSeqScan(v) @@ -29,8 +29,8 @@ var DummyVars = map[string]sessionVar{ ), "synchronous_commit": makeDummyBooleanSessionVar( "synchronous_commit", - func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().SynchronousCommit) + func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().SynchronousCommit), nil }, func(m sessionDataMutator, v bool) { m.SetSynchronousCommit(v) diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index a41ed76ed1eb..b61c49535e6c 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -68,7 +68,7 @@ type sessionVar struct { // Get returns a string representation of a given variable to be used // either by SHOW or in the pg_catalog table. - Get func(evalCtx *extendedEvalContext) string + Get func(evalCtx *extendedEvalContext) (string, error) // GetFromSessionData returns a string representation of a given variable to // be used by BufferParamStatus. This is only required if the variable @@ -124,7 +124,7 @@ func formatFloatAsPostgresSetting(f float64) string { // They are logged to telemetry and output a notice that these are unused. func makeDummyBooleanSessionVar( name string, - getFunc func(*extendedEvalContext) string, + getFunc func(*extendedEvalContext) (string, error), setFunc func(sessionDataMutator, bool), sv func(_ *settings.Values) string, ) sessionVar { @@ -155,8 +155,8 @@ var varGen = map[string]sessionVar{ m.SetApplicationName(s) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().ApplicationName + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().ApplicationName, nil }, GetFromSessionData: func(sd *sessiondata.SessionData) string { return sd.ApplicationName @@ -177,8 +177,8 @@ var varGen = map[string]sessionVar{ m.SetBytesEncodeFormat(mode) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().DataConversionConfig.BytesEncodeFormat.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().DataConversionConfig.BytesEncodeFormat.String(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondatapb.BytesEncodeHex.String() }, }, @@ -202,8 +202,8 @@ var varGen = map[string]sessionVar{ m.SetNoticeDisplaySeverity(severity) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return pgnotice.DisplaySeverity(evalCtx.SessionData().NoticeDisplaySeverity).String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return pgnotice.DisplaySeverity(evalCtx.SessionData().NoticeDisplaySeverity).String(), nil }, GlobalDefault: func(_ *settings.Values) string { return "notice" }, }, @@ -224,7 +224,7 @@ var varGen = map[string]sessionVar{ "unimplemented client encoding: %q", encoding) } }, - Get: func(evalCtx *extendedEvalContext) string { return "UTF8" }, + Get: func(evalCtx *extendedEvalContext) (string, error) { return "UTF8", nil }, GlobalDefault: func(_ *settings.Values) string { return "UTF8" }, }, @@ -260,7 +260,7 @@ var varGen = map[string]sessionVar{ m.SetDatabase(dbName) return nil }, - Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionData().Database }, + Get: func(evalCtx *extendedEvalContext) (string, error) { return evalCtx.SessionData().Database, nil }, GlobalDefault: func(_ *settings.Values) string { // The "defaultdb" value is set as session default in the pgwire // connection code. The global default is the empty string, @@ -300,8 +300,8 @@ var varGen = map[string]sessionVar{ m.SetDateStyle(ds) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.GetDateStyle().SQLString() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.GetDateStyle().SQLString(), nil }, GetFromSessionData: func(sd *sessiondata.SessionData) string { return sd.GetDateStyle().SQLString() @@ -311,8 +311,8 @@ var varGen = map[string]sessionVar{ }, }, `datestyle_enabled`: { - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().DateStyleEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().DateStyleEnabled), nil }, GetStringVal: makePostgresBoolGetStringValFn("datestyle_enabled"), Set: func(ctx context.Context, m sessionDataMutator, s string) error { @@ -338,8 +338,8 @@ var varGen = map[string]sessionVar{ // Controls the subsequent parsing of a "naked" INT type. // TODO(bob): Remove or no-op this in v2.4: https://github.com/cockroachdb/cockroach/issues/32844 `default_int_size`: { - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(int64(evalCtx.SessionData().DefaultIntSize), 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(int64(evalCtx.SessionData().DefaultIntSize), 10), nil }, GetStringVal: makeIntGetStringValFn("default_int_size"), Set: func(ctx context.Context, m sessionDataMutator, val string) error { @@ -379,8 +379,8 @@ var varGen = map[string]sessionVar{ } return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return "" + Get: func(evalCtx *extendedEvalContext) (string, error) { + return "", nil }, GlobalDefault: func(sv *settings.Values) string { return "" }, }, @@ -397,8 +397,8 @@ var varGen = map[string]sessionVar{ return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return "serializable" + Get: func(evalCtx *extendedEvalContext) (string, error) { + return "serializable", nil }, GlobalDefault: func(sv *settings.Values) string { return "default" }, }, @@ -413,12 +413,12 @@ var varGen = map[string]sessionVar{ m.SetDefaultTransactionPriority(pri) return nil }, - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { pri := tree.UserPriority(evalCtx.SessionData().DefaultTxnPriority) if pri == tree.UnspecifiedUserPriority { pri = tree.Normal } - return strings.ToLower(pri.String()) + return strings.ToLower(pri.String()), nil }, GlobalDefault: func(sv *settings.Values) string { return strings.ToLower(tree.Normal.String()) @@ -436,8 +436,8 @@ var varGen = map[string]sessionVar{ m.SetDefaultTransactionReadOnly(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().DefaultTxnReadOnly) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().DefaultTxnReadOnly), nil }, GlobalDefault: globalFalse, }, @@ -453,8 +453,8 @@ var varGen = map[string]sessionVar{ m.SetDefaultTransactionUseFollowerReads(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().DefaultTxnUseFollowerReads) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().DefaultTxnUseFollowerReads), nil }, GlobalDefault: globalFalse, }, @@ -470,8 +470,8 @@ var varGen = map[string]sessionVar{ m.SetDisablePlanGists(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().DisablePlanGists) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().DisablePlanGists), nil }, GlobalDefault: globalFalse, }, @@ -486,8 +486,8 @@ var varGen = map[string]sessionVar{ m.SetDistSQLMode(mode) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().DistSQLMode.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().DistSQLMode.String(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondatapb.DistSQLExecMode(DistSQLClusterExecMode.Get(sv)).String() @@ -507,8 +507,8 @@ var varGen = map[string]sessionVar{ m.SetDistSQLWorkMem(limit) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return humanizeutil.IBytes(evalCtx.SessionData().WorkMemLimit) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return humanizeutil.IBytes(evalCtx.SessionData().WorkMemLimit), nil }, GlobalDefault: func(sv *settings.Values) string { return humanizeutil.IBytes(settingWorkMemBytes.Get(sv)) @@ -527,8 +527,8 @@ var varGen = map[string]sessionVar{ m.SetExperimentalDistSQLPlanning(mode) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().ExperimentalDistSQLPlanningMode.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().ExperimentalDistSQLPlanningMode.String(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondatapb.ExperimentalDistSQLPlanningMode(experimentalDistSQLPlanningClusterMode.Get(sv)).String() @@ -546,8 +546,8 @@ var varGen = map[string]sessionVar{ m.SetPartiallyDistributedPlansDisabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().PartiallyDistributedPlansDisabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().PartiallyDistributedPlansDisabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(false) @@ -565,8 +565,8 @@ var varGen = map[string]sessionVar{ m.SetZigzagJoinEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().ZigzagJoinEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().ZigzagJoinEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(zigzagJoinClusterMode.Get(sv)) @@ -588,8 +588,8 @@ var varGen = map[string]sessionVar{ m.SetReorderJoinsLimit(int(b)) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().ReorderJoinsLimit, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().ReorderJoinsLimit, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return strconv.FormatInt(ReorderJoinsLimitClusterValue.Get(sv), 10) @@ -607,8 +607,8 @@ var varGen = map[string]sessionVar{ m.SetRequireExplicitPrimaryKeys(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().RequireExplicitPrimaryKeys) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().RequireExplicitPrimaryKeys), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(requireExplicitPrimaryKeysClusterMode.Get(sv)) @@ -626,8 +626,8 @@ var varGen = map[string]sessionVar{ m.SetVectorize(mode) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().VectorizeMode.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().VectorizeMode.String(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondatapb.VectorizeExecMode( @@ -646,8 +646,8 @@ var varGen = map[string]sessionVar{ m.SetTestingVectorizeInjectPanics(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().TestingVectorizeInjectPanics) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().TestingVectorizeInjectPanics), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(false) @@ -663,8 +663,8 @@ var varGen = map[string]sessionVar{ } return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return "on" + Get: func(evalCtx *extendedEvalContext) (string, error) { + return "on", nil }, GlobalDefault: func(sv *settings.Values) string { return "on" @@ -686,8 +686,8 @@ var varGen = map[string]sessionVar{ m.SetOptimizerFKCascadesLimit(int(b)) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().OptimizerFKCascadesLimit, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().OptimizerFKCascadesLimit, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return strconv.FormatInt(optDrivenFKCascadesClusterLimit.Get(sv), 10) @@ -705,8 +705,8 @@ var varGen = map[string]sessionVar{ m.SetOptimizerUseHistograms(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().OptimizerUseHistograms) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().OptimizerUseHistograms), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(optUseHistogramsClusterMode.Get(sv)) @@ -724,8 +724,8 @@ var varGen = map[string]sessionVar{ m.SetOptimizerUseMultiColStats(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().OptimizerUseMultiColStats) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().OptimizerUseMultiColStats), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(optUseMultiColStatsClusterMode.Get(sv)) @@ -743,8 +743,8 @@ var varGen = map[string]sessionVar{ m.SetLocalityOptimizedSearch(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().LocalityOptimizedSearch) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().LocalityOptimizedSearch), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(localityOptimizedSearchMode.Get(sv)) @@ -762,8 +762,8 @@ var varGen = map[string]sessionVar{ m.SetImplicitSelectForUpdate(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().ImplicitSelectForUpdate) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().ImplicitSelectForUpdate), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(implicitSelectForUpdateClusterMode.Get(sv)) @@ -781,8 +781,8 @@ var varGen = map[string]sessionVar{ m.SetInsertFastPath(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().InsertFastPath) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().InsertFastPath), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(insertFastPathClusterMode.Get(sv)) @@ -800,8 +800,8 @@ var varGen = map[string]sessionVar{ m.SetSerialNormalizationMode(mode) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().SerialNormalizationMode.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().SerialNormalizationMode.String(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondatapb.SerialNormalizationMode( @@ -820,8 +820,8 @@ var varGen = map[string]sessionVar{ m.SetStubCatalogTablesEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().StubCatalogTablesEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().StubCatalogTablesEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(stubCatalogTablesEnabledClusterValue.Get(sv)) @@ -848,8 +848,8 @@ var varGen = map[string]sessionVar{ m.SetExtraFloatDigits(int32(i)) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return fmt.Sprintf("%d", evalCtx.SessionData().DataConversionConfig.ExtraFloatDigits) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return fmt.Sprintf("%d", evalCtx.SessionData().DataConversionConfig.ExtraFloatDigits), nil }, GlobalDefault: func(sv *settings.Values) string { return "0" }, }, @@ -857,8 +857,8 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. See docs on SessionData.ForceSavepointRestart. // https://github.com/cockroachdb/cockroach/issues/30588 `force_savepoint_restart`: { - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().ForceSavepointRestart) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().ForceSavepointRestart), nil }, GetStringVal: makePostgresBoolGetStringValFn("force_savepoint_restart"), Set: func(_ context.Context, m sessionDataMutator, val string) error { @@ -911,8 +911,8 @@ var varGen = map[string]sessionVar{ m.SetIntervalStyle(style) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strings.ToLower(evalCtx.SessionData().GetIntervalStyle().String()) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strings.ToLower(evalCtx.SessionData().GetIntervalStyle().String()), nil }, GetFromSessionData: func(sd *sessiondata.SessionData) string { return strings.ToLower(sd.GetIntervalStyle().String()) @@ -922,8 +922,8 @@ var varGen = map[string]sessionVar{ }, }, `intervalstyle_enabled`: { - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().IntervalStyleEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().IntervalStyleEnabled), nil }, GetStringVal: makePostgresBoolGetStringValFn("intervalstyle_enabled"), Set: func(ctx context.Context, m sessionDataMutator, s string) error { @@ -947,8 +947,8 @@ var varGen = map[string]sessionVar{ }, `is_superuser`: { - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().IsSuperuser) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().IsSuperuser), nil }, GetFromSessionData: func(sd *sessiondata.SessionData) string { return formatBoolAsPostgresSetting(sd.IsSuperuser) @@ -970,8 +970,8 @@ var varGen = map[string]sessionVar{ m.SetLargeFullScanRows(f) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatFloatAsPostgresSetting(evalCtx.SessionData().LargeFullScanRows) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatFloatAsPostgresSetting(evalCtx.SessionData().LargeFullScanRows), nil }, GlobalDefault: func(sv *settings.Values) string { return formatFloatAsPostgresSetting(largeFullScanRows.Get(sv)) @@ -980,8 +980,8 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. `locality`: { - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.Locality.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.Locality.String(), nil }, }, @@ -989,9 +989,9 @@ var varGen = map[string]sessionVar{ `lock_timeout`: { GetStringVal: makeTimeoutVarGetter(`lock_timeout`), Set: lockTimeoutVarSet, - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { ms := evalCtx.SessionData().LockTimeout.Nanoseconds() / int64(time.Millisecond) - return strconv.FormatInt(ms, 10) + return strconv.FormatInt(ms, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return clusterLockTimeout.String(sv) @@ -1007,7 +1007,7 @@ var varGen = map[string]sessionVar{ // Supported for PG compatibility only. // See https://www.postgresql.org/docs/10/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS `max_identifier_length`: { - Get: func(evalCtx *extendedEvalContext) string { return "128" }, + Get: func(evalCtx *extendedEvalContext) (string, error) { return "128", nil }, }, // See https://www.postgresql.org/docs/10/static/runtime-config-preset.html#GUC-MAX-INDEX-KEYS @@ -1015,25 +1015,25 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. `node_id`: { - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { nodeID, _ := evalCtx.NodeID.OptionalNodeID() // zero if unavailable - return fmt.Sprintf("%d", nodeID) + return fmt.Sprintf("%d", nodeID), nil }, }, // CockroachDB extension. // TODO(dan): This should also work with SET. `results_buffer_size`: { - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().ResultsBufferSize, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().ResultsBufferSize, 10), nil }, }, // CockroachDB extension (inspired by MySQL). // See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_safe_updates `sql_safe_updates`: { - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().SafeUpdates) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().SafeUpdates), nil }, GetStringVal: makePostgresBoolGetStringValFn("sql_safe_updates"), Set: func(_ context.Context, m sessionDataMutator, s string) error { @@ -1049,8 +1049,8 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. `prefer_lookup_joins_for_fks`: { - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().PreferLookupJoinsForFKs) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().PreferLookupJoinsForFKs), nil }, GetStringVal: makePostgresBoolGetStringValFn("prefer_lookup_joins_for_fks"), Set: func(_ context.Context, m sessionDataMutator, s string) error { @@ -1068,11 +1068,11 @@ var varGen = map[string]sessionVar{ // See https://www.postgresql.org/docs/current/sql-set-role.html. `role`: { - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { if evalCtx.SessionData().SessionUserProto == "" { - return security.NoneRole + return security.NoneRole, nil } - return evalCtx.SessionData().User().Normalized() + return evalCtx.SessionData().User().Normalized(), nil }, // SetWithPlanner is defined in init(), as otherwise there is a circular // initialization loop with the planner. @@ -1114,8 +1114,8 @@ var varGen = map[string]sessionVar{ m.UpdateSearchPath(paths) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().SearchPath.SQLIdentifiers() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().SearchPath.SQLIdentifiers(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondata.DefaultSearchPath.String() @@ -1158,7 +1158,7 @@ var varGen = map[string]sessionVar{ `ssl_renegotiation_limit`: { Hidden: true, GetStringVal: makeIntGetStringValFn(`ssl_renegotiation_limit`), - Get: func(_ *extendedEvalContext) string { return "0" }, + Get: func(_ *extendedEvalContext) (string, error) { return "0", nil }, GlobalDefault: func(_ *settings.Values) string { return "0" }, Set: func(_ context.Context, _ sessionDataMutator, s string) error { i, err := strconv.ParseInt(s, 10, 64) @@ -1180,21 +1180,25 @@ var varGen = map[string]sessionVar{ // CockroachDB extension `session_id`: { - Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionID.String() }, + Get: func(evalCtx *extendedEvalContext) (string, error) { return evalCtx.SessionID.String(), nil }, }, // CockroachDB extension. // In PG this is a pseudo-function used with SELECT, not SHOW. // See https://www.postgresql.org/docs/10/static/functions-info.html `session_user`: { - Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionData().User().Normalized() }, + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().User().Normalized(), nil + }, }, // See pg sources src/backend/utils/misc/guc.c. The variable is defined // but is hidden from SHOW ALL. `session_authorization`: { Hidden: true, - Get: func(evalCtx *extendedEvalContext) string { return evalCtx.SessionData().User().Normalized() }, + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().User().Normalized(), nil + }, }, // Supported for PG compatibility only. @@ -1225,9 +1229,9 @@ var varGen = map[string]sessionVar{ `statement_timeout`: { GetStringVal: makeTimeoutVarGetter(`statement_timeout`), Set: stmtTimeoutVarSet, - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { ms := evalCtx.SessionData().StmtTimeout.Nanoseconds() / int64(time.Millisecond) - return strconv.FormatInt(ms, 10) + return strconv.FormatInt(ms, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return clusterStatementTimeout.String(sv) @@ -1237,9 +1241,9 @@ var varGen = map[string]sessionVar{ `idle_in_session_timeout`: { GetStringVal: makeTimeoutVarGetter(`idle_in_session_timeout`), Set: idleInSessionTimeoutVarSet, - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { ms := evalCtx.SessionData().IdleInSessionTimeout.Nanoseconds() / int64(time.Millisecond) - return strconv.FormatInt(ms, 10) + return strconv.FormatInt(ms, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return clusterIdleInSessionTimeout.String(sv) @@ -1249,9 +1253,9 @@ var varGen = map[string]sessionVar{ `idle_in_transaction_session_timeout`: { GetStringVal: makeTimeoutVarGetter(`idle_in_transaction_session_timeout`), Set: idleInTransactionSessionTimeoutVarSet, - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { ms := evalCtx.SessionData().IdleInTransactionSessionTimeout.Nanoseconds() / int64(time.Millisecond) - return strconv.FormatInt(ms, 10) + return strconv.FormatInt(ms, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return clusterIdleInTransactionSessionTimeout.String(sv) @@ -1260,8 +1264,8 @@ var varGen = map[string]sessionVar{ // See https://www.postgresql.org/docs/10/static/runtime-config-client.html#GUC-TIMEZONE `timezone`: { - Get: func(evalCtx *extendedEvalContext) string { - return sessionDataTimeZoneFormat(evalCtx.SessionData().GetLocation()) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return sessionDataTimeZoneFormat(evalCtx.SessionData().GetLocation()), nil }, GetFromSessionData: func(sd *sessiondata.SessionData) string { return sessionDataTimeZoneFormat(sd.GetLocation()) @@ -1274,8 +1278,8 @@ var varGen = map[string]sessionVar{ // This is not directly documented in PG's docs but does indeed behave this way. // See https://github.com/postgres/postgres/blob/REL_10_STABLE/src/backend/utils/misc/guc.c#L3401-L3409 `transaction_isolation`: { - Get: func(evalCtx *extendedEvalContext) string { - return "serializable" + Get: func(evalCtx *extendedEvalContext) (string, error) { + return "serializable", nil }, RuntimeSet: func(_ context.Context, evalCtx *extendedEvalContext, local bool, s string) error { _, ok := tree.IsolationLevelMap[s] @@ -1289,15 +1293,15 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. `transaction_priority`: { - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.Txn.UserPriority().String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.Txn.UserPriority().String(), nil }, }, // CockroachDB extension. `transaction_status`: { - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.TxnState + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.TxnState, nil }, }, @@ -1312,24 +1316,24 @@ var varGen = map[string]sessionVar{ m.SetReadOnly(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.TxnReadOnly) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.TxnReadOnly), nil }, GlobalDefault: globalFalse, }, // CockroachDB extension. `tracing`: { - Get: func(evalCtx *extendedEvalContext) string { + Get: func(evalCtx *extendedEvalContext) (string, error) { sessTracing := evalCtx.Tracing if sessTracing.Enabled() { val := "on" if sessTracing.KVTracingEnabled() { val += ", kv" } - return val + return val, nil } - return "off" + return "off", nil }, // Setting is done by the SetTracing statement. }, @@ -1337,8 +1341,8 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. `allow_prepare_as_opt_plan`: { Hidden: true, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().AllowPrepareAsOptPlan) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().AllowPrepareAsOptPlan), nil }, Set: func(_ context.Context, m sessionDataMutator, s string) error { b, err := paramparse.ParseBoolVar("allow_prepare_as_opt_plan", s) @@ -1354,8 +1358,8 @@ var varGen = map[string]sessionVar{ // CockroachDB extension. `save_tables_prefix`: { Hidden: true, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().SaveTablesPrefix + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().SaveTablesPrefix, nil }, Set: func(_ context.Context, m sessionDataMutator, s string) error { m.SetSaveTablesPrefix(s) @@ -1375,8 +1379,8 @@ var varGen = map[string]sessionVar{ m.SetTempTablesEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().TempTablesEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().TempTablesEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(temporaryTablesEnabledClusterMode.Get(sv)) @@ -1394,8 +1398,8 @@ var varGen = map[string]sessionVar{ m.SetPlacementEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().PlacementEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().PlacementEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(placementEnabledClusterMode.Get(sv)) @@ -1413,8 +1417,8 @@ var varGen = map[string]sessionVar{ m.SetAutoRehomingEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().AutoRehomingEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().AutoRehomingEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(autoRehomingEnabledClusterMode.Get(sv)) @@ -1432,8 +1436,8 @@ var varGen = map[string]sessionVar{ m.SetOnUpdateRehomeRowEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().OnUpdateRehomeRowEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().OnUpdateRehomeRowEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(onUpdateRehomeRowEnabledClusterMode.Get(sv)) @@ -1451,8 +1455,8 @@ var varGen = map[string]sessionVar{ m.SetImplicitColumnPartitioningEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().ImplicitColumnPartitioningEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().ImplicitColumnPartitioningEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(implicitColumnPartitioningEnabledClusterMode.Get(sv)) @@ -1468,8 +1472,8 @@ var varGen = map[string]sessionVar{ _, err := paramparse.ParseBoolVar("enable_drop_enum_value", s) return err }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(true) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(true), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(true) @@ -1487,8 +1491,8 @@ var varGen = map[string]sessionVar{ m.SetOverrideMultiRegionZoneConfigEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().OverrideMultiRegionZoneConfigEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().OverrideMultiRegionZoneConfigEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(overrideMultiRegionZoneConfigClusterMode.Get(sv)) @@ -1506,8 +1510,8 @@ var varGen = map[string]sessionVar{ m.SetHashShardedIndexesEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().HashShardedIndexesEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().HashShardedIndexesEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(hashShardedIndexesEnabledClusterMode.Get(sv)) @@ -1525,8 +1529,8 @@ var varGen = map[string]sessionVar{ m.SetDisallowFullTableScans(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().DisallowFullTableScans) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().DisallowFullTableScans), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(disallowFullTableScans.Get(sv)) @@ -1544,8 +1548,8 @@ var varGen = map[string]sessionVar{ m.SetAlterColumnTypeGeneral(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().AlterColumnTypeGeneralEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().AlterColumnTypeGeneralEnabled), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(experimentalAlterColumnTypeGeneralMode.Get(sv)) @@ -1564,8 +1568,8 @@ var varGen = map[string]sessionVar{ m.SetUniqueWithoutIndexConstraints(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableUniqueWithoutIndexConstraints) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableUniqueWithoutIndexConstraints), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(experimentalUniqueWithoutIndexConstraintsMode.Get(sv)) @@ -1583,8 +1587,8 @@ var varGen = map[string]sessionVar{ m.SetUseNewSchemaChanger(mode) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().NewSchemaChangerMode.String() + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().NewSchemaChangerMode.String(), nil }, GlobalDefault: func(sv *settings.Values) string { return sessiondatapb.NewSchemaChangerMode(experimentalUseNewSchemaChanger.Get(sv)).String() @@ -1601,8 +1605,8 @@ var varGen = map[string]sessionVar{ m.SetStreamReplicationEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableStreamReplication) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableStreamReplication), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(experimentalStreamReplicationEnabled.Get(sv)) @@ -1622,8 +1626,8 @@ var varGen = map[string]sessionVar{ m.SetExperimentalComputedColumnRewrites(s) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().ExperimentalComputedColumnRewrites + Get: func(evalCtx *extendedEvalContext) (string, error) { + return evalCtx.SessionData().ExperimentalComputedColumnRewrites, nil }, GlobalDefault: func(sv *settings.Values) string { return experimentalComputedColumnRewrites.Get(sv) @@ -1640,8 +1644,8 @@ var varGen = map[string]sessionVar{ m.SetCopyPartitioningWhenDeinterleavingTable(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().CopyPartitioningWhenDeinterleavingTable) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().CopyPartitioningWhenDeinterleavingTable), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(copyPartitioningWhenDeinterleavingTable.Get(sv)) @@ -1658,8 +1662,8 @@ var varGen = map[string]sessionVar{ m.SetNullOrderedLast(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().NullOrderedLast) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().NullOrderedLast), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(false) @@ -1676,8 +1680,8 @@ var varGen = map[string]sessionVar{ m.SetPropagateInputOrdering(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().PropagateInputOrdering) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().PropagateInputOrdering), nil }, GlobalDefault: func(sv *settings.Values) string { return formatBoolAsPostgresSetting(propagateInputOrdering.Get(sv)) @@ -1699,8 +1703,8 @@ var varGen = map[string]sessionVar{ m.SetTxnRowsWrittenLog(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().TxnRowsWrittenLog, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().TxnRowsWrittenLog, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return strconv.FormatInt(txnRowsWrittenLog.Get(sv), 10) @@ -1722,8 +1726,8 @@ var varGen = map[string]sessionVar{ m.SetTxnRowsWrittenErr(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().TxnRowsWrittenErr, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().TxnRowsWrittenErr, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return strconv.FormatInt(txnRowsWrittenErr.Get(sv), 10) @@ -1745,8 +1749,8 @@ var varGen = map[string]sessionVar{ m.SetTxnRowsReadLog(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().TxnRowsReadLog, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().TxnRowsReadLog, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return strconv.FormatInt(txnRowsReadLog.Get(sv), 10) @@ -1768,8 +1772,8 @@ var varGen = map[string]sessionVar{ m.SetTxnRowsReadErr(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return strconv.FormatInt(evalCtx.SessionData().TxnRowsReadErr, 10) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return strconv.FormatInt(evalCtx.SessionData().TxnRowsReadErr, 10), nil }, GlobalDefault: func(sv *settings.Values) string { return strconv.FormatInt(txnRowsReadErr.Get(sv), 10) @@ -1788,8 +1792,8 @@ var varGen = map[string]sessionVar{ m.SetInjectRetryErrorsEnabled(b) return nil }, - Get: func(evalCtx *extendedEvalContext) string { - return formatBoolAsPostgresSetting(evalCtx.SessionData().InjectRetryErrorsEnabled) + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().InjectRetryErrorsEnabled), nil }, GlobalDefault: globalFalse, }, @@ -1866,14 +1870,14 @@ func makePostgresBoolGetStringValFn(varName string) getStringValFn { func makeReadOnlyVar(value string) sessionVar { return sessionVar{ - Get: func(_ *extendedEvalContext) string { return value }, + Get: func(_ *extendedEvalContext) (string, error) { return value, nil }, GlobalDefault: func(_ *settings.Values) string { return value }, } } func makeReadOnlyVarWithFn(fn func() string) sessionVar { return sessionVar{ - Get: func(_ *extendedEvalContext) string { return fn() }, + Get: func(_ *extendedEvalContext) (string, error) { return fn(), nil }, GlobalDefault: func(_ *settings.Values) string { return fn() }, } } @@ -1903,7 +1907,7 @@ func sessionDataTimeZoneFormat(loc *time.Location) string { func makeCompatBoolVar(varName string, displayValue, anyValAllowed bool) sessionVar { displayValStr := formatBoolAsPostgresSetting(displayValue) return sessionVar{ - Get: func(_ *extendedEvalContext) string { return displayValStr }, + Get: func(_ *extendedEvalContext) (string, error) { return displayValStr, nil }, Set: func(_ context.Context, m sessionDataMutator, s string) error { b, err := paramparse.ParseBoolVar(varName, s) if err != nil { @@ -1943,8 +1947,8 @@ var _ = makeCompatIntVar func makeCompatStringVar(varName, displayValue string, extraAllowed ...string) sessionVar { allowedVals := append(extraAllowed, strings.ToLower(displayValue)) return sessionVar{ - Get: func(_ *extendedEvalContext) string { - return displayValue + Get: func(_ *extendedEvalContext) (string, error) { + return displayValue, nil }, Set: func(_ context.Context, m sessionDataMutator, s string) error { enc := strings.ToLower(s) @@ -1997,7 +2001,7 @@ func IsSessionVariableConfigurable(varName string) (exists, configurable bool) { // IsCustomOptionSessionVariable returns whether the given varName is a custom // session variable. func IsCustomOptionSessionVariable(varName string) bool { - isCustom, _ := getCustomOptionSessionVar(varName) + _, isCustom := getCustomOptionSessionVar(varName) return isCustom } @@ -2036,8 +2040,8 @@ func getSessionVar(name string, missingOk bool) (bool, sessionVar, error) { v, ok := varGen[name] if !ok { - if isCustom, v := getCustomOptionSessionVar(name); isCustom { - return true, v, nil + if vCustom, isCustom := getCustomOptionSessionVar(name); isCustom { + return true, vCustom, nil } if missingOk { return false, sessionVar{}, nil @@ -2048,11 +2052,16 @@ func getSessionVar(name string, missingOk bool) (bool, sessionVar, error) { return true, v, nil } -func getCustomOptionSessionVar(varName string) (isCustom bool, sv sessionVar) { +func getCustomOptionSessionVar(varName string) (sv sessionVar, isCustom bool) { if strings.Contains(varName, ".") { - return true, sessionVar{ - Get: func(evalCtx *extendedEvalContext) string { - return evalCtx.SessionData().CustomOptions[varName] + return sessionVar{ + Get: func(evalCtx *extendedEvalContext) (string, error) { + v, ok := evalCtx.SessionData().CustomOptions[varName] + if !ok { + return "", pgerror.Newf(pgcode.UndefinedObject, + "unrecognized configuration parameter %q", varName) + } + return v, nil }, Set: func(ctx context.Context, m sessionDataMutator, val string) error { // TODO(#72026): do some memory accounting. @@ -2062,9 +2071,9 @@ func getCustomOptionSessionVar(varName string) (isCustom bool, sv sessionVar) { GlobalDefault: func(sv *settings.Values) string { return "" }, - } + }, true } - return false, sessionVar{} + return sessionVar{}, false } // GetSessionVar implements the EvalSessionAccessor interface. @@ -2076,7 +2085,8 @@ func (p *planner) GetSessionVar( if err != nil || !ok { return ok, "", err } - return true, v.Get(&p.extendedEvalCtx), nil + val, err := v.Get(&p.extendedEvalCtx) + return true, val, err } // SetSessionVar implements the EvalSessionAccessor interface. From 66ff69fbb78388ff7790cf3335df1b3e84049708 Mon Sep 17 00:00:00 2001 From: Casper Date: Fri, 29 Oct 2021 14:03:53 -0400 Subject: [PATCH 160/205] streamingccl: source cluster producer job in streaming replication Introduce a producer job in source cluster that tracks a stream replication's liveness. The stream replication will be stopped if the job has been inactive for a peried of time. Release note: none --- pkg/ccl/streamingccl/BUILD.bazel | 1 + pkg/ccl/streamingccl/settings.go | 23 + .../streamingccl/streamproducer/BUILD.bazel | 18 +- .../streamproducer/producer_job.go | 101 ++ .../streamproducer/producer_job_test.go | 179 ++ pkg/jobs/jobspb/BUILD.bazel | 1 + pkg/jobs/jobspb/jobs.pb.go | 1511 +++++++++++------ pkg/jobs/jobspb/jobs.proto | 16 +- pkg/jobs/jobspb/wrap.go | 14 +- pkg/sql/sem/tree/eval.go | 2 +- pkg/ts/catalog/chart_catalog.go | 12 + 11 files changed, 1375 insertions(+), 503 deletions(-) create mode 100644 pkg/ccl/streamingccl/settings.go create mode 100644 pkg/ccl/streamingccl/streamproducer/producer_job.go create mode 100644 pkg/ccl/streamingccl/streamproducer/producer_job_test.go diff --git a/pkg/ccl/streamingccl/BUILD.bazel b/pkg/ccl/streamingccl/BUILD.bazel index 298edbfb38f6..d82bfbe9294a 100644 --- a/pkg/ccl/streamingccl/BUILD.bazel +++ b/pkg/ccl/streamingccl/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "addresses.go", "event.go", + "settings.go", ], importpath = "github.com/cockroachdb/cockroach/pkg/ccl/streamingccl", visibility = ["//visibility:public"], diff --git a/pkg/ccl/streamingccl/settings.go b/pkg/ccl/streamingccl/settings.go new file mode 100644 index 000000000000..6ec55726f7b5 --- /dev/null +++ b/pkg/ccl/streamingccl/settings.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package streamingccl + +import "time" + +// DefaultJobLivenessTrackingFrequency is the default frequency to check +// the liveness of a streaming replication producer job. +var DefaultJobLivenessTrackingFrequency = 1 * time.Minute + +// TestingSetDefaultJobLivenessTrackingFrequency changes DefaultJobLivenessTrackingFrequency for tests. +// Returns function to restore the frequency to its original value. +func TestingSetDefaultJobLivenessTrackingFrequency(f time.Duration) func() { + old := DefaultJobLivenessTrackingFrequency + DefaultJobLivenessTrackingFrequency = f + return func() { DefaultJobLivenessTrackingFrequency = old } +} diff --git a/pkg/ccl/streamingccl/streamproducer/BUILD.bazel b/pkg/ccl/streamingccl/streamproducer/BUILD.bazel index 006eb9f66671..5151b3e5f5a5 100644 --- a/pkg/ccl/streamingccl/streamproducer/BUILD.bazel +++ b/pkg/ccl/streamingccl/streamproducer/BUILD.bazel @@ -2,17 +2,24 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "streamproducer", - srcs = ["replication_stream_planning.go"], + srcs = [ + "producer_job.go", + "replication_stream_planning.go", + ], importpath = "github.com/cockroachdb/cockroach/pkg/ccl/streamingccl/streamproducer", visibility = ["//visibility:public"], deps = [ "//pkg/ccl/changefeedccl/changefeedbase", "//pkg/ccl/changefeedccl/changefeeddist", + "//pkg/ccl/streamingccl", "//pkg/ccl/utilccl", + "//pkg/jobs", "//pkg/jobs/jobspb", "//pkg/keys", "//pkg/roachpb:with-mocks", + "//pkg/security", "//pkg/server/telemetry", + "//pkg/settings/cluster", "//pkg/sql", "//pkg/sql/catalog/colinfo", "//pkg/sql/pgwire/pgcode", @@ -20,6 +27,7 @@ go_library( "//pkg/sql/sem/tree", "//pkg/sql/types", "//pkg/util/hlc", + "//pkg/util/timeutil", "@com_github_cockroachdb_errors//:errors", ], ) @@ -28,29 +36,37 @@ go_test( name = "streamproducer_test", srcs = [ "main_test.go", + "producer_job_test.go", "replication_stream_test.go", ], embed = [":streamproducer"], deps = [ + "//pkg/base", "//pkg/ccl/changefeedccl", "//pkg/ccl/kvccl/kvtenantccl", "//pkg/ccl/storageccl", "//pkg/ccl/streamingccl", "//pkg/ccl/streamingccl/streamingtest", "//pkg/ccl/utilccl", + "//pkg/jobs", + "//pkg/jobs/jobspb", + "//pkg/kv", "//pkg/roachpb:with-mocks", "//pkg/security", "//pkg/security/securitytest", "//pkg/server", + "//pkg/settings/cluster", "//pkg/sql/catalog/catalogkv", "//pkg/testutils", "//pkg/testutils/serverutils", + "//pkg/testutils/sqlutils", "//pkg/testutils/testcluster", "//pkg/util/hlc", "//pkg/util/leaktest", "//pkg/util/log", "//pkg/util/protoutil", "//pkg/util/randutil", + "//pkg/util/timeutil", "@com_github_jackc_pgx_v4//:pgx", "@com_github_stretchr_testify//require", ], diff --git a/pkg/ccl/streamingccl/streamproducer/producer_job.go b/pkg/ccl/streamingccl/streamproducer/producer_job.go new file mode 100644 index 000000000000..faeb2acd9b28 --- /dev/null +++ b/pkg/ccl/streamingccl/streamproducer/producer_job.go @@ -0,0 +1,101 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package streamproducer + +import ( + "context" + "fmt" + "time" + + "github.com/cockroachdb/cockroach/pkg/ccl/streamingccl" + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/sql" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" +) + +func makeProducerJobRecord( + registry *jobs.Registry, tenantID uint64, timeout time.Duration, username security.SQLUsername, +) (jobspb.JobID, jobs.Record) { + prefix := keys.MakeTenantPrefix(roachpb.MakeTenantID(tenantID)) + spans := []*roachpb.Span{{Key: prefix, EndKey: prefix.PrefixEnd()}} + jr := jobs.Record{ + Description: fmt.Sprintf("stream replication for tenant %d", tenantID), + Username: username, + Details: jobspb.StreamReplicationDetails{ + Spans: spans, + }, + Progress: jobspb.StreamReplicationProgress{ + Expiration: timeutil.Now().Add(timeout), + }, + } + return registry.MakeJobID(), jr +} + +type producerJobResumer struct { + job *jobs.Job + + timeSource timeutil.TimeSource + timer timeutil.TimerI +} + +// Resume is part of the jobs.Resumer interface. +func (p *producerJobResumer) Resume(ctx context.Context, execCtx interface{}) error { + jobExec := execCtx.(sql.JobExecContext) + execCfg := jobExec.ExecCfg() + isTimedOut := func(job *jobs.Job) bool { + progress := p.job.Progress() + return progress.GetStreamReplication().Expiration.Before(p.timeSource.Now()) + } + if isTimedOut(p.job) { + return errors.Errorf("replication stream %d timed out", p.job.ID()) + } + + p.timer.Reset(streamingccl.DefaultJobLivenessTrackingFrequency) + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-p.timer.Ch(): + p.timer.MarkRead() + p.timer.Reset(streamingccl.DefaultJobLivenessTrackingFrequency) + j, err := execCfg.JobRegistry.LoadJob(ctx, p.job.ID()) + if err != nil { + return err + } + if isTimedOut(j) { + return errors.Errorf("replication stream %d timed out", p.job.ID()) + } + } + } +} + +// OnFailOrCancel implements jobs.Resumer interface +func (p *producerJobResumer) OnFailOrCancel(ctx context.Context, execCtx interface{}) error { + return nil +} + +func init() { + jobs.RegisterConstructor( + jobspb.TypeStreamReplication, + func(job *jobs.Job, _ *cluster.Settings) jobs.Resumer { + ts := timeutil.DefaultTimeSource{} + return &producerJobResumer{ + job: job, + timeSource: ts, + timer: ts.NewTimer(), + } + }, + ) +} diff --git a/pkg/ccl/streamingccl/streamproducer/producer_job_test.go b/pkg/ccl/streamingccl/streamproducer/producer_job_test.go new file mode 100644 index 000000000000..480c0534c980 --- /dev/null +++ b/pkg/ccl/streamingccl/streamproducer/producer_job_test.go @@ -0,0 +1,179 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package streamproducer + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/ccl/streamingccl" + "github.com/cockroachdb/cockroach/pkg/jobs" + "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/stretchr/testify/require" +) + +type coordinatedTimer struct { + timer timeutil.TimerI + in <-chan struct{} + out chan<- struct{} +} + +// Reset resets the timer and waits for a new time to be +// assigned to the underlying manual time source. +func (s *coordinatedTimer) Reset(duration time.Duration) { + s.timer.Reset(duration) + s.out <- struct{}{} +} + +// Stop behaves the same as the normal timer. +func (s *coordinatedTimer) Stop() bool { + return s.timer.Stop() +} + +// Ch returns next timer event after a new time is assigned to +// the underlying manual time source. +func (s *coordinatedTimer) Ch() <-chan time.Time { + <-s.in + return s.timer.Ch() +} + +// MarkRead behaves the same as the normal timer. +func (s *coordinatedTimer) MarkRead() { + s.timer.MarkRead() +} + +func makeCoordinatedTimer( + i timeutil.TimerI, in <-chan struct{}, out chan<- struct{}, +) timeutil.TimerI { + return &coordinatedTimer{ + timer: i, + in: in, + out: out, + } +} + +type coordinatedResumer struct { + resumer jobs.Resumer + revertingConfirmed chan<- struct{} +} + +// Resume behaves the same as the normal resumer. +func (c coordinatedResumer) Resume(ctx context.Context, execCtx interface{}) error { + return c.resumer.Resume(ctx, execCtx) +} + +// OnFailOrCancel is called after the job reaches 'reverting' status +// and notifies watcher after it finishes. +func (c coordinatedResumer) OnFailOrCancel(ctx context.Context, execCtx interface{}) error { + err := c.resumer.OnFailOrCancel(ctx, execCtx) + c.revertingConfirmed <- struct{}{} + return err +} + +func TestStreamReplicationProducerJob(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + ctx := context.Background() + clusterArgs := base.TestClusterArgs{ + ServerArgs: base.TestServerArgs{ + Knobs: base.TestingKnobs{ + JobsTestingKnobs: jobs.NewTestingKnobsWithShortIntervals(), + }, + }, + } + tc := testcluster.StartTestCluster(t, 1, clusterArgs) + defer tc.Stopper().Stop(ctx) + + source := tc.Server(0) + sql := sqlutils.MakeSQLRunner(tc.ServerConn(0)) + registry := source.JobRegistry().(*jobs.Registry) + + registerConstructor := func(initialTime time.Time) (*timeutil.ManualTime, func(), func(), func()) { + mt := timeutil.NewManualTime(initialTime) + waitUntilReverting := make(chan struct{}) + in, out := make(chan struct{}, 1), make(chan struct{}, 1) + jobs.RegisterConstructor(jobspb.TypeStreamReplication, func(job *jobs.Job, _ *cluster.Settings) jobs.Resumer { + r := &producerJobResumer{ + job: job, + timeSource: mt, + timer: makeCoordinatedTimer(mt.NewTimer(), in, out), + } + return coordinatedResumer{ + resumer: r, + revertingConfirmed: waitUntilReverting, + } + }) + return mt, + func() { + in <- struct{}{} // Signals the timer that a new time is assigned to time source + }, + func() { + <-out // Waits until caller starts waiting for a new timer event + }, + func() { + <-waitUntilReverting // Waits until job reaches 'reverting' status + } + } + + startJob := func(jobID jobspb.JobID, jr jobs.Record) error { + return source.DB().Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { + _, err := registry.CreateAdoptableJobWithTxn(ctx, jr, jobID, txn) + return err + }) + } + + timeout, username := 1*time.Second, security.MakeSQLUsernameFromPreNormalizedString("user") + jobsQuery := func(jobID jobspb.JobID) string { + return fmt.Sprintf("SELECT status FROM system.jobs WHERE id = %d", jobID) + } + expirationTime := func(record jobs.Record) time.Time { + return record.Progress.(jobspb.StreamReplicationProgress).Expiration + } + t.Run("producer-job", func(t *testing.T) { + jobID, jr := makeProducerJobRecord(registry, 10, timeout, username) + + // Case 1: Resumer wakes up and finds the job timed out. + _, _, _, waitUntilReverting := registerConstructor(expirationTime(jr).Add(1 * time.Millisecond)) + require.NoError(t, startJob(jobID, jr)) + + waitUntilReverting() + sql.SucceedsSoonDuration = 1 * time.Second + sql.CheckQueryResultsRetry(t, jobsQuery(jobID), [][]string{{"failed"}}) + + // Shorten the tracking frequency to make timer easy to be triggerred. + reset := streamingccl.TestingSetDefaultJobLivenessTrackingFrequency(1 * time.Millisecond) + defer reset() + + // Case 2: Resumer wakes up and find the job still active. + jobID, jr = makeProducerJobRecord(registry, 20, timeout, username) + mt, timeGiven, waitForTimeRequest, waitUntilReverting := registerConstructor(expirationTime(jr).Add(-5 * time.Millisecond)) + require.NoError(t, startJob(jobID, jr)) + waitForTimeRequest() + sql.CheckQueryResults(t, jobsQuery(jobID), [][]string{{"running"}}) + + // Reset the time to be after the timeout + mt.AdvanceTo(expirationTime(jr).Add(2 * time.Millisecond)) + timeGiven() + waitUntilReverting() + status := sql.QueryStr(t, jobsQuery(jobID))[0][0] + require.True(t, status == "reverting" || status == "failed") + }) +} diff --git a/pkg/jobs/jobspb/BUILD.bazel b/pkg/jobs/jobspb/BUILD.bazel index 4adbc95f37f3..e3efba9db95a 100644 --- a/pkg/jobs/jobspb/BUILD.bazel +++ b/pkg/jobs/jobspb/BUILD.bazel @@ -35,6 +35,7 @@ proto_library( "@com_github_cockroachdb_errors//errorspb:errorspb_proto", "@com_github_gogo_protobuf//gogoproto:gogo_proto", "@com_google_protobuf//:any_proto", + "@com_google_protobuf//:timestamp_proto", ], ) diff --git a/pkg/jobs/jobspb/jobs.pb.go b/pkg/jobs/jobspb/jobs.pb.go index 6d684c7c8c7c..cb7caa47ac35 100644 --- a/pkg/jobs/jobspb/jobs.pb.go +++ b/pkg/jobs/jobspb/jobs.pb.go @@ -22,15 +22,19 @@ import ( _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + _ "github.com/gogo/protobuf/types" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" io "io" math "math" math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -114,6 +118,7 @@ const ( TypeMigration Type = 12 TypeAutoSpanConfigReconciliation Type = 13 TypeAutoSQLStatsCompaction Type = 14 + TypeStreamReplication Type = 15 ) var Type_name = map[int32]string{ @@ -132,6 +137,7 @@ var Type_name = map[int32]string{ 12: "MIGRATION", 13: "AUTO_SPAN_CONFIG_RECONCILIATION", 14: "AUTO_SQL_STATS_COMPACTION", + 15: "STREAM_REPLICATION", } var Type_value = map[string]int32{ @@ -150,6 +156,7 @@ var Type_value = map[string]int32{ "MIGRATION": 12, "AUTO_SPAN_CONFIG_RECONCILIATION": 13, "AUTO_SQL_STATS_COMPACTION": 14, + "STREAM_REPLICATION": 15, } func (Type) EnumDescriptor() ([]byte, []int) { @@ -200,7 +207,7 @@ func (x SchedulePTSChainingRecord_PTSAction) String() string { } func (SchedulePTSChainingRecord_PTSAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{4, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{6, 0} } type SchemaChangeGCProgress_Status int32 @@ -232,7 +239,7 @@ func (x SchemaChangeGCProgress_Status) String() string { } func (SchemaChangeGCProgress_Status) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{24, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{26, 0} } type ResolvedSpan_BoundaryType int32 @@ -272,7 +279,7 @@ func (x ResolvedSpan_BoundaryType) String() string { } func (ResolvedSpan_BoundaryType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{27, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{29, 0} } // BackupEncryptionOptions stores information resolved during the BACKUP/RESTORE @@ -514,6 +521,74 @@ func (m *StreamIngestionProgress_PartitionProgress) XXX_DiscardUnknown() { var xxx_messageInfo_StreamIngestionProgress_PartitionProgress proto.InternalMessageInfo +type StreamReplicationDetails struct { + // Key spans we are replicating + Spans []*roachpb.Span `protobuf:"bytes,1,rep,name=spans,proto3" json:"spans,omitempty"` +} + +func (m *StreamReplicationDetails) Reset() { *m = StreamReplicationDetails{} } +func (m *StreamReplicationDetails) String() string { return proto.CompactTextString(m) } +func (*StreamReplicationDetails) ProtoMessage() {} +func (*StreamReplicationDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_6c315f3a2536c4ef, []int{4} +} +func (m *StreamReplicationDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StreamReplicationDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *StreamReplicationDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_StreamReplicationDetails.Merge(m, src) +} +func (m *StreamReplicationDetails) XXX_Size() int { + return m.Size() +} +func (m *StreamReplicationDetails) XXX_DiscardUnknown() { + xxx_messageInfo_StreamReplicationDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_StreamReplicationDetails proto.InternalMessageInfo + +type StreamReplicationProgress struct { + // Expiration timestamp of consumer heartbeat + Expiration time.Time `protobuf:"bytes,1,opt,name=expiration,proto3,stdtime" json:"expiration"` +} + +func (m *StreamReplicationProgress) Reset() { *m = StreamReplicationProgress{} } +func (m *StreamReplicationProgress) String() string { return proto.CompactTextString(m) } +func (*StreamReplicationProgress) ProtoMessage() {} +func (*StreamReplicationProgress) Descriptor() ([]byte, []int) { + return fileDescriptor_6c315f3a2536c4ef, []int{5} +} +func (m *StreamReplicationProgress) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StreamReplicationProgress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *StreamReplicationProgress) XXX_Merge(src proto.Message) { + xxx_messageInfo_StreamReplicationProgress.Merge(m, src) +} +func (m *StreamReplicationProgress) XXX_Size() int { + return m.Size() +} +func (m *StreamReplicationProgress) XXX_DiscardUnknown() { + xxx_messageInfo_StreamReplicationProgress.DiscardUnknown(m) +} + +var xxx_messageInfo_StreamReplicationProgress proto.InternalMessageInfo + type SchedulePTSChainingRecord struct { ProtectedTimestampRecord *github_com_cockroachdb_cockroach_pkg_util_uuid.UUID `protobuf:"bytes,1,opt,name=protected_timestamp_record,json=protectedTimestampRecord,proto3,customtype=github.com/cockroachdb/cockroach/pkg/util/uuid.UUID" json:"protected_timestamp_record,omitempty"` Action SchedulePTSChainingRecord_PTSAction `protobuf:"varint,2,opt,name=action,proto3,enum=cockroach.sql.jobs.jobspb.SchedulePTSChainingRecord_PTSAction" json:"action,omitempty"` @@ -523,7 +598,7 @@ func (m *SchedulePTSChainingRecord) Reset() { *m = SchedulePTSChainingRe func (m *SchedulePTSChainingRecord) String() string { return proto.CompactTextString(m) } func (*SchedulePTSChainingRecord) ProtoMessage() {} func (*SchedulePTSChainingRecord) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{4} + return fileDescriptor_6c315f3a2536c4ef, []int{6} } func (m *SchedulePTSChainingRecord) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -582,7 +657,7 @@ func (m *BackupDetails) Reset() { *m = BackupDetails{} } func (m *BackupDetails) String() string { return proto.CompactTextString(m) } func (*BackupDetails) ProtoMessage() {} func (*BackupDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{5} + return fileDescriptor_6c315f3a2536c4ef, []int{7} } func (m *BackupDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -614,7 +689,7 @@ func (m *BackupProgress) Reset() { *m = BackupProgress{} } func (m *BackupProgress) String() string { return proto.CompactTextString(m) } func (*BackupProgress) ProtoMessage() {} func (*BackupProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{6} + return fileDescriptor_6c315f3a2536c4ef, []int{8} } func (m *BackupProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -689,7 +764,7 @@ func (m *RestoreDetails) Reset() { *m = RestoreDetails{} } func (m *RestoreDetails) String() string { return proto.CompactTextString(m) } func (*RestoreDetails) ProtoMessage() {} func (*RestoreDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{7} + return fileDescriptor_6c315f3a2536c4ef, []int{9} } func (m *RestoreDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -728,7 +803,7 @@ func (m *RestoreDetails_DescriptorRewrite) Reset() { *m = RestoreDetails func (m *RestoreDetails_DescriptorRewrite) String() string { return proto.CompactTextString(m) } func (*RestoreDetails_DescriptorRewrite) ProtoMessage() {} func (*RestoreDetails_DescriptorRewrite) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{7, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{9, 0} } func (m *RestoreDetails_DescriptorRewrite) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -761,7 +836,7 @@ func (m *RestoreDetails_BackupLocalityInfo) Reset() { *m = RestoreDetail func (m *RestoreDetails_BackupLocalityInfo) String() string { return proto.CompactTextString(m) } func (*RestoreDetails_BackupLocalityInfo) ProtoMessage() {} func (*RestoreDetails_BackupLocalityInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{7, 1} + return fileDescriptor_6c315f3a2536c4ef, []int{9, 1} } func (m *RestoreDetails_BackupLocalityInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -795,7 +870,7 @@ func (m *RestoreDetails_RevalidateIndex) Reset() { *m = RestoreDetails_R func (m *RestoreDetails_RevalidateIndex) String() string { return proto.CompactTextString(m) } func (*RestoreDetails_RevalidateIndex) ProtoMessage() {} func (*RestoreDetails_RevalidateIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{7, 4} + return fileDescriptor_6c315f3a2536c4ef, []int{9, 4} } func (m *RestoreDetails_RevalidateIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -833,7 +908,7 @@ func (m *RestoreDetails_DatabaseModifier) Reset() { *m = RestoreDetails_ func (m *RestoreDetails_DatabaseModifier) String() string { return proto.CompactTextString(m) } func (*RestoreDetails_DatabaseModifier) ProtoMessage() {} func (*RestoreDetails_DatabaseModifier) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{7, 5} + return fileDescriptor_6c315f3a2536c4ef, []int{9, 5} } func (m *RestoreDetails_DatabaseModifier) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -866,7 +941,7 @@ func (m *RestoreProgress) Reset() { *m = RestoreProgress{} } func (m *RestoreProgress) String() string { return proto.CompactTextString(m) } func (*RestoreProgress) ProtoMessage() {} func (*RestoreProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{8} + return fileDescriptor_6c315f3a2536c4ef, []int{10} } func (m *RestoreProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -937,7 +1012,7 @@ func (m *ImportDetails) Reset() { *m = ImportDetails{} } func (m *ImportDetails) String() string { return proto.CompactTextString(m) } func (*ImportDetails) ProtoMessage() {} func (*ImportDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{9} + return fileDescriptor_6c315f3a2536c4ef, []int{11} } func (m *ImportDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -975,7 +1050,7 @@ func (m *ImportDetails_Table) Reset() { *m = ImportDetails_Table{} } func (m *ImportDetails_Table) String() string { return proto.CompactTextString(m) } func (*ImportDetails_Table) ProtoMessage() {} func (*ImportDetails_Table) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{9, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{11, 0} } func (m *ImportDetails_Table) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1008,7 +1083,7 @@ func (m *ImportDetails_Schema) Reset() { *m = ImportDetails_Schema{} } func (m *ImportDetails_Schema) String() string { return proto.CompactTextString(m) } func (*ImportDetails_Schema) ProtoMessage() {} func (*ImportDetails_Schema) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{9, 1} + return fileDescriptor_6c315f3a2536c4ef, []int{11, 1} } func (m *ImportDetails_Schema) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1041,7 +1116,7 @@ func (m *ImportDetails_Type) Reset() { *m = ImportDetails_Type{} } func (m *ImportDetails_Type) String() string { return proto.CompactTextString(m) } func (*ImportDetails_Type) ProtoMessage() {} func (*ImportDetails_Type) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{9, 2} + return fileDescriptor_6c315f3a2536c4ef, []int{11, 2} } func (m *ImportDetails_Type) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1083,7 +1158,7 @@ func (m *SequenceValChunk) Reset() { *m = SequenceValChunk{} } func (m *SequenceValChunk) String() string { return proto.CompactTextString(m) } func (*SequenceValChunk) ProtoMessage() {} func (*SequenceValChunk) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{10} + return fileDescriptor_6c315f3a2536c4ef, []int{12} } func (m *SequenceValChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1119,7 +1194,7 @@ func (m *SequenceDetails) Reset() { *m = SequenceDetails{} } func (m *SequenceDetails) String() string { return proto.CompactTextString(m) } func (*SequenceDetails) ProtoMessage() {} func (*SequenceDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{11} + return fileDescriptor_6c315f3a2536c4ef, []int{13} } func (m *SequenceDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1154,7 +1229,7 @@ func (m *SequenceDetails_SequenceChunks) Reset() { *m = SequenceDetails_ func (m *SequenceDetails_SequenceChunks) String() string { return proto.CompactTextString(m) } func (*SequenceDetails_SequenceChunks) ProtoMessage() {} func (*SequenceDetails_SequenceChunks) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{11, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{13, 0} } func (m *SequenceDetails_SequenceChunks) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1201,7 +1276,7 @@ func (m *ImportProgress) Reset() { *m = ImportProgress{} } func (m *ImportProgress) String() string { return proto.CompactTextString(m) } func (*ImportProgress) ProtoMessage() {} func (*ImportProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{12} + return fileDescriptor_6c315f3a2536c4ef, []int{14} } func (m *ImportProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1244,7 +1319,7 @@ func (m *TypeSchemaChangeDetails) Reset() { *m = TypeSchemaChangeDetails func (m *TypeSchemaChangeDetails) String() string { return proto.CompactTextString(m) } func (*TypeSchemaChangeDetails) ProtoMessage() {} func (*TypeSchemaChangeDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{13} + return fileDescriptor_6c315f3a2536c4ef, []int{15} } func (m *TypeSchemaChangeDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1277,7 +1352,7 @@ func (m *TypeSchemaChangeProgress) Reset() { *m = TypeSchemaChangeProgre func (m *TypeSchemaChangeProgress) String() string { return proto.CompactTextString(m) } func (*TypeSchemaChangeProgress) ProtoMessage() {} func (*TypeSchemaChangeProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{14} + return fileDescriptor_6c315f3a2536c4ef, []int{16} } func (m *TypeSchemaChangeProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1311,7 +1386,7 @@ func (m *NewSchemaChangeDetails) Reset() { *m = NewSchemaChangeDetails{} func (m *NewSchemaChangeDetails) String() string { return proto.CompactTextString(m) } func (*NewSchemaChangeDetails) ProtoMessage() {} func (*NewSchemaChangeDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{15} + return fileDescriptor_6c315f3a2536c4ef, []int{17} } func (m *NewSchemaChangeDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1345,7 +1420,7 @@ func (m *NewSchemaChangeProgress) Reset() { *m = NewSchemaChangeProgress func (m *NewSchemaChangeProgress) String() string { return proto.CompactTextString(m) } func (*NewSchemaChangeProgress) ProtoMessage() {} func (*NewSchemaChangeProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{16} + return fileDescriptor_6c315f3a2536c4ef, []int{18} } func (m *NewSchemaChangeProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1379,7 +1454,7 @@ func (m *AutoSpanConfigReconciliationDetails) Reset() { *m = AutoSpanCon func (m *AutoSpanConfigReconciliationDetails) String() string { return proto.CompactTextString(m) } func (*AutoSpanConfigReconciliationDetails) ProtoMessage() {} func (*AutoSpanConfigReconciliationDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{17} + return fileDescriptor_6c315f3a2536c4ef, []int{19} } func (m *AutoSpanConfigReconciliationDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1413,7 +1488,7 @@ func (m *AutoSpanConfigReconciliationProgress) Reset() { *m = AutoSpanCo func (m *AutoSpanConfigReconciliationProgress) String() string { return proto.CompactTextString(m) } func (*AutoSpanConfigReconciliationProgress) ProtoMessage() {} func (*AutoSpanConfigReconciliationProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{18} + return fileDescriptor_6c315f3a2536c4ef, []int{20} } func (m *AutoSpanConfigReconciliationProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1446,7 +1521,7 @@ func (m *ResumeSpanList) Reset() { *m = ResumeSpanList{} } func (m *ResumeSpanList) String() string { return proto.CompactTextString(m) } func (*ResumeSpanList) ProtoMessage() {} func (*ResumeSpanList) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{19} + return fileDescriptor_6c315f3a2536c4ef, []int{21} } func (m *ResumeSpanList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1481,7 +1556,7 @@ func (m *DroppedTableDetails) Reset() { *m = DroppedTableDetails{} } func (m *DroppedTableDetails) String() string { return proto.CompactTextString(m) } func (*DroppedTableDetails) ProtoMessage() {} func (*DroppedTableDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{20} + return fileDescriptor_6c315f3a2536c4ef, []int{22} } func (m *DroppedTableDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1544,7 +1619,7 @@ func (m *SchemaChangeGCDetails) Reset() { *m = SchemaChangeGCDetails{} } func (m *SchemaChangeGCDetails) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCDetails) ProtoMessage() {} func (*SchemaChangeGCDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{21} + return fileDescriptor_6c315f3a2536c4ef, []int{23} } func (m *SchemaChangeGCDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1578,7 +1653,7 @@ func (m *SchemaChangeGCDetails_DroppedIndex) Reset() { *m = SchemaChange func (m *SchemaChangeGCDetails_DroppedIndex) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCDetails_DroppedIndex) ProtoMessage() {} func (*SchemaChangeGCDetails_DroppedIndex) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{21, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{23, 0} } func (m *SchemaChangeGCDetails_DroppedIndex) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1612,7 +1687,7 @@ func (m *SchemaChangeGCDetails_DroppedID) Reset() { *m = SchemaChangeGCD func (m *SchemaChangeGCDetails_DroppedID) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCDetails_DroppedID) ProtoMessage() {} func (*SchemaChangeGCDetails_DroppedID) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{21, 1} + return fileDescriptor_6c315f3a2536c4ef, []int{23, 1} } func (m *SchemaChangeGCDetails_DroppedID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1646,7 +1721,7 @@ func (m *SchemaChangeGCDetails_DroppedTenant) Reset() { *m = SchemaChang func (m *SchemaChangeGCDetails_DroppedTenant) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCDetails_DroppedTenant) ProtoMessage() {} func (*SchemaChangeGCDetails_DroppedTenant) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{21, 2} + return fileDescriptor_6c315f3a2536c4ef, []int{23, 2} } func (m *SchemaChangeGCDetails_DroppedTenant) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1713,7 +1788,7 @@ func (m *SchemaChangeDetails) Reset() { *m = SchemaChangeDetails{} } func (m *SchemaChangeDetails) String() string { return proto.CompactTextString(m) } func (*SchemaChangeDetails) ProtoMessage() {} func (*SchemaChangeDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{22} + return fileDescriptor_6c315f3a2536c4ef, []int{24} } func (m *SchemaChangeDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1745,7 +1820,7 @@ func (m *SchemaChangeProgress) Reset() { *m = SchemaChangeProgress{} } func (m *SchemaChangeProgress) String() string { return proto.CompactTextString(m) } func (*SchemaChangeProgress) ProtoMessage() {} func (*SchemaChangeProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{23} + return fileDescriptor_6c315f3a2536c4ef, []int{25} } func (m *SchemaChangeProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1783,7 +1858,7 @@ func (m *SchemaChangeGCProgress) Reset() { *m = SchemaChangeGCProgress{} func (m *SchemaChangeGCProgress) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCProgress) ProtoMessage() {} func (*SchemaChangeGCProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{24} + return fileDescriptor_6c315f3a2536c4ef, []int{26} } func (m *SchemaChangeGCProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1817,7 +1892,7 @@ func (m *SchemaChangeGCProgress_IndexProgress) Reset() { *m = SchemaChan func (m *SchemaChangeGCProgress_IndexProgress) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCProgress_IndexProgress) ProtoMessage() {} func (*SchemaChangeGCProgress_IndexProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{24, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{26, 0} } func (m *SchemaChangeGCProgress_IndexProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1851,7 +1926,7 @@ func (m *SchemaChangeGCProgress_TableProgress) Reset() { *m = SchemaChan func (m *SchemaChangeGCProgress_TableProgress) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCProgress_TableProgress) ProtoMessage() {} func (*SchemaChangeGCProgress_TableProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{24, 1} + return fileDescriptor_6c315f3a2536c4ef, []int{26, 1} } func (m *SchemaChangeGCProgress_TableProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1884,7 +1959,7 @@ func (m *SchemaChangeGCProgress_TenantProgress) Reset() { *m = SchemaCha func (m *SchemaChangeGCProgress_TenantProgress) String() string { return proto.CompactTextString(m) } func (*SchemaChangeGCProgress_TenantProgress) ProtoMessage() {} func (*SchemaChangeGCProgress_TenantProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{24, 2} + return fileDescriptor_6c315f3a2536c4ef, []int{26, 2} } func (m *SchemaChangeGCProgress_TenantProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1917,7 +1992,7 @@ func (m *ChangefeedTarget) Reset() { *m = ChangefeedTarget{} } func (m *ChangefeedTarget) String() string { return proto.CompactTextString(m) } func (*ChangefeedTarget) ProtoMessage() {} func (*ChangefeedTarget) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{25} + return fileDescriptor_6c315f3a2536c4ef, []int{27} } func (m *ChangefeedTarget) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1970,7 +2045,7 @@ func (m *ChangefeedDetails) Reset() { *m = ChangefeedDetails{} } func (m *ChangefeedDetails) String() string { return proto.CompactTextString(m) } func (*ChangefeedDetails) ProtoMessage() {} func (*ChangefeedDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{26} + return fileDescriptor_6c315f3a2536c4ef, []int{28} } func (m *ChangefeedDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2005,7 +2080,7 @@ func (m *ResolvedSpan) Reset() { *m = ResolvedSpan{} } func (m *ResolvedSpan) String() string { return proto.CompactTextString(m) } func (*ResolvedSpan) ProtoMessage() {} func (*ResolvedSpan) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{27} + return fileDescriptor_6c315f3a2536c4ef, []int{29} } func (m *ResolvedSpan) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2038,7 +2113,7 @@ func (m *ResolvedSpans) Reset() { *m = ResolvedSpans{} } func (m *ResolvedSpans) String() string { return proto.CompactTextString(m) } func (*ResolvedSpans) ProtoMessage() {} func (*ResolvedSpans) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{28} + return fileDescriptor_6c315f3a2536c4ef, []int{30} } func (m *ResolvedSpans) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2081,7 +2156,7 @@ func (m *ChangefeedProgress) Reset() { *m = ChangefeedProgress{} } func (m *ChangefeedProgress) String() string { return proto.CompactTextString(m) } func (*ChangefeedProgress) ProtoMessage() {} func (*ChangefeedProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{29} + return fileDescriptor_6c315f3a2536c4ef, []int{31} } func (m *ChangefeedProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2119,7 +2194,7 @@ func (m *ChangefeedProgress_Checkpoint) Reset() { *m = ChangefeedProgres func (m *ChangefeedProgress_Checkpoint) String() string { return proto.CompactTextString(m) } func (*ChangefeedProgress_Checkpoint) ProtoMessage() {} func (*ChangefeedProgress_Checkpoint) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{29, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{31, 0} } func (m *ChangefeedProgress_Checkpoint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2163,7 +2238,7 @@ func (m *CreateStatsDetails) Reset() { *m = CreateStatsDetails{} } func (m *CreateStatsDetails) String() string { return proto.CompactTextString(m) } func (*CreateStatsDetails) ProtoMessage() {} func (*CreateStatsDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{30} + return fileDescriptor_6c315f3a2536c4ef, []int{32} } func (m *CreateStatsDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2204,7 +2279,7 @@ func (m *CreateStatsDetails_ColStat) Reset() { *m = CreateStatsDetails_C func (m *CreateStatsDetails_ColStat) String() string { return proto.CompactTextString(m) } func (*CreateStatsDetails_ColStat) ProtoMessage() {} func (*CreateStatsDetails_ColStat) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{30, 0} + return fileDescriptor_6c315f3a2536c4ef, []int{32, 0} } func (m *CreateStatsDetails_ColStat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2236,7 +2311,7 @@ func (m *CreateStatsProgress) Reset() { *m = CreateStatsProgress{} } func (m *CreateStatsProgress) String() string { return proto.CompactTextString(m) } func (*CreateStatsProgress) ProtoMessage() {} func (*CreateStatsProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{31} + return fileDescriptor_6c315f3a2536c4ef, []int{33} } func (m *CreateStatsProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2269,7 +2344,7 @@ func (m *MigrationDetails) Reset() { *m = MigrationDetails{} } func (m *MigrationDetails) String() string { return proto.CompactTextString(m) } func (*MigrationDetails) ProtoMessage() {} func (*MigrationDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{32} + return fileDescriptor_6c315f3a2536c4ef, []int{34} } func (m *MigrationDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2302,7 +2377,7 @@ func (m *MigrationProgress) Reset() { *m = MigrationProgress{} } func (m *MigrationProgress) String() string { return proto.CompactTextString(m) } func (*MigrationProgress) ProtoMessage() {} func (*MigrationProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{33} + return fileDescriptor_6c315f3a2536c4ef, []int{35} } func (m *MigrationProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2334,7 +2409,7 @@ func (m *AutoSQLStatsCompactionDetails) Reset() { *m = AutoSQLStatsCompa func (m *AutoSQLStatsCompactionDetails) String() string { return proto.CompactTextString(m) } func (*AutoSQLStatsCompactionDetails) ProtoMessage() {} func (*AutoSQLStatsCompactionDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{34} + return fileDescriptor_6c315f3a2536c4ef, []int{36} } func (m *AutoSQLStatsCompactionDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2366,7 +2441,7 @@ func (m *AutoSQLStatsCompactionProgress) Reset() { *m = AutoSQLStatsComp func (m *AutoSQLStatsCompactionProgress) String() string { return proto.CompactTextString(m) } func (*AutoSQLStatsCompactionProgress) ProtoMessage() {} func (*AutoSQLStatsCompactionProgress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{35} + return fileDescriptor_6c315f3a2536c4ef, []int{37} } func (m *AutoSQLStatsCompactionProgress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2430,6 +2505,7 @@ type Payload struct { // *Payload_Migration // *Payload_AutoSpanConfigReconciliation // *Payload_AutoSQLStatsCompaction + // *Payload_StreamReplication Details isPayload_Details `protobuf_oneof:"details"` // PauseReason is used to describe the reason that the job is currently paused // or has been requested to be paused. @@ -2445,7 +2521,7 @@ func (m *Payload) Reset() { *m = Payload{} } func (m *Payload) String() string { return proto.CompactTextString(m) } func (*Payload) ProtoMessage() {} func (*Payload) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{36} + return fileDescriptor_6c315f3a2536c4ef, []int{38} } func (m *Payload) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2515,6 +2591,9 @@ type Payload_AutoSpanConfigReconciliation struct { type Payload_AutoSQLStatsCompaction struct { AutoSQLStatsCompaction *AutoSQLStatsCompactionDetails `protobuf:"bytes,30,opt,name=autoSQLStatsCompaction,proto3,oneof" json:"autoSQLStatsCompaction,omitempty"` } +type Payload_StreamReplication struct { + StreamReplication *StreamReplicationDetails `protobuf:"bytes,33,opt,name=streamReplication,proto3,oneof" json:"streamReplication,omitempty"` +} func (*Payload_Backup) isPayload_Details() {} func (*Payload_Restore) isPayload_Details() {} @@ -2529,6 +2608,7 @@ func (*Payload_NewSchemaChange) isPayload_Details() {} func (*Payload_Migration) isPayload_Details() {} func (*Payload_AutoSpanConfigReconciliation) isPayload_Details() {} func (*Payload_AutoSQLStatsCompaction) isPayload_Details() {} +func (*Payload_StreamReplication) isPayload_Details() {} func (m *Payload) GetDetails() isPayload_Details { if m != nil { @@ -2628,6 +2708,13 @@ func (m *Payload) GetAutoSQLStatsCompaction() *AutoSQLStatsCompactionDetails { return nil } +func (m *Payload) GetStreamReplication() *StreamReplicationDetails { + if x, ok := m.GetDetails().(*Payload_StreamReplication); ok { + return x.StreamReplication + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*Payload) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -2644,6 +2731,7 @@ func (*Payload) XXX_OneofWrappers() []interface{} { (*Payload_Migration)(nil), (*Payload_AutoSpanConfigReconciliation)(nil), (*Payload_AutoSQLStatsCompaction)(nil), + (*Payload_StreamReplication)(nil), } } @@ -2668,6 +2756,7 @@ type Progress struct { // *Progress_Migration // *Progress_AutoSpanConfigReconciliation // *Progress_AutoSQLStatsCompaction + // *Progress_StreamReplication Details isProgress_Details `protobuf_oneof:"details"` TraceID github_com_cockroachdb_cockroach_pkg_util_tracing_tracingpb.TraceID `protobuf:"varint,21,opt,name=trace_id,json=traceId,proto3,customtype=github.com/cockroachdb/cockroach/pkg/util/tracing/tracingpb.TraceID" json:"trace_id"` } @@ -2676,7 +2765,7 @@ func (m *Progress) Reset() { *m = Progress{} } func (m *Progress) String() string { return proto.CompactTextString(m) } func (*Progress) ProtoMessage() {} func (*Progress) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{37} + return fileDescriptor_6c315f3a2536c4ef, []int{39} } func (m *Progress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2757,6 +2846,9 @@ type Progress_AutoSpanConfigReconciliation struct { type Progress_AutoSQLStatsCompaction struct { AutoSQLStatsCompaction *AutoSQLStatsCompactionProgress `protobuf:"bytes,23,opt,name=autoSQLStatsCompaction,proto3,oneof" json:"autoSQLStatsCompaction,omitempty"` } +type Progress_StreamReplication struct { + StreamReplication *StreamReplicationProgress `protobuf:"bytes,24,opt,name=streamReplication,proto3,oneof" json:"streamReplication,omitempty"` +} func (*Progress_FractionCompleted) isProgress_Progress() {} func (*Progress_HighWater) isProgress_Progress() {} @@ -2773,6 +2865,7 @@ func (*Progress_NewSchemaChange) isProgress_Details() {} func (*Progress_Migration) isProgress_Details() {} func (*Progress_AutoSpanConfigReconciliation) isProgress_Details() {} func (*Progress_AutoSQLStatsCompaction) isProgress_Details() {} +func (*Progress_StreamReplication) isProgress_Details() {} func (m *Progress) GetProgress() isProgress_Progress { if m != nil { @@ -2892,6 +2985,13 @@ func (m *Progress) GetAutoSQLStatsCompaction() *AutoSQLStatsCompactionProgress { return nil } +func (m *Progress) GetStreamReplication() *StreamReplicationProgress { + if x, ok := m.GetDetails().(*Progress_StreamReplication); ok { + return x.StreamReplication + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*Progress) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -2910,6 +3010,7 @@ func (*Progress) XXX_OneofWrappers() []interface{} { (*Progress_Migration)(nil), (*Progress_AutoSpanConfigReconciliation)(nil), (*Progress_AutoSQLStatsCompaction)(nil), + (*Progress_StreamReplication)(nil), } } @@ -2925,7 +3026,7 @@ func (m *Job) Reset() { *m = Job{} } func (m *Job) String() string { return proto.CompactTextString(m) } func (*Job) ProtoMessage() {} func (*Job) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{38} + return fileDescriptor_6c315f3a2536c4ef, []int{40} } func (m *Job) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2974,7 +3075,7 @@ func (m *RetriableExecutionFailure) Reset() { *m = RetriableExecutionFai func (m *RetriableExecutionFailure) String() string { return proto.CompactTextString(m) } func (*RetriableExecutionFailure) ProtoMessage() {} func (*RetriableExecutionFailure) Descriptor() ([]byte, []int) { - return fileDescriptor_6c315f3a2536c4ef, []int{39} + return fileDescriptor_6c315f3a2536c4ef, []int{41} } func (m *RetriableExecutionFailure) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3015,6 +3116,8 @@ func init() { proto.RegisterType((*StreamIngestionProgress)(nil), "cockroach.sql.jobs.jobspb.StreamIngestionProgress") proto.RegisterMapType((map[string]StreamIngestionProgress_PartitionProgress)(nil), "cockroach.sql.jobs.jobspb.StreamIngestionProgress.PartitionProgressEntry") proto.RegisterType((*StreamIngestionProgress_PartitionProgress)(nil), "cockroach.sql.jobs.jobspb.StreamIngestionProgress.PartitionProgress") + proto.RegisterType((*StreamReplicationDetails)(nil), "cockroach.sql.jobs.jobspb.StreamReplicationDetails") + proto.RegisterType((*StreamReplicationProgress)(nil), "cockroach.sql.jobs.jobspb.StreamReplicationProgress") proto.RegisterType((*SchedulePTSChainingRecord)(nil), "cockroach.sql.jobs.jobspb.SchedulePTSChainingRecord") proto.RegisterType((*BackupDetails)(nil), "cockroach.sql.jobs.jobspb.BackupDetails") proto.RegisterMapType((map[string]string)(nil), "cockroach.sql.jobs.jobspb.BackupDetails.UrisByLocalityKvEntry") @@ -3080,381 +3183,390 @@ func init() { func init() { proto.RegisterFile("jobs/jobspb/jobs.proto", fileDescriptor_6c315f3a2536c4ef) } var fileDescriptor_6c315f3a2536c4ef = []byte{ - // 5983 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5c, 0x4b, 0x6c, 0x23, 0xc9, - 0x79, 0x56, 0x53, 0x14, 0x1f, 0x3f, 0x1f, 0x6a, 0x96, 0x34, 0x12, 0x87, 0x9e, 0x15, 0x65, 0xee, - 0xcc, 0xee, 0xcc, 0xda, 0x4b, 0xad, 0x67, 0xed, 0xf5, 0xee, 0xd8, 0x3b, 0xbb, 0x7c, 0x49, 0xa2, - 0xde, 0xd3, 0x94, 0x66, 0x5f, 0x59, 0x77, 0x9a, 0xec, 0x12, 0xd5, 0x11, 0xd9, 0xcd, 0xe9, 0x6a, - 0xce, 0x8c, 0x6c, 0xc0, 0x30, 0xec, 0x18, 0x08, 0x06, 0x39, 0x24, 0x40, 0x92, 0x4b, 0x32, 0x49, - 0x10, 0xdb, 0x40, 0x0e, 0x31, 0x82, 0x18, 0x41, 0x92, 0x63, 0x8e, 0x3e, 0x24, 0x80, 0x91, 0x20, - 0x88, 0x93, 0x83, 0x92, 0xc8, 0x87, 0xf8, 0x90, 0x43, 0x90, 0xe3, 0x9c, 0x82, 0x7a, 0x74, 0xb3, - 0x49, 0x51, 0x14, 0xa5, 0x19, 0xdb, 0x17, 0x0d, 0xfb, 0xaf, 0xaa, 0xaf, 0xaa, 0xfe, 0xfe, 0xeb, - 0x7f, 0xd5, 0xdf, 0x03, 0x73, 0xbf, 0x61, 0xd5, 0xc9, 0x12, 0xfd, 0xd3, 0xa9, 0xb3, 0x7f, 0xf2, - 0x1d, 0xdb, 0x72, 0x2c, 0x74, 0xb5, 0x61, 0x35, 0x0e, 0x6d, 0x4b, 0x6b, 0x1c, 0xe4, 0xc9, 0x83, - 0x56, 0x9e, 0xb5, 0xf0, 0x5e, 0x99, 0x2b, 0xd8, 0xb6, 0x2d, 0x9b, 0xf6, 0xe7, 0x3f, 0xf8, 0x88, - 0xcc, 0x6c, 0xd3, 0x6a, 0x5a, 0xec, 0xe7, 0x12, 0xfd, 0x25, 0xa8, 0x29, 0x86, 0xd1, 0xa9, 0x2f, - 0x69, 0x1d, 0x43, 0x90, 0x90, 0x4b, 0xd2, 0x35, 0x47, 0x13, 0xb4, 0xb4, 0x4b, 0x33, 0xac, 0xd7, - 0xf7, 0x2d, 0xbb, 0xad, 0x39, 0x2e, 0xec, 0xcb, 0xe4, 0x41, 0x6b, 0xa9, 0xa1, 0x39, 0x5a, 0xcb, - 0x6a, 0x2e, 0xe9, 0x98, 0x34, 0x3a, 0xf5, 0x25, 0xe2, 0xd8, 0xdd, 0x86, 0xd3, 0xb5, 0xb1, 0x2e, - 0x3a, 0x65, 0x87, 0x74, 0x72, 0xb0, 0xa9, 0x99, 0x8e, 0x8b, 0xdf, 0x75, 0x8c, 0xd6, 0xd2, 0x41, - 0xab, 0xb1, 0xe4, 0x18, 0x6d, 0x4c, 0x1c, 0xad, 0xdd, 0x11, 0x2d, 0x9f, 0xa5, 0x43, 0x49, 0xe3, - 0x00, 0xb7, 0xb5, 0xc6, 0x81, 0x66, 0x36, 0xb1, 0xbd, 0xc4, 0xe7, 0x68, 0x74, 0xea, 0xa2, 0xcb, - 0xf5, 0x46, 0xab, 0x4b, 0x1c, 0x6c, 0x3f, 0xc4, 0x36, 0x31, 0x2c, 0x73, 0x49, 0x3c, 0xaa, 0xe2, - 0x99, 0xf7, 0xca, 0xfd, 0x20, 0x00, 0xf3, 0x45, 0xad, 0x71, 0xd8, 0xed, 0x54, 0xcc, 0x86, 0x7d, - 0xd4, 0x71, 0x0c, 0xcb, 0xdc, 0x66, 0x7f, 0x09, 0x92, 0x61, 0xf2, 0x10, 0x1f, 0xa5, 0xa5, 0x45, - 0xe9, 0x66, 0x5c, 0xa1, 0x3f, 0xd1, 0xbb, 0x10, 0x6c, 0x5b, 0x3a, 0x4e, 0x07, 0x16, 0xa5, 0x9b, - 0xc9, 0xdb, 0xb7, 0xf2, 0x67, 0xb2, 0x3b, 0xdf, 0x43, 0xdb, 0xb4, 0x74, 0xac, 0xb0, 0x61, 0xa8, - 0x0e, 0x91, 0xc3, 0x36, 0x51, 0x0d, 0x73, 0xdf, 0x4a, 0x4f, 0x2e, 0x4a, 0x37, 0x63, 0xb7, 0xef, - 0x8c, 0x80, 0x38, 0x63, 0x59, 0xf9, 0xf5, 0xcd, 0x5a, 0xd5, 0xdc, 0xb7, 0x8a, 0xb1, 0x93, 0xe3, - 0x6c, 0x58, 0x3c, 0x28, 0xe1, 0xc3, 0x36, 0xa1, 0x3f, 0x32, 0xdb, 0xe0, 0xd2, 0xe8, 0xfa, 0xbb, - 0xb6, 0xc1, 0xd6, 0x1f, 0x55, 0xe8, 0x4f, 0xf4, 0x79, 0x40, 0x98, 0xe3, 0x61, 0x5d, 0xa5, 0x2f, - 0x52, 0xa5, 0x1b, 0x0c, 0xb0, 0x0d, 0xca, 0x5e, 0x4b, 0x59, 0x73, 0xb4, 0x75, 0x7c, 0x74, 0x27, - 0xf8, 0xf3, 0x3f, 0xcd, 0x4a, 0xfc, 0x6f, 0xee, 0x5b, 0x93, 0x90, 0xec, 0x2d, 0x85, 0xc1, 0xaf, - 0x42, 0x88, 0xbd, 0x01, 0xcc, 0x66, 0x48, 0xde, 0x7e, 0x63, 0x2c, 0x76, 0xd0, 0xa1, 0xf9, 0x1a, - 0x1b, 0xa7, 0x88, 0xf1, 0x08, 0x41, 0x90, 0x68, 0x2d, 0x47, 0x2c, 0x84, 0xfd, 0x46, 0x7f, 0x28, - 0xc1, 0xe2, 0xe0, 0x8a, 0x8a, 0x47, 0xeb, 0x9b, 0xb5, 0x4d, 0x8d, 0xbe, 0xc6, 0x75, 0x7c, 0x54, - 0x2d, 0xa7, 0x27, 0x17, 0x27, 0x6f, 0xc6, 0x6e, 0x6f, 0x8f, 0x3f, 0x71, 0xe5, 0x1c, 0xc4, 0x8a, - 0xe9, 0xd8, 0x47, 0xca, 0xb9, 0x13, 0x67, 0x6a, 0x70, 0x63, 0x2c, 0x28, 0xbf, 0x0c, 0x45, 0xb9, - 0x0c, 0xcd, 0xc2, 0xd4, 0x43, 0xad, 0xd5, 0xc5, 0x62, 0xb7, 0xfc, 0xe1, 0x4e, 0xe0, 0x6d, 0x29, - 0x37, 0x0f, 0x21, 0xce, 0x18, 0x94, 0x80, 0x68, 0xa1, 0x52, 0xbb, 0xfd, 0xa5, 0xb7, 0x56, 0x4a, - 0x9b, 0xf2, 0x84, 0x78, 0x05, 0xbf, 0x1d, 0x80, 0xb9, 0x9a, 0x63, 0x63, 0xad, 0x5d, 0x35, 0x9b, - 0x98, 0xd0, 0x3d, 0x95, 0xb1, 0xa3, 0x19, 0x2d, 0x82, 0x6e, 0x40, 0x92, 0xb0, 0x16, 0x55, 0xd3, - 0x75, 0x1b, 0x13, 0x22, 0x26, 0x4c, 0x70, 0x6a, 0x81, 0x13, 0xd1, 0x2d, 0x88, 0x8a, 0x6e, 0x86, - 0x9e, 0x0e, 0x2e, 0x4a, 0x37, 0x83, 0xc5, 0xf8, 0xc9, 0x71, 0x36, 0x22, 0x50, 0xcb, 0x4a, 0x84, - 0x37, 0x57, 0x75, 0xf4, 0x05, 0x08, 0x92, 0x8e, 0x66, 0xb2, 0x45, 0xc6, 0x6e, 0xcf, 0xfb, 0x38, - 0x2c, 0xce, 0x7c, 0xbe, 0xd6, 0xd1, 0xcc, 0x62, 0xf0, 0xc7, 0xc7, 0xd9, 0x09, 0x85, 0x75, 0x45, - 0x45, 0x00, 0xe2, 0x68, 0xb6, 0xa3, 0xd2, 0xc3, 0x2a, 0xe4, 0xfb, 0x25, 0xdf, 0x40, 0x7a, 0x98, - 0xf3, 0x07, 0xad, 0x46, 0x7e, 0xd7, 0x3d, 0xcc, 0x62, 0x78, 0x94, 0x0d, 0xa3, 0x54, 0xba, 0x42, - 0xae, 0x01, 0xe8, 0x0a, 0xa7, 0x7a, 0x2b, 0xdc, 0x65, 0x44, 0xba, 0x42, 0xde, 0x5c, 0xd5, 0x73, - 0xff, 0x3a, 0x09, 0xf3, 0x03, 0xec, 0xd8, 0xb1, 0xad, 0x26, 0xdb, 0xe8, 0x32, 0xc4, 0x1b, 0x5d, - 0xc7, 0x7a, 0x88, 0x6d, 0xbe, 0x18, 0x69, 0xfc, 0xc5, 0xc4, 0xc4, 0x40, 0xb6, 0x9c, 0x6f, 0x02, - 0xea, 0x68, 0xb6, 0x63, 0x50, 0x70, 0xb5, 0x23, 0xd0, 0xd3, 0x01, 0x26, 0x75, 0xd5, 0x11, 0x52, - 0x77, 0xc6, 0xba, 0xf2, 0x3b, 0x2e, 0x98, 0x4b, 0x61, 0x42, 0x22, 0x66, 0x4e, 0x75, 0x06, 0x5b, - 0x33, 0x4d, 0x48, 0x9d, 0x1a, 0x82, 0x14, 0x40, 0x06, 0x43, 0xc6, 0xba, 0xea, 0xe9, 0xc5, 0x8b, - 0x6c, 0x31, 0xe5, 0x0e, 0xf7, 0x1a, 0x32, 0x4f, 0x24, 0x98, 0x1b, 0xbe, 0xb8, 0x21, 0x12, 0xfc, - 0xb1, 0x5f, 0x82, 0x63, 0xb7, 0xcb, 0x2f, 0x82, 0x11, 0xfe, 0x73, 0xf0, 0xc3, 0x00, 0x5c, 0xa5, - 0x07, 0x41, 0xef, 0xb6, 0xf0, 0xce, 0x6e, 0xad, 0x74, 0xa0, 0x19, 0xa6, 0x61, 0x36, 0x15, 0xdc, - 0xb0, 0x6c, 0x1d, 0xfd, 0xae, 0x04, 0x19, 0xaa, 0xbb, 0x71, 0xa3, 0x8f, 0x01, 0xaa, 0xcd, 0x9a, - 0xb9, 0xb6, 0x2e, 0xd6, 0xfe, 0xfd, 0x38, 0xfb, 0x66, 0xd3, 0x70, 0x0e, 0xba, 0xf5, 0x7c, 0xc3, - 0x6a, 0x2f, 0x79, 0x2b, 0xd4, 0xeb, 0xbd, 0xdf, 0x4b, 0x9d, 0xc3, 0xe6, 0x12, 0x33, 0x31, 0xdd, - 0xae, 0xa1, 0xe7, 0xf7, 0xf6, 0xaa, 0xe5, 0x93, 0xe3, 0x6c, 0x7a, 0xc7, 0x05, 0xf7, 0xd8, 0xc3, - 0x67, 0x56, 0xd2, 0x9d, 0x33, 0x5a, 0xd0, 0x7d, 0x08, 0x69, 0x0d, 0xba, 0x1d, 0x61, 0x19, 0xee, - 0x8e, 0x62, 0xc9, 0x59, 0x3b, 0xcb, 0xef, 0xec, 0xd6, 0x0a, 0x0c, 0x45, 0x11, 0x68, 0xb9, 0xeb, - 0x10, 0xf5, 0x88, 0x08, 0x20, 0xb4, 0xb7, 0x53, 0x2e, 0xec, 0x56, 0xe4, 0x09, 0x14, 0x83, 0xb0, - 0x52, 0xd9, 0xa8, 0x14, 0x6a, 0x15, 0x59, 0xca, 0xfd, 0x73, 0x18, 0x12, 0xdc, 0x58, 0xb8, 0xfa, - 0xa0, 0xff, 0x28, 0x4a, 0x97, 0x3a, 0x8a, 0x77, 0x21, 0x82, 0x4d, 0xce, 0x60, 0xf1, 0xa2, 0xc7, - 0x42, 0x08, 0x63, 0x93, 0xb1, 0x07, 0x5d, 0xe5, 0xd6, 0x87, 0xea, 0x81, 0x68, 0x31, 0x7c, 0x72, - 0x9c, 0x9d, 0xdc, 0x53, 0xaa, 0xdc, 0x0c, 0x7d, 0x47, 0x82, 0x99, 0xae, 0x6d, 0x10, 0xb5, 0x7e, - 0xa4, 0xb6, 0xac, 0x86, 0xd6, 0x32, 0x9c, 0x23, 0xf5, 0xf0, 0x61, 0x7a, 0x8a, 0x1d, 0xac, 0xbb, - 0xe7, 0xda, 0x44, 0xb1, 0xcd, 0xfc, 0x9e, 0x6d, 0x90, 0xe2, 0xd1, 0x86, 0x40, 0x58, 0x7f, 0xc8, - 0x4f, 0xd3, 0xec, 0xc9, 0x71, 0x56, 0xde, 0x53, 0xaa, 0xfe, 0xa6, 0xfb, 0x8a, 0xdc, 0x1d, 0xe8, - 0x8c, 0xbe, 0x0a, 0x19, 0x1d, 0x77, 0x6c, 0xdc, 0xd0, 0xa8, 0x20, 0xd5, 0x19, 0xb2, 0xda, 0xd6, - 0x4c, 0x63, 0x1f, 0x13, 0x87, 0xa9, 0xc7, 0xb8, 0x92, 0xee, 0xf5, 0xe0, 0x53, 0x6f, 0x8a, 0x76, - 0xa4, 0x79, 0xa6, 0x94, 0xea, 0x06, 0x8b, 0xdb, 0xe6, 0x74, 0x88, 0x31, 0xea, 0xf6, 0xc5, 0xad, - 0xba, 0x92, 0xc2, 0xa7, 0xfc, 0x0f, 0x05, 0xa6, 0x7d, 0x53, 0x30, 0xaf, 0x21, 0xca, 0xf0, 0x6f, - 0x8d, 0x6d, 0xf0, 0x94, 0x24, 0xee, 0x37, 0xda, 0xe7, 0x9c, 0x9e, 0xf0, 0xaf, 0xe2, 0xf4, 0xbc, - 0x0d, 0xc9, 0x86, 0xd5, 0x6a, 0x61, 0x26, 0xe6, 0xea, 0x9e, 0x52, 0x4d, 0x47, 0x98, 0xd0, 0xa4, - 0x4e, 0x8e, 0xb3, 0x89, 0x92, 0xd7, 0x42, 0xc5, 0x27, 0xd1, 0xf0, 0x3f, 0xa2, 0xdf, 0x93, 0xe0, - 0x1a, 0x11, 0xe7, 0x49, 0xed, 0x38, 0x44, 0x6d, 0x88, 0x13, 0xe5, 0xee, 0x07, 0x18, 0xbf, 0xbe, - 0x78, 0x99, 0xe3, 0x58, 0x7c, 0xe9, 0xe4, 0x38, 0x7b, 0xb6, 0x1e, 0x52, 0xae, 0xba, 0x13, 0xef, - 0x38, 0xa4, 0xbf, 0x29, 0x53, 0x82, 0x2b, 0x43, 0x45, 0xf3, 0x3c, 0x6f, 0x20, 0xea, 0xd7, 0x82, - 0x32, 0x24, 0xb9, 0xac, 0xb8, 0x2a, 0x32, 0xf7, 0xc7, 0xf3, 0x90, 0x54, 0x30, 0x71, 0x2c, 0x1b, - 0xbb, 0x07, 0xdd, 0x7f, 0x48, 0x83, 0x97, 0x38, 0xa4, 0x3f, 0x92, 0x60, 0x86, 0x7a, 0xde, 0xb6, - 0xd1, 0x71, 0x2c, 0x5b, 0xb5, 0xf1, 0x23, 0xdb, 0x70, 0xb0, 0x6b, 0xe2, 0x0a, 0x23, 0xf8, 0xd6, - 0xbf, 0x90, 0x7c, 0xd9, 0x03, 0x51, 0x04, 0x06, 0x3f, 0x8c, 0x77, 0xbf, 0xfd, 0x1f, 0xd9, 0x3b, - 0x63, 0x89, 0xd2, 0xe9, 0x60, 0x20, 0x5f, 0x2d, 0x2b, 0x48, 0x3f, 0x05, 0x8c, 0xae, 0x41, 0x90, - 0x1e, 0x66, 0xe6, 0xfd, 0x45, 0x8b, 0x91, 0x93, 0xe3, 0x6c, 0x90, 0x1e, 0x77, 0x85, 0x51, 0x91, - 0x03, 0xb3, 0xe2, 0x2c, 0x7b, 0xaa, 0x85, 0x1d, 0x9d, 0x30, 0xdb, 0xd2, 0x57, 0xc7, 0xdf, 0x12, - 0xe7, 0xbe, 0xfb, 0x0a, 0x99, 0xcb, 0xcd, 0xb9, 0x87, 0xea, 0xa7, 0x5a, 0xd0, 0x0e, 0x24, 0xa9, - 0x3f, 0x5d, 0xd7, 0x08, 0x56, 0xe9, 0x92, 0x49, 0x5a, 0x66, 0xf3, 0x0d, 0x1e, 0x55, 0xf2, 0xa0, - 0x45, 0xfb, 0xe4, 0xcb, 0xa2, 0xb3, 0x8f, 0x6f, 0x09, 0xdd, 0x47, 0x23, 0x68, 0x05, 0x62, 0x8e, - 0x56, 0x6f, 0xb9, 0x70, 0x5c, 0x37, 0xbe, 0x72, 0x06, 0xdc, 0x2e, 0xed, 0xe9, 0xc3, 0x02, 0xc7, - 0x25, 0x10, 0x54, 0x06, 0x70, 0x8e, 0x3a, 0x2e, 0x4e, 0x92, 0xe1, 0xdc, 0x38, 0x0b, 0xe7, 0xa8, - 0xe3, 0x87, 0x89, 0x3a, 0xe2, 0x99, 0xa0, 0x35, 0x88, 0xf3, 0x78, 0x4b, 0xe0, 0x4c, 0x33, 0x9c, - 0x57, 0xcf, 0xc0, 0x61, 0x7e, 0xac, 0xe6, 0x43, 0x8a, 0x11, 0x8f, 0x42, 0xb1, 0xc2, 0xdc, 0x8d, - 0x23, 0xe9, 0x2b, 0x0c, 0xe6, 0xb5, 0xb3, 0x96, 0xc3, 0x9d, 0x3d, 0x73, 0xdf, 0xfa, 0xc0, 0x70, - 0x0e, 0xf6, 0x88, 0xd6, 0xc4, 0xae, 0x04, 0x0b, 0x00, 0xb4, 0x04, 0x31, 0xea, 0xae, 0xd9, 0x86, - 0x8e, 0x55, 0xbd, 0xce, 0x14, 0x70, 0xb4, 0x98, 0x3c, 0x39, 0xce, 0xc2, 0xb6, 0x20, 0x97, 0x8b, - 0x0a, 0xb8, 0x5d, 0xca, 0x75, 0xf4, 0x39, 0x48, 0x75, 0x6c, 0xdc, 0xd1, 0x6c, 0xac, 0x36, 0xac, - 0x76, 0xa7, 0x85, 0x1d, 0xac, 0x33, 0x85, 0x13, 0x51, 0x64, 0xd1, 0x50, 0x72, 0xe9, 0xdc, 0xb1, - 0xd6, 0x1c, 0x1a, 0xb3, 0x11, 0x6c, 0xd3, 0x9e, 0x51, 0xd6, 0x33, 0xc1, 0xa8, 0x55, 0x41, 0x44, - 0x47, 0x30, 0x47, 0x8e, 0x88, 0x83, 0xdb, 0x2a, 0xe3, 0x3b, 0x51, 0xdb, 0x46, 0xd3, 0xa6, 0x46, - 0x23, 0x9d, 0x62, 0xfb, 0x2b, 0x8d, 0x2f, 0x75, 0x35, 0x86, 0xc3, 0xde, 0x27, 0xd9, 0x14, 0x28, - 0x3c, 0x2a, 0x99, 0x25, 0x43, 0x9a, 0xd0, 0x9b, 0x70, 0xa5, 0x77, 0x44, 0x88, 0xda, 0xe9, 0xd6, - 0x5b, 0x06, 0x39, 0xc0, 0x5c, 0xf5, 0x45, 0x94, 0x59, 0x5f, 0xe3, 0x8e, 0xdb, 0x86, 0x8e, 0xfa, - 0x4e, 0x7d, 0x83, 0x72, 0x47, 0x6b, 0xe2, 0x74, 0x6c, 0x51, 0xba, 0x39, 0x55, 0x5c, 0x7d, 0x76, - 0x9c, 0x2d, 0x8f, 0x7d, 0x64, 0x09, 0x6e, 0x2f, 0x39, 0x36, 0xc6, 0x3e, 0x0d, 0x50, 0x12, 0x78, - 0xfe, 0xc3, 0xeb, 0xd2, 0x90, 0x02, 0xd0, 0x33, 0x49, 0xe9, 0xf8, 0xa5, 0xed, 0xa5, 0x0f, 0x05, - 0x99, 0x80, 0x6c, 0xfc, 0x50, 0x6b, 0x19, 0xba, 0xe6, 0x60, 0xd5, 0x30, 0x75, 0xfc, 0x18, 0x93, - 0x34, 0x62, 0xac, 0x7f, 0x67, 0x7c, 0xd6, 0x2b, 0x1e, 0x46, 0x95, 0x42, 0xb8, 0xde, 0xb2, 0xdd, - 0x4f, 0xc6, 0x04, 0xfd, 0xa5, 0x04, 0xc8, 0x3b, 0xed, 0x6d, 0x4b, 0x37, 0xf6, 0x0d, 0x6c, 0x93, - 0xf4, 0x0c, 0x9b, 0xf0, 0xfd, 0x0b, 0x28, 0x4d, 0x81, 0xb1, 0xe9, 0x42, 0xbc, 0x18, 0x9d, 0x99, - 0xd2, 0x07, 0x71, 0xd1, 0x75, 0x48, 0xea, 0xb8, 0xde, 0x6d, 0xaa, 0x1d, 0xad, 0x4b, 0xb0, 0x6a, - 0x99, 0xe9, 0x59, 0x66, 0x6f, 0xe2, 0x8c, 0xba, 0x43, 0x89, 0xdb, 0x66, 0xe6, 0xcf, 0x02, 0x90, - 0x3a, 0xa5, 0xc8, 0xd1, 0x2e, 0x04, 0x0c, 0xee, 0x57, 0x27, 0x8a, 0xd4, 0xc4, 0x07, 0xaa, 0xe5, - 0x67, 0xc7, 0xcf, 0xb5, 0xc0, 0x80, 0xa1, 0xa3, 0x26, 0x44, 0xe9, 0x51, 0xe3, 0x91, 0x5e, 0x80, - 0x81, 0xaf, 0xd1, 0x48, 0x6f, 0x87, 0x11, 0x9f, 0x7b, 0x8a, 0x08, 0x07, 0xaf, 0xea, 0x28, 0x0b, - 0x31, 0xc7, 0x52, 0xf1, 0x63, 0x83, 0x38, 0x86, 0xd9, 0x64, 0xfe, 0x68, 0x44, 0x01, 0xc7, 0xaa, - 0x08, 0x0a, 0x7a, 0x1d, 0x62, 0x26, 0x7e, 0xa4, 0xea, 0x75, 0xd5, 0xd4, 0x84, 0x19, 0x8d, 0x16, - 0x13, 0x27, 0xc7, 0xd9, 0xe8, 0x16, 0x7e, 0x54, 0x2e, 0x6e, 0x69, 0x6d, 0xac, 0x44, 0x4d, 0xfc, - 0xa8, 0x5c, 0xa7, 0x3f, 0x33, 0x7f, 0x14, 0x00, 0x74, 0xda, 0x34, 0xa0, 0xbf, 0x93, 0xe0, 0x9a, - 0xeb, 0xd3, 0x5a, 0xb6, 0xd1, 0x34, 0x4c, 0xad, 0xd5, 0xe7, 0xdc, 0x4a, 0x4c, 0x3a, 0x3e, 0x7e, - 0x1e, 0xfb, 0x23, 0x1c, 0xde, 0x6d, 0x01, 0x3f, 0xe8, 0xf8, 0x5e, 0xa3, 0xfe, 0x17, 0x77, 0x7c, - 0x4f, 0x75, 0xb9, 0xaf, 0xa4, 0xbb, 0x67, 0x0c, 0xce, 0xac, 0xc3, 0x4b, 0x23, 0x81, 0x2f, 0xe2, - 0xb6, 0x64, 0xbe, 0x2d, 0xc1, 0xfc, 0x19, 0xce, 0x80, 0x1f, 0x27, 0xc1, 0x71, 0xee, 0xf5, 0x87, - 0x92, 0x5f, 0x79, 0x0e, 0x87, 0xc3, 0xbf, 0x88, 0x15, 0xb8, 0x7a, 0xa6, 0x1e, 0x3d, 0x6f, 0x37, - 0x11, 0x3f, 0xd0, 0xbf, 0x49, 0x30, 0x3d, 0xa0, 0x16, 0xd0, 0x47, 0xbe, 0xf3, 0x50, 0x3d, 0x39, - 0xce, 0x86, 0xd9, 0x24, 0x2f, 0xe4, 0x50, 0x1c, 0x9e, 0x3e, 0x14, 0x5b, 0x74, 0x06, 0x36, 0x31, - 0x9b, 0xe1, 0xbd, 0x4b, 0xcf, 0xc0, 0x21, 0x7a, 0x07, 0x23, 0xf3, 0xf7, 0x12, 0xc8, 0x83, 0x1a, - 0x08, 0x6d, 0x83, 0x8c, 0x1f, 0x3b, 0xb6, 0xa6, 0xfa, 0x5c, 0x06, 0xe9, 0x22, 0x2e, 0x43, 0x92, - 0x0d, 0xdf, 0xf5, 0xfc, 0x86, 0x4f, 0x20, 0x61, 0xe3, 0x26, 0x75, 0xec, 0x1b, 0x96, 0xb9, 0x6f, - 0x34, 0xc5, 0x9b, 0x7e, 0x6b, 0x6c, 0xbf, 0x28, 0xaf, 0xb0, 0xe1, 0x25, 0x36, 0x5a, 0x89, 0xdb, - 0xbe, 0xa7, 0xcc, 0xb7, 0x24, 0x98, 0x1b, 0xae, 0x44, 0x87, 0xc8, 0xda, 0x4e, 0xbf, 0xac, 0xdd, - 0xb9, 0xbc, 0x9e, 0xf6, 0x49, 0xc8, 0x5a, 0x30, 0x22, 0xc9, 0x81, 0xb5, 0x60, 0x24, 0x21, 0x27, - 0x73, 0x6f, 0x50, 0x61, 0x61, 0x23, 0xbd, 0x64, 0xcd, 0x4b, 0x00, 0x07, 0x46, 0xf3, 0x40, 0x7d, - 0xa4, 0x39, 0xd8, 0x16, 0xa9, 0xe4, 0x28, 0xa5, 0x7c, 0x40, 0x09, 0xb9, 0x7f, 0x8a, 0x43, 0xa2, - 0xda, 0xee, 0x58, 0xb6, 0xe3, 0x7a, 0xf4, 0x1b, 0x10, 0xe2, 0x3e, 0x84, 0x60, 0x7b, 0x7e, 0xc4, - 0x32, 0xfb, 0x46, 0x72, 0x1f, 0x50, 0x18, 0x2d, 0x81, 0x81, 0xb6, 0x21, 0xcc, 0x1d, 0x2f, 0x92, - 0x9e, 0x67, 0x70, 0x4b, 0x63, 0xc3, 0x71, 0x17, 0xce, 0x75, 0xb7, 0x04, 0x0a, 0xaa, 0xc2, 0x14, - 0x95, 0x0c, 0x92, 0xce, 0x30, 0xb8, 0xd7, 0xc7, 0x5f, 0xdd, 0x51, 0xc7, 0x5d, 0x1c, 0x47, 0xf0, - 0xdc, 0xf8, 0xc0, 0x50, 0x37, 0xfe, 0x5d, 0x08, 0xf1, 0x2b, 0x05, 0x91, 0x49, 0xcc, 0x0e, 0x49, - 0x41, 0x56, 0xb7, 0x97, 0x8d, 0x16, 0x5e, 0x66, 0xdd, 0xdc, 0x8d, 0xf3, 0x41, 0xe8, 0x15, 0x88, - 0x10, 0xe2, 0xa8, 0xc4, 0xf8, 0x3a, 0xd7, 0xe8, 0x93, 0x3c, 0x5d, 0x5e, 0xab, 0xed, 0xd6, 0x8c, - 0xaf, 0x63, 0x25, 0x4c, 0x88, 0x43, 0x7f, 0xa0, 0x05, 0x60, 0xbe, 0x21, 0xd1, 0xa8, 0xc7, 0xc7, - 0x9c, 0xbb, 0x49, 0xc5, 0x47, 0x61, 0x38, 0x87, 0x46, 0x47, 0xdd, 0x3f, 0x24, 0xdc, 0xa3, 0x12, - 0x38, 0x87, 0x46, 0x67, 0x79, 0x9d, 0x28, 0x61, 0xda, 0xb8, 0x7c, 0x48, 0x50, 0x06, 0x22, 0x8f, - 0xb4, 0x56, 0x8b, 0x05, 0x62, 0x53, 0x0c, 0xc5, 0x7b, 0xee, 0x37, 0x75, 0xa1, 0x5f, 0xac, 0xa9, - 0x13, 0xa1, 0x4f, 0x47, 0x73, 0x0e, 0x58, 0x30, 0x1f, 0x55, 0x80, 0x93, 0x76, 0x34, 0xe7, 0x00, - 0xa5, 0x21, 0xcc, 0xf7, 0x45, 0xd2, 0x91, 0xc5, 0xc9, 0x9b, 0x71, 0xc5, 0x7d, 0x44, 0xaf, 0xc2, - 0x34, 0xcf, 0x0a, 0xaa, 0xba, 0x61, 0xe3, 0x86, 0xd3, 0x3a, 0x62, 0xde, 0x60, 0x44, 0x49, 0x72, - 0x72, 0x59, 0x50, 0xd1, 0x2d, 0x90, 0x07, 0xdd, 0x67, 0xe6, 0xc5, 0x45, 0x94, 0xe9, 0x01, 0xef, - 0x99, 0x7a, 0xda, 0x42, 0x6c, 0x7c, 0x6e, 0x69, 0x9a, 0x7b, 0xda, 0xa2, 0xa1, 0xe7, 0x92, 0xde, - 0x02, 0x59, 0xf8, 0xce, 0xbd, 0xbe, 0x09, 0x8e, 0xcb, 0xe9, 0xbd, 0xae, 0x79, 0x98, 0xe9, 0x68, - 0x36, 0xc1, 0x6a, 0xbd, 0x6b, 0xea, 0x2d, 0xac, 0x72, 0xac, 0x74, 0x92, 0xf5, 0x4e, 0xb1, 0xa6, - 0x22, 0x6b, 0xe1, 0x22, 0x7c, 0x5e, 0xce, 0x63, 0xee, 0x57, 0x91, 0xf3, 0xb8, 0x09, 0xb2, 0x8e, - 0xf7, 0xb5, 0x6e, 0xcb, 0x51, 0x0d, 0x53, 0xc8, 0xe9, 0x55, 0xea, 0x7e, 0x2b, 0x49, 0x41, 0xaf, - 0x9a, 0x5c, 0x42, 0xbf, 0x09, 0xf3, 0x9e, 0xaf, 0xd9, 0xb1, 0x8d, 0xb6, 0x66, 0x1f, 0xa9, 0x5c, - 0x09, 0xa6, 0x3f, 0xc3, 0x5c, 0x95, 0xe5, 0x67, 0xc7, 0xd9, 0xe2, 0x65, 0xe5, 0x87, 0x2b, 0x57, - 0xe6, 0xe3, 0x5c, 0x71, 0xa7, 0xd9, 0xe1, 0xb3, 0xf0, 0xa6, 0xcc, 0x0f, 0x02, 0x30, 0xc5, 0x54, - 0x0b, 0xba, 0x03, 0x41, 0x3a, 0x4a, 0xe4, 0x13, 0xc7, 0x0d, 0x45, 0xd9, 0x18, 0x84, 0x20, 0xc8, - 0xbc, 0x2b, 0xc4, 0x64, 0x92, 0xfd, 0x46, 0xf3, 0x10, 0x26, 0xf8, 0x81, 0xfa, 0x50, 0x6b, 0xa5, - 0x67, 0xd8, 0x91, 0x09, 0x11, 0xfc, 0xe0, 0xbe, 0xd6, 0x42, 0x57, 0x20, 0x64, 0x10, 0xd5, 0xc4, - 0x8f, 0x98, 0x97, 0x1a, 0x51, 0xa6, 0x0c, 0xb2, 0x85, 0x1f, 0xa1, 0xcf, 0x40, 0xf4, 0x91, 0x46, - 0x54, 0xdc, 0xee, 0x38, 0x47, 0xec, 0xad, 0x45, 0xe8, 0x21, 0x23, 0x15, 0xfa, 0xcc, 0xdc, 0x3c, - 0xcd, 0x6e, 0x62, 0x47, 0x6d, 0x58, 0x2d, 0x1e, 0x57, 0x46, 0x69, 0x18, 0x4c, 0x49, 0x25, 0xab, - 0x45, 0xd6, 0x82, 0x91, 0x80, 0x3c, 0xb9, 0x16, 0x8c, 0x4c, 0xca, 0xc1, 0xb5, 0x60, 0x24, 0x28, - 0x4f, 0xad, 0x05, 0x23, 0x53, 0x72, 0x68, 0x2d, 0x18, 0x09, 0xc9, 0xe1, 0xb5, 0x60, 0x24, 0x2c, - 0x47, 0xd6, 0x82, 0x91, 0x88, 0x1c, 0x5d, 0x0b, 0x46, 0xa2, 0x32, 0xac, 0x05, 0x23, 0x20, 0xc7, - 0xd6, 0x82, 0x91, 0x98, 0x1c, 0x5f, 0x0b, 0x46, 0xe2, 0x72, 0x82, 0x2b, 0xf9, 0xb5, 0x60, 0x24, - 0x29, 0x4f, 0xaf, 0x05, 0x23, 0xd3, 0xb2, 0xbc, 0x16, 0x8c, 0xc8, 0x72, 0x6a, 0x2d, 0x18, 0x49, - 0xc9, 0x28, 0x53, 0x11, 0xb7, 0x37, 0x1a, 0xfa, 0x4a, 0x1f, 0x9f, 0xc6, 0x0e, 0x91, 0xd9, 0xa0, - 0x4c, 0x01, 0x82, 0x54, 0x55, 0xa2, 0x77, 0xfa, 0x40, 0xc6, 0x34, 0xbe, 0x6c, 0x48, 0xee, 0x47, - 0x12, 0xc8, 0x35, 0xfc, 0xa0, 0x8b, 0xcd, 0x06, 0xbe, 0xaf, 0xb5, 0x4a, 0x07, 0x5d, 0xf3, 0x10, - 0xbd, 0x02, 0xd3, 0x0d, 0xfa, 0x43, 0xe5, 0x89, 0x61, 0xca, 0x74, 0x89, 0x31, 0x3d, 0xc1, 0xc8, - 0x35, 0x4a, 0xa5, 0xbc, 0x7f, 0x09, 0x40, 0xf4, 0xa3, 0x22, 0x19, 0x60, 0x5d, 0xa2, 0xbc, 0x0b, - 0x95, 0xc6, 0x01, 0x18, 0xdb, 0x7a, 0xc4, 0xf4, 0x73, 0x1f, 0x8c, 0x62, 0x3d, 0x42, 0x4b, 0x30, - 0x6b, 0xe2, 0xc7, 0x8e, 0x3a, 0xd8, 0x99, 0xe9, 0x62, 0x25, 0x45, 0xdb, 0x4a, 0xfe, 0x01, 0xb9, - 0x7f, 0x0c, 0xc0, 0xb4, 0xbb, 0x68, 0xd7, 0x16, 0xee, 0x83, 0x4c, 0x05, 0xc4, 0xd0, 0x55, 0xc7, - 0xe2, 0x48, 0xae, 0x55, 0x7c, 0x77, 0x54, 0x46, 0xaf, 0x1f, 0x85, 0x3e, 0x57, 0xf5, 0x5d, 0x8b, - 0x4d, 0xc7, 0x9d, 0x03, 0x25, 0x41, 0xfc, 0xb4, 0xcc, 0x1e, 0x24, 0xdd, 0x41, 0x9c, 0x82, 0x4a, - 0x10, 0xea, 0x9b, 0xef, 0x73, 0x63, 0xcc, 0xe7, 0xb2, 0x5a, 0x11, 0x43, 0x33, 0xdf, 0x00, 0x74, - 0x7a, 0x6e, 0xbf, 0x63, 0x32, 0xc5, 0x1d, 0x93, 0xed, 0x7e, 0xc7, 0xe4, 0x9d, 0x8b, 0xed, 0xcd, - 0xb7, 0x6c, 0x7f, 0xfa, 0xf0, 0xbb, 0x93, 0x90, 0xe4, 0x16, 0xd8, 0xf3, 0x45, 0xa8, 0x3e, 0xa6, - 0xea, 0xde, 0x30, 0x9b, 0xbd, 0xcb, 0x2c, 0xba, 0xbf, 0x80, 0x22, 0xbb, 0x0d, 0x5e, 0xe7, 0x97, - 0xa9, 0xdf, 0xa6, 0xe9, 0xfd, 0xb7, 0x5e, 0x01, 0xea, 0x7f, 0x69, 0xba, 0xd7, 0xe9, 0x06, 0x24, - 0x99, 0xef, 0xdd, 0xeb, 0x35, 0xc9, 0x7a, 0x25, 0x18, 0xd5, 0xeb, 0x56, 0x84, 0x04, 0xe9, 0x68, - 0xbe, 0x1b, 0xb4, 0x20, 0x63, 0xea, 0x39, 0xb7, 0x8a, 0x71, 0x3a, 0xc6, 0xef, 0x48, 0xd9, 0x98, - 0x74, 0xdb, 0x58, 0xed, 0x58, 0x3c, 0x1b, 0x36, 0xa9, 0x44, 0x39, 0x65, 0xc7, 0x22, 0x68, 0x8f, - 0x89, 0x0a, 0xe3, 0x85, 0xaa, 0x73, 0xe6, 0xa4, 0x43, 0x43, 0x73, 0x4b, 0x23, 0xd8, 0xa9, 0x4c, - 0x93, 0x01, 0x09, 0x7c, 0x1f, 0xc2, 0xa4, 0xdb, 0xa6, 0xda, 0x90, 0x59, 0xd3, 0xd8, 0xed, 0xc5, - 0x21, 0x6b, 0x2e, 0x76, 0x5b, 0x87, 0xdb, 0x9d, 0x1a, 0xef, 0xe7, 0x39, 0x4c, 0xfc, 0x31, 0xf7, - 0x57, 0x12, 0xcc, 0xd3, 0x53, 0xca, 0x8f, 0x7b, 0x89, 0x15, 0x2b, 0xb8, 0xe8, 0x1a, 0x84, 0x99, - 0x9b, 0xed, 0x85, 0x13, 0xab, 0x27, 0xc7, 0xd9, 0x10, 0xed, 0xfd, 0xdc, 0x4e, 0x41, 0x88, 0x02, - 0x57, 0x59, 0x7a, 0xc8, 0xb1, 0x35, 0x93, 0xb0, 0xcb, 0x36, 0xfa, 0xe2, 0xdb, 0xb8, 0x5d, 0xc7, - 0x36, 0x7f, 0x9d, 0x71, 0x65, 0xb6, 0xaf, 0x71, 0x93, 0xb7, 0xe5, 0x32, 0x90, 0x1e, 0x5c, 0xb2, - 0x97, 0x84, 0xfe, 0x35, 0x98, 0xdb, 0xc2, 0x8f, 0x86, 0xed, 0xa6, 0x08, 0x61, 0xae, 0x6e, 0xdd, - 0x43, 0x73, 0x73, 0x50, 0x69, 0xf9, 0xeb, 0x35, 0xf2, 0x6c, 0xa5, 0xbb, 0x6c, 0x80, 0xe2, 0x0e, - 0xcc, 0x7d, 0x02, 0xf3, 0x03, 0xe8, 0x9e, 0x00, 0xbc, 0x0f, 0x21, 0xe2, 0x68, 0x8e, 0x70, 0x8c, - 0x93, 0xe3, 0xa0, 0xd7, 0x1c, 0xcd, 0xe9, 0x12, 0x45, 0x8c, 0xcb, 0xdd, 0x80, 0x97, 0x0b, 0x5d, - 0xc7, 0xa2, 0x22, 0x26, 0xa2, 0x09, 0xdc, 0xb0, 0xcc, 0x86, 0xd1, 0x32, 0x34, 0xdf, 0x65, 0x7a, - 0xee, 0x15, 0xb8, 0x3e, 0xaa, 0x9b, 0xc7, 0x09, 0x85, 0x65, 0xe3, 0xbb, 0x6d, 0x4c, 0x7b, 0x6e, - 0x18, 0xc4, 0x41, 0xef, 0x43, 0x5c, 0xc8, 0x28, 0x15, 0x5d, 0x97, 0x0d, 0xe7, 0x88, 0x79, 0xcc, - 0xf6, 0x40, 0x48, 0xee, 0xaf, 0x25, 0x98, 0x29, 0xdb, 0x56, 0xa7, 0x83, 0x75, 0x61, 0x47, 0x39, - 0x6f, 0x5d, 0xf3, 0x29, 0xf9, 0xcc, 0xe7, 0x16, 0x04, 0xaa, 0x65, 0x11, 0x25, 0xde, 0x7d, 0xde, - 0xe0, 0xb3, 0x5a, 0x46, 0xef, 0x70, 0x06, 0x77, 0x09, 0xd3, 0xe8, 0xc9, 0xdb, 0x9f, 0x1d, 0x79, - 0xaf, 0xdb, 0xe3, 0x6c, 0x97, 0xe4, 0xbe, 0x1f, 0x86, 0x2b, 0xfe, 0x97, 0xb6, 0x52, 0x72, 0x17, - 0xfe, 0x29, 0x84, 0xdd, 0x7c, 0xdc, 0x18, 0x9a, 0x7b, 0x18, 0x44, 0x5e, 0xf0, 0xc3, 0x9f, 0x93, - 0x73, 0x31, 0x51, 0x0d, 0x52, 0x86, 0xe9, 0x60, 0xbb, 0x85, 0xb5, 0x87, 0xd4, 0xb7, 0xa3, 0x3c, - 0x13, 0x17, 0x21, 0xe3, 0xfa, 0x27, 0xb2, 0x0f, 0x80, 0xfb, 0x39, 0x9f, 0xc2, 0x8c, 0x1f, 0xd4, - 0x5d, 0xff, 0xe8, 0x0c, 0x3c, 0x5b, 0x5e, 0x0f, 0xd6, 0xbd, 0x2a, 0xf0, 0x01, 0xb9, 0xd9, 0xc3, - 0x0f, 0xbd, 0x08, 0x8f, 0xdf, 0xb2, 0xdc, 0xb9, 0x34, 0x47, 0xca, 0x03, 0xd1, 0x5e, 0x5f, 0xa0, - 0xc1, 0xcc, 0xf2, 0x2f, 0x28, 0xd0, 0xb8, 0x0f, 0x21, 0x9e, 0x7f, 0x17, 0x17, 0x9e, 0x77, 0x2f, - 0xbb, 0x05, 0x9e, 0xe0, 0x57, 0x04, 0x5a, 0xe6, 0x0f, 0x24, 0x88, 0xfb, 0x5f, 0x37, 0x32, 0x20, - 0xc2, 0xd8, 0xef, 0xaa, 0xc8, 0xc9, 0x17, 0x9e, 0x0f, 0xe1, 0xa2, 0x54, 0xd5, 0xa9, 0x77, 0xa9, - 0xdb, 0x56, 0xa7, 0x77, 0xe1, 0x3d, 0xa9, 0x44, 0x28, 0x81, 0x7a, 0xee, 0x99, 0x6f, 0x42, 0xd4, - 0x63, 0xba, 0x2f, 0x21, 0x3a, 0xf9, 0x02, 0x13, 0xa2, 0x23, 0xe7, 0x2f, 0x43, 0xa2, 0x8f, 0x63, - 0x68, 0xce, 0x5b, 0x43, 0xb0, 0x18, 0xe2, 0x6b, 0x38, 0x17, 0x25, 0xf7, 0xc3, 0x30, 0xcc, 0x0c, - 0xd3, 0xdc, 0x1f, 0x81, 0xec, 0xd3, 0x5b, 0x6a, 0xcb, 0x20, 0x8e, 0x90, 0xcd, 0x5b, 0xa3, 0x93, - 0x24, 0x3e, 0xe5, 0x27, 0x44, 0x31, 0x69, 0xf7, 0xab, 0xc4, 0x4f, 0x20, 0xa9, 0xf3, 0x85, 0x8b, - 0xab, 0x11, 0x51, 0xb3, 0x35, 0x2a, 0xad, 0x31, 0x44, 0x01, 0x0a, 0xf4, 0x84, 0xee, 0x6b, 0x22, - 0xa8, 0x01, 0x09, 0x0f, 0x9c, 0x25, 0x25, 0x68, 0x50, 0xfb, 0xfc, 0xca, 0x30, 0xee, 0xce, 0xc2, - 0xd2, 0x14, 0x4d, 0x98, 0x76, 0x27, 0x71, 0x53, 0x29, 0xd1, 0x17, 0x32, 0x8d, 0xcb, 0x98, 0x9a, - 0x48, 0xad, 0x7c, 0x47, 0x82, 0x19, 0x77, 0x26, 0x2f, 0xe2, 0x13, 0x85, 0x5a, 0x89, 0x62, 0xed, - 0xe4, 0x38, 0x9b, 0x12, 0x9c, 0x71, 0xf3, 0x51, 0xcf, 0x2d, 0x77, 0x29, 0x7d, 0x00, 0x50, 0xa7, - 0x3e, 0x09, 0x6d, 0x77, 0xeb, 0xaf, 0x84, 0x4f, 0x42, 0x15, 0xdb, 0xf3, 0xfb, 0x24, 0xf4, 0x67, - 0x55, 0x47, 0xdf, 0x95, 0x20, 0xc5, 0xaf, 0x36, 0xdb, 0x5d, 0x47, 0xe3, 0xc5, 0x0d, 0x6e, 0x62, - 0xe4, 0xa3, 0x93, 0xe3, 0xec, 0x34, 0x7b, 0xbd, 0x9b, 0xa2, 0x8d, 0x4d, 0x7b, 0xe9, 0xf8, 0xb6, - 0x87, 0x22, 0xf2, 0x08, 0x1e, 0x41, 0x47, 0xeb, 0x90, 0xe4, 0xd9, 0x22, 0xb7, 0x26, 0x94, 0xf9, - 0x78, 0x89, 0xe2, 0xf5, 0x67, 0xc7, 0xd9, 0xc5, 0x21, 0xe7, 0x84, 0x27, 0x9a, 0xee, 0xf3, 0xbe, - 0x4a, 0x62, 0xdf, 0xff, 0x88, 0x36, 0x60, 0x9a, 0xbb, 0xc2, 0xbd, 0x92, 0x2c, 0x18, 0xff, 0x42, - 0x9e, 0xbb, 0xd1, 0x1e, 0x95, 0x67, 0x15, 0x73, 0x73, 0x30, 0x3b, 0xd4, 0x07, 0xfb, 0x69, 0x08, - 0xe6, 0xfa, 0xd5, 0xaa, 0xe7, 0x25, 0xa9, 0x83, 0xf6, 0xf6, 0xbd, 0xb1, 0x55, 0xb3, 0x57, 0x9c, - 0xc5, 0x54, 0xa3, 0xfb, 0x34, 0x68, 0x71, 0x3f, 0x1d, 0xb0, 0x5e, 0x97, 0xc0, 0x67, 0xaf, 0x77, - 0x00, 0xdf, 0x35, 0x61, 0x1f, 0x7a, 0x96, 0x85, 0xa7, 0xfd, 0xde, 0xbf, 0x04, 0x3c, 0x1b, 0xef, - 0x15, 0x96, 0xb9, 0xb6, 0xe5, 0x1f, 0x24, 0x48, 0xf4, 0xed, 0xec, 0x97, 0x69, 0x5c, 0x76, 0x3c, - 0xdf, 0x8a, 0x17, 0x88, 0xbd, 0x7d, 0xf1, 0x6d, 0xf5, 0xbb, 0x5c, 0x99, 0xbf, 0x95, 0x20, 0xd1, - 0xc7, 0xc8, 0x5f, 0x90, 0x59, 0x7a, 0xf1, 0x2b, 0xaf, 0x43, 0xb2, 0xff, 0x15, 0xf9, 0xe6, 0x90, - 0x5e, 0xcc, 0x1c, 0xb9, 0x2f, 0x43, 0x88, 0x53, 0x10, 0x82, 0xe4, 0x07, 0x85, 0xea, 0x6e, 0x75, - 0x6b, 0x45, 0x5d, 0xde, 0x56, 0xd4, 0x95, 0x92, 0x3c, 0x81, 0xe2, 0x10, 0x29, 0x57, 0x36, 0x2a, - 0x94, 0x28, 0x4b, 0x28, 0x06, 0x61, 0xf6, 0x54, 0x29, 0xcb, 0x81, 0x5c, 0x11, 0x64, 0x8e, 0xbd, - 0x8f, 0xa9, 0x99, 0xa1, 0x51, 0x09, 0xca, 0xc3, 0x0c, 0x8b, 0x20, 0xda, 0xd4, 0xb3, 0xa2, 0xc7, - 0x5b, 0xf5, 0xf9, 0xe2, 0x29, 0xaf, 0x89, 0x9e, 0xde, 0x2d, 0xad, 0x8d, 0x73, 0x7f, 0x13, 0x84, - 0x54, 0x0f, 0xc4, 0x35, 0xb2, 0x7f, 0x21, 0xf5, 0xe2, 0xa3, 0xd0, 0xb9, 0x57, 0xd3, 0xa7, 0xc6, - 0x8b, 0x50, 0x49, 0x5c, 0x11, 0x7f, 0x40, 0x0f, 0xcd, 0xb3, 0xe3, 0x6c, 0x6a, 0x70, 0xb1, 0xe4, - 0x39, 0xef, 0x8e, 0xdd, 0x25, 0xb2, 0xc4, 0xb7, 0x61, 0x1e, 0xaa, 0xbd, 0x1a, 0x3e, 0x9e, 0xf8, - 0x36, 0xcc, 0xc3, 0x3d, 0xa5, 0xaa, 0x84, 0x69, 0xe3, 0x9e, 0x6d, 0xa0, 0x35, 0x08, 0x5a, 0x1d, - 0xc7, 0x0d, 0xe9, 0xdf, 0xba, 0xd0, 0x96, 0xb6, 0x3b, 0x62, 0x3f, 0x0a, 0xc3, 0x40, 0x6b, 0xbc, - 0xda, 0xa2, 0xc7, 0x68, 0x11, 0x74, 0x8f, 0xa5, 0x42, 0x13, 0x7d, 0x2f, 0x22, 0xd3, 0x84, 0xb8, - 0x9f, 0x63, 0x43, 0xee, 0x83, 0x0a, 0xfd, 0x69, 0x97, 0xcf, 0x8d, 0xb5, 0x74, 0x11, 0xb0, 0xfa, - 0xae, 0x08, 0xbf, 0x0c, 0x51, 0x6f, 0x1f, 0x17, 0xb9, 0x29, 0xf5, 0x6e, 0x8e, 0x78, 0x5a, 0x72, - 0x4a, 0x0e, 0xe5, 0xbe, 0x1f, 0x80, 0xb8, 0x82, 0x89, 0xd5, 0x7a, 0x88, 0x75, 0xea, 0x41, 0x79, - 0x55, 0xd8, 0xd2, 0xf8, 0x55, 0xd8, 0x05, 0x88, 0xf6, 0x2c, 0xd0, 0x05, 0xea, 0x36, 0x7b, 0xa3, - 0xd0, 0x47, 0x90, 0xa8, 0x5b, 0x5d, 0x53, 0xd7, 0xec, 0x23, 0xe6, 0x57, 0x31, 0x0f, 0x24, 0x39, - 0xb2, 0x8a, 0xce, 0xbf, 0xea, 0x7c, 0x51, 0x0c, 0xa6, 0xfe, 0x93, 0x12, 0xaf, 0xfb, 0x9e, 0x72, - 0xef, 0x42, 0xdc, 0xdf, 0x8a, 0x22, 0x10, 0xdc, 0xda, 0xde, 0xaa, 0xf0, 0x33, 0x59, 0x2c, 0x94, - 0xd6, 0x97, 0xab, 0x1b, 0x1b, 0xb2, 0x44, 0xe9, 0x95, 0x0f, 0xab, 0xbb, 0x72, 0x80, 0x57, 0xba, - 0xd6, 0x76, 0x0b, 0xca, 0xae, 0x9b, 0xbd, 0xcd, 0x61, 0x48, 0xf8, 0xe7, 0xa3, 0x9a, 0x8f, 0xba, - 0x9d, 0x8c, 0xd0, 0x17, 0x79, 0xbf, 0x3a, 0xe6, 0x8a, 0x5d, 0x09, 0xb2, 0xfd, 0xa8, 0xb9, 0x7f, - 0x09, 0x00, 0xea, 0xbd, 0x78, 0x4f, 0x59, 0x7d, 0x08, 0xd0, 0x38, 0xc0, 0x8d, 0xc3, 0x8e, 0x65, - 0x98, 0x8e, 0x88, 0x35, 0xdf, 0x1e, 0x4b, 0x76, 0x3c, 0x65, 0x55, 0xf2, 0xc6, 0x2b, 0x3e, 0x2c, - 0xf4, 0xfb, 0xa3, 0xef, 0x29, 0x26, 0xd9, 0x3d, 0x05, 0x3b, 0xf9, 0xbf, 0xd4, 0xbb, 0x8a, 0x4c, - 0x01, 0xa0, 0xb7, 0x62, 0xf4, 0x26, 0x4c, 0x5d, 0x20, 0xbb, 0xc1, 0xfb, 0xfa, 0x65, 0x3d, 0xf7, - 0x7f, 0x41, 0x40, 0x25, 0x1b, 0x6b, 0x0e, 0xa6, 0x2a, 0x9a, 0x8c, 0x4a, 0x71, 0x14, 0x61, 0x8a, - 0x87, 0xf4, 0x81, 0x8b, 0x84, 0xf4, 0xde, 0x35, 0x23, 0x8b, 0xe6, 0xbf, 0x06, 0xf1, 0x86, 0xd5, - 0xea, 0xb6, 0x4d, 0x95, 0xd5, 0x6c, 0x89, 0xf8, 0xe3, 0x4b, 0xa3, 0xde, 0xd8, 0xa9, 0xc5, 0xe5, - 0x4b, 0x56, 0x8b, 0x3e, 0x7b, 0xdf, 0x08, 0x30, 0x40, 0xd6, 0x03, 0x5d, 0x83, 0xa8, 0xa7, 0x79, - 0x78, 0xf1, 0x88, 0xd2, 0x23, 0xa0, 0xdb, 0x30, 0xa5, 0x11, 0xd5, 0xda, 0x67, 0xce, 0xf4, 0x79, - 0x47, 0x51, 0x09, 0x6a, 0x64, 0x7b, 0x1f, 0xbd, 0x06, 0xa9, 0xb6, 0xf6, 0x58, 0xdd, 0xb7, 0x79, - 0x15, 0xb8, 0x6a, 0xe8, 0x2d, 0xae, 0x09, 0x25, 0x65, 0xba, 0xad, 0x3d, 0x5e, 0x16, 0xf4, 0xaa, - 0xde, 0xc2, 0xe8, 0x4d, 0x48, 0xec, 0x3f, 0xe0, 0xa1, 0x15, 0xb7, 0x4a, 0xbc, 0x00, 0x6e, 0xfa, - 0xe4, 0x38, 0x1b, 0x5b, 0xbe, 0xc7, 0x18, 0xc3, 0x2e, 0x77, 0x62, 0xfb, 0x0f, 0xbc, 0x87, 0xcc, - 0xff, 0x4a, 0x10, 0x16, 0x3b, 0x42, 0x1d, 0x00, 0xc1, 0x1e, 0x43, 0xe7, 0xef, 0x34, 0x51, 0xbc, - 0x77, 0x72, 0x9c, 0x8d, 0x96, 0x18, 0xb5, 0x5a, 0x26, 0xcf, 0x8e, 0xb3, 0xef, 0x5f, 0xd6, 0xa2, - 0xb8, 0x20, 0x4a, 0x94, 0x4f, 0x52, 0xd5, 0x59, 0x66, 0xf9, 0x40, 0x23, 0xea, 0x81, 0x41, 0x1c, - 0xab, 0x69, 0x6b, 0x6d, 0x51, 0x75, 0x11, 0x3f, 0xd0, 0xc8, 0xaa, 0x4b, 0x43, 0x19, 0xea, 0x9b, - 0x3d, 0xe4, 0x25, 0x77, 0xbc, 0x64, 0xc7, 0x7b, 0x46, 0xb7, 0xe1, 0x8a, 0x37, 0x58, 0xa5, 0x9c, - 0xaa, 0x77, 0x1b, 0x87, 0x98, 0xd9, 0x20, 0xaa, 0xdc, 0x67, 0xbc, 0xc6, 0x4d, 0xed, 0x71, 0x91, - 0x37, 0xe5, 0xae, 0xc0, 0x8c, 0xef, 0xb5, 0x7a, 0x9e, 0x34, 0x06, 0x99, 0x17, 0x87, 0xf8, 0x3e, - 0xa6, 0xb9, 0x07, 0xd3, 0x03, 0xdf, 0x8a, 0x09, 0xfd, 0xeb, 0xcf, 0x38, 0xf6, 0x7f, 0x5c, 0x96, - 0x2f, 0xf1, 0x47, 0x37, 0x36, 0x48, 0x36, 0xfa, 0x9e, 0x73, 0x5f, 0x80, 0x94, 0x37, 0x8d, 0xa7, - 0x48, 0xae, 0x41, 0x94, 0x55, 0x05, 0xb4, 0x35, 0xfb, 0xd0, 0xad, 0x0c, 0xf0, 0x08, 0xb9, 0x2c, - 0xbc, 0xc4, 0xb2, 0x90, 0xf7, 0x36, 0xd8, 0x8a, 0x4b, 0x56, 0xbb, 0xc3, 0x5f, 0xbb, 0x9b, 0xa6, - 0x5c, 0x84, 0x85, 0xe1, 0x1d, 0xbc, 0xcd, 0xfd, 0xf7, 0x34, 0x84, 0x77, 0xb4, 0xa3, 0x96, 0xa5, - 0xe9, 0x68, 0x11, 0x62, 0x6e, 0x31, 0x9e, 0xbb, 0xa1, 0xa8, 0xe2, 0x27, 0xf5, 0xcb, 0xb1, 0xcc, - 0xae, 0xcf, 0x7c, 0x72, 0x6c, 0x40, 0xb2, 0x4b, 0xb0, 0x4d, 0x45, 0x4c, 0x65, 0x5f, 0xce, 0x71, - 0x7b, 0x56, 0x2c, 0x3e, 0x3b, 0xce, 0xde, 0x1d, 0x4f, 0x3a, 0x70, 0xa3, 0x6b, 0x1b, 0xce, 0x51, - 0xbe, 0x76, 0x6f, 0x63, 0x4f, 0x40, 0x51, 0x65, 0x64, 0x29, 0x89, 0xae, 0xff, 0x51, 0xd4, 0x5c, - 0xd2, 0x37, 0xad, 0xb6, 0x8d, 0x86, 0x6d, 0x11, 0xf7, 0x86, 0x49, 0x50, 0x37, 0x19, 0x11, 0xbd, - 0x0a, 0xd3, 0xfb, 0x86, 0xc9, 0x6e, 0x84, 0xdd, 0x7e, 0xfc, 0x72, 0x29, 0xe9, 0x92, 0x45, 0xc7, - 0x87, 0x90, 0xf4, 0x15, 0x3b, 0x52, 0x29, 0x0f, 0x31, 0x29, 0xdf, 0x3e, 0x39, 0xce, 0x26, 0x7a, - 0x5a, 0x83, 0x4b, 0xfa, 0xf3, 0xf8, 0x4e, 0x89, 0xde, 0x34, 0x54, 0xce, 0x67, 0x61, 0x8a, 0x7d, - 0x6a, 0xc9, 0xab, 0xd9, 0x15, 0xfe, 0x80, 0x2a, 0x90, 0x10, 0xb9, 0x16, 0xfe, 0x1d, 0xa6, 0xa8, - 0x10, 0xf5, 0xdf, 0x2b, 0xb8, 0x5f, 0x6a, 0xe6, 0x2b, 0x66, 0xc3, 0xd2, 0xb1, 0x5e, 0xa1, 0xcf, - 0x8a, 0x48, 0x2d, 0xb3, 0x07, 0x82, 0x56, 0x20, 0xd9, 0x68, 0x61, 0xcd, 0xec, 0x76, 0x5c, 0x1c, - 0x34, 0x26, 0x4e, 0x42, 0x8c, 0x13, 0x40, 0x5b, 0x80, 0xf6, 0x59, 0xad, 0x9a, 0x7f, 0x55, 0xec, - 0x3e, 0x76, 0x1c, 0x30, 0x99, 0x8d, 0x55, 0x7a, 0x2b, 0x43, 0xd7, 0x21, 0x61, 0x5a, 0x66, 0x43, - 0x33, 0x1b, 0xb8, 0xc5, 0x54, 0x37, 0xbf, 0xc2, 0xed, 0x27, 0xa2, 0x22, 0x84, 0x78, 0x59, 0x82, - 0x08, 0x92, 0x6f, 0x8e, 0xfb, 0xcd, 0xc7, 0xea, 0x84, 0x22, 0x46, 0xa2, 0x0a, 0x84, 0x6d, 0x5e, - 0x6d, 0xc3, 0x4a, 0x15, 0xce, 0x4d, 0x56, 0xf9, 0x2a, 0x7a, 0x56, 0x27, 0x14, 0x77, 0x2c, 0xda, - 0x75, 0x0b, 0x9b, 0xb9, 0xa1, 0x16, 0x25, 0xa9, 0xf9, 0x31, 0x43, 0x90, 0x1e, 0x60, 0x1f, 0x0a, - 0xdd, 0xa0, 0xc1, 0x6e, 0xdf, 0x58, 0x11, 0xc3, 0xe8, 0x0d, 0xf6, 0x15, 0xca, 0xd0, 0x0d, 0xf2, - 0x91, 0x68, 0x8b, 0x7a, 0x1a, 0xae, 0xf3, 0xc0, 0xca, 0x1b, 0x62, 0xb7, 0x3f, 0x7f, 0x11, 0x07, - 0x7b, 0x75, 0x42, 0xf1, 0x21, 0xa0, 0x7b, 0x10, 0x6b, 0xf4, 0x74, 0x60, 0x7a, 0x9a, 0x01, 0xbe, - 0x7e, 0x21, 0x43, 0xb8, 0x4a, 0x8d, 0x5f, 0x8f, 0x8a, 0x3e, 0x86, 0x24, 0xe9, 0x0b, 0xc8, 0xd2, - 0x57, 0x18, 0xea, 0x1b, 0x17, 0x4d, 0x08, 0xaf, 0x4e, 0x28, 0x03, 0x48, 0xe8, 0xd7, 0x41, 0x76, - 0x06, 0x6e, 0xa1, 0xd8, 0xad, 0xff, 0xe8, 0x7a, 0xe1, 0x33, 0xee, 0xda, 0x56, 0x27, 0x94, 0x53, - 0x68, 0xe8, 0x53, 0x98, 0x26, 0xfd, 0x1f, 0xa8, 0xa5, 0xe7, 0xd9, 0x04, 0x5f, 0x18, 0xff, 0x93, - 0xb6, 0x1e, 0xfe, 0x20, 0x16, 0x85, 0x37, 0xfb, 0x2f, 0xb3, 0x58, 0xf5, 0xcb, 0x68, 0xf8, 0xe1, - 0x97, 0x6b, 0x14, 0x7e, 0x00, 0x0b, 0xad, 0x43, 0xb4, 0xed, 0x1a, 0x15, 0x56, 0x3b, 0x32, 0x3a, - 0x86, 0x19, 0xb4, 0x73, 0xab, 0x13, 0x4a, 0x6f, 0x3c, 0xfa, 0x4d, 0x09, 0xae, 0x69, 0x23, 0x6e, - 0xbd, 0x58, 0xad, 0xc9, 0xe8, 0x44, 0xff, 0x18, 0x77, 0x6b, 0xab, 0x13, 0xca, 0xc8, 0x59, 0x90, - 0x0d, 0x73, 0xda, 0x50, 0xa3, 0x96, 0x5e, 0x38, 0xd7, 0xd1, 0x1e, 0x69, 0x2e, 0x57, 0x27, 0x94, - 0x33, 0x90, 0xd1, 0x67, 0x21, 0xce, 0xab, 0xa2, 0x6d, 0xac, 0x11, 0xcb, 0x4c, 0x5f, 0xe3, 0xb6, - 0x91, 0xd1, 0x14, 0x46, 0x42, 0xdf, 0x80, 0xac, 0x8d, 0x1d, 0xdb, 0x60, 0x6e, 0x16, 0x7e, 0x8c, - 0x1b, 0x5d, 0xe6, 0x98, 0xed, 0x6b, 0x46, 0xab, 0x6b, 0x63, 0xb5, 0x65, 0x35, 0xd3, 0x8b, 0x4c, - 0xfd, 0x8e, 0x8e, 0x91, 0x04, 0x42, 0xc5, 0x05, 0x58, 0xe6, 0xe3, 0x95, 0x6b, 0xf6, 0x59, 0x4d, - 0x1b, 0x56, 0xb3, 0x18, 0x85, 0xb0, 0xb8, 0xd1, 0xf6, 0x2a, 0x56, 0x78, 0xad, 0x0a, 0xaf, 0x52, - 0xc9, 0xc8, 0x9f, 0xc9, 0xfd, 0x3c, 0x06, 0x11, 0xcf, 0xaf, 0x58, 0x02, 0xe4, 0xb9, 0x8e, 0xbd, - 0x2f, 0x1c, 0xa8, 0xc5, 0x0f, 0xac, 0x4e, 0x28, 0x29, 0xb7, 0xad, 0xf7, 0x91, 0xc3, 0xdd, 0xbe, - 0x1a, 0xc5, 0x71, 0x3e, 0xdc, 0xa5, 0xb2, 0xe3, 0x15, 0x31, 0x52, 0x4b, 0x2c, 0x8a, 0xe0, 0x3d, - 0x4b, 0xcc, 0x6f, 0x1e, 0x92, 0x2e, 0x59, 0x58, 0xe2, 0x1b, 0x90, 0xb4, 0xbb, 0x26, 0xbb, 0x86, - 0x16, 0xf9, 0x1e, 0xee, 0x2f, 0x27, 0x04, 0x55, 0xa4, 0x6c, 0x4a, 0x03, 0xc6, 0xe1, 0xd6, 0xb9, - 0xc6, 0xc1, 0xdd, 0xfb, 0xaa, 0xe4, 0x59, 0x87, 0xe5, 0x41, 0xeb, 0xf0, 0xda, 0xf9, 0xd6, 0xc1, - 0x07, 0xe3, 0x99, 0x87, 0xbd, 0xa1, 0xe6, 0x61, 0x69, 0x4c, 0xfd, 0xe6, 0x43, 0xec, 0xb7, 0x0f, - 0xa5, 0x01, 0xfb, 0x70, 0xeb, 0x5c, 0xfb, 0xe0, 0xdf, 0xa3, 0x30, 0x10, 0xdb, 0x43, 0x0c, 0xc4, - 0xeb, 0x17, 0x0a, 0x45, 0x57, 0xa5, 0x3e, 0x0b, 0xa1, 0x0c, 0xb3, 0x10, 0xf9, 0xf1, 0x2c, 0x84, - 0x0f, 0xb2, 0xcf, 0x44, 0x7c, 0x72, 0xca, 0x44, 0xc8, 0xe7, 0xeb, 0xd8, 0xa1, 0x49, 0xbe, 0x55, - 0xe9, 0x94, 0x8d, 0xd0, 0x86, 0xd8, 0x88, 0x14, 0x83, 0x7f, 0xf3, 0x02, 0x36, 0xc2, 0x37, 0xc1, - 0x69, 0x23, 0xf1, 0x21, 0xc4, 0xfd, 0x8a, 0x9d, 0x55, 0xb0, 0x8d, 0x36, 0x41, 0x67, 0x7c, 0xf4, - 0xcc, 0x64, 0xc0, 0xd7, 0x84, 0xbe, 0x76, 0xda, 0x3e, 0xcc, 0x9c, 0x0b, 0x7e, 0x46, 0x79, 0xc4, - 0xaa, 0x74, 0xda, 0x40, 0x6c, 0xf8, 0x0d, 0xc4, 0xec, 0xb9, 0xee, 0xc3, 0xa9, 0x08, 0x65, 0x55, - 0xf2, 0x5b, 0x88, 0xef, 0x4a, 0x70, 0x6d, 0x94, 0x8a, 0x17, 0xb6, 0xf9, 0xbd, 0x4b, 0x5a, 0x08, - 0xdf, 0xa4, 0x23, 0xa7, 0x41, 0xe4, 0x4c, 0x13, 0x31, 0x7f, 0x6e, 0xf9, 0xd4, 0xe8, 0x80, 0x69, - 0x55, 0x3a, 0xd3, 0x46, 0x3c, 0x80, 0x88, 0x63, 0x6b, 0x0d, 0x76, 0x1f, 0x77, 0x85, 0x5d, 0xba, - 0xde, 0x17, 0x79, 0x98, 0xd2, 0xf8, 0x79, 0x18, 0x8a, 0x60, 0x98, 0x4d, 0xf7, 0x5f, 0x2a, 0x88, - 0x14, 0x93, 0xe5, 0x65, 0xc2, 0xe2, 0xa7, 0x12, 0x66, 0xf3, 0x54, 0xf5, 0x22, 0x40, 0xc4, 0xad, - 0x97, 0xf2, 0x99, 0x80, 0xdc, 0xf7, 0x24, 0x98, 0x5c, 0xb3, 0xea, 0xe8, 0x25, 0x5f, 0xb6, 0x3f, - 0x21, 0xd6, 0x32, 0xb5, 0x66, 0xd5, 0x45, 0xda, 0xfe, 0xbd, 0xde, 0x68, 0x91, 0x3c, 0x79, 0x79, - 0x04, 0x5f, 0xbc, 0xcb, 0x12, 0x6f, 0x10, 0xfa, 0x2a, 0x84, 0x3b, 0x3c, 0x76, 0x14, 0x16, 0x21, - 0x37, 0x6a, 0x3c, 0xef, 0xa9, 0xb8, 0x43, 0x72, 0xff, 0x13, 0x80, 0xab, 0x67, 0xda, 0x3b, 0x34, - 0xd7, 0x97, 0xef, 0x8f, 0xba, 0x59, 0x7b, 0xf4, 0x45, 0x98, 0xeb, 0x19, 0x57, 0x5e, 0x33, 0xd8, - 0x67, 0x4f, 0x66, 0xbd, 0x56, 0x56, 0x36, 0x28, 0xac, 0xca, 0x1b, 0xd0, 0xa3, 0xab, 0xd8, 0x1c, - 0x88, 0x1a, 0x91, 0xd7, 0x56, 0x31, 0x5d, 0x3b, 0x64, 0x42, 0xcc, 0x30, 0x89, 0x43, 0xc3, 0x11, - 0xf7, 0x82, 0x75, 0xaa, 0xb8, 0x29, 0x98, 0xf8, 0xe5, 0xb1, 0x5e, 0x28, 0x2f, 0xe1, 0xbc, 0xb7, - 0x51, 0x15, 0x38, 0xec, 0x25, 0x42, 0xef, 0x49, 0x01, 0x77, 0x86, 0xaa, 0x8e, 0xde, 0x72, 0x23, - 0xc1, 0xa9, 0x31, 0xc3, 0x2a, 0x11, 0x2b, 0xbe, 0x0a, 0xd3, 0x8e, 0xdd, 0x35, 0xf9, 0x07, 0xea, - 0x1c, 0x81, 0xa5, 0x77, 0x94, 0xa4, 0x47, 0x66, 0xfd, 0x5f, 0xbb, 0xe5, 0xff, 0xcf, 0x59, 0x36, - 0x2d, 0x1d, 0xa3, 0x24, 0xc0, 0x8e, 0x46, 0x48, 0xe7, 0xc0, 0xd6, 0x08, 0x96, 0x27, 0x50, 0x18, - 0x26, 0xd7, 0x37, 0x6b, 0xb2, 0xf4, 0xda, 0x87, 0xfe, 0x9b, 0x91, 0xb2, 0x52, 0xa8, 0x6e, 0x55, - 0xb7, 0x56, 0xd4, 0xad, 0xc2, 0x66, 0xa5, 0x26, 0x4f, 0xa0, 0x34, 0xcc, 0x7e, 0x50, 0xa8, 0xee, - 0x8a, 0xab, 0x12, 0xb5, 0xba, 0xb5, 0x5b, 0x51, 0xee, 0x17, 0x36, 0x64, 0x09, 0xcd, 0x01, 0x52, - 0xb6, 0x4b, 0xeb, 0xb5, 0x72, 0x51, 0x2d, 0x6d, 0x6f, 0xee, 0x14, 0x4a, 0xbb, 0xd5, 0xed, 0x2d, - 0x39, 0x80, 0x22, 0x10, 0x2c, 0x6f, 0x6f, 0x55, 0x64, 0x78, 0xed, 0x4f, 0xa6, 0x44, 0xe9, 0xea, - 0x75, 0x88, 0xed, 0x6d, 0xd5, 0x76, 0x2a, 0xa5, 0xea, 0x72, 0xb5, 0x52, 0x96, 0x27, 0x32, 0x33, - 0x4f, 0x9e, 0x2e, 0x4e, 0xd3, 0xa6, 0x3d, 0x93, 0x74, 0x70, 0x83, 0xb9, 0x04, 0x28, 0x03, 0xa1, - 0x62, 0xa1, 0xb4, 0xbe, 0xb7, 0x23, 0x4b, 0x99, 0xe4, 0x93, 0xa7, 0x8b, 0xc0, 0xbe, 0x10, 0xe0, - 0xc6, 0xfb, 0x1a, 0x4f, 0xfa, 0x6e, 0x2b, 0x15, 0x39, 0x90, 0x99, 0x7e, 0xf2, 0x74, 0x31, 0xc6, - 0x72, 0xc9, 0xc2, 0x24, 0xbf, 0x0a, 0x89, 0x5a, 0x69, 0xb5, 0xb2, 0x59, 0x50, 0x4b, 0xab, 0x85, - 0xad, 0x95, 0x8a, 0x3c, 0x99, 0x99, 0x7d, 0xf2, 0x74, 0x51, 0x1e, 0x54, 0xeb, 0x74, 0x8a, 0xea, - 0xe6, 0xce, 0xb6, 0xb2, 0x2b, 0x07, 0x7b, 0x53, 0x70, 0x6b, 0x8a, 0x72, 0x00, 0x7c, 0xf4, 0x72, - 0xa5, 0x52, 0x96, 0xa7, 0x32, 0xe8, 0xc9, 0xd3, 0xc5, 0x24, 0x6d, 0xef, 0x19, 0x49, 0x74, 0x03, - 0xe2, 0x25, 0xa5, 0x52, 0xd8, 0xad, 0xa8, 0xb5, 0xdd, 0xc2, 0x6e, 0x4d, 0x0e, 0xf5, 0x76, 0xe2, - 0x33, 0x7c, 0x28, 0x0f, 0xa9, 0xc2, 0xde, 0xee, 0xb6, 0xda, 0xd7, 0x37, 0x9c, 0x99, 0x7f, 0xf2, - 0x74, 0x71, 0x86, 0xf6, 0xa5, 0x5a, 0xc7, 0xdf, 0xff, 0xf3, 0x20, 0xf7, 0xad, 0x5f, 0x5d, 0x29, - 0xc9, 0x91, 0xcc, 0xdc, 0x93, 0xa7, 0x8b, 0x68, 0x70, 0x0b, 0x2b, 0x25, 0x7a, 0x28, 0x76, 0x3f, - 0xda, 0xa9, 0x94, 0x2b, 0xb5, 0x92, 0xda, 0xbf, 0xed, 0x68, 0x26, 0xfd, 0xe4, 0xe9, 0xe2, 0x2c, - 0x1d, 0x73, 0x6a, 0xeb, 0xaf, 0x83, 0x5c, 0xdb, 0x55, 0x2a, 0x85, 0x4d, 0xb5, 0xba, 0xb5, 0x52, - 0xa9, 0xb1, 0x97, 0x05, 0xbd, 0x25, 0x0d, 0x98, 0x28, 0xba, 0x85, 0xad, 0xca, 0x07, 0x03, 0xf8, - 0xb1, 0x5e, 0xff, 0x01, 0xab, 0x83, 0x16, 0x21, 0xba, 0x59, 0x5d, 0x51, 0x0a, 0x0c, 0x37, 0x9e, - 0x49, 0x3d, 0x79, 0xba, 0x98, 0xa0, 0xfd, 0x3c, 0x1b, 0x82, 0xaa, 0x90, 0x65, 0x4c, 0xa9, 0xed, - 0x14, 0xb6, 0xd4, 0xd2, 0xf6, 0xd6, 0x72, 0x75, 0x45, 0x55, 0x2a, 0xa5, 0xed, 0xad, 0x52, 0x75, - 0xa3, 0xca, 0xc7, 0x25, 0x32, 0xd7, 0x9f, 0x3c, 0x5d, 0x5c, 0x74, 0x59, 0x74, 0xa6, 0xc6, 0x7f, - 0x17, 0xae, 0x72, 0xa8, 0x7b, 0x1b, 0x9c, 0xb9, 0x7e, 0x09, 0x4c, 0x66, 0x16, 0x9e, 0x3c, 0x5d, - 0xcc, 0x78, 0x20, 0xa7, 0x74, 0x77, 0x26, 0xf2, 0x5b, 0xdf, 0x5b, 0x98, 0xf8, 0xf3, 0xef, 0x2f, - 0x4c, 0x14, 0x6f, 0xfe, 0xf8, 0xbf, 0x16, 0x26, 0x7e, 0x7c, 0xb2, 0x20, 0xfd, 0xe4, 0x64, 0x41, - 0xfa, 0xe9, 0xc9, 0x82, 0xf4, 0x9f, 0x27, 0x0b, 0xd2, 0xef, 0xfc, 0x6c, 0x61, 0xe2, 0x27, 0x3f, - 0x5b, 0x98, 0xf8, 0xe9, 0xcf, 0x16, 0x26, 0x3e, 0x0e, 0x71, 0x85, 0x56, 0x0f, 0xb1, 0x1c, 0xd7, - 0x9b, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x93, 0xfa, 0x2b, 0x71, 0x69, 0x4b, 0x00, 0x00, + // 6118 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x7c, 0x5b, 0x6c, 0x1b, 0xd9, + 0x7d, 0xb7, 0x86, 0xa2, 0x78, 0xf9, 0x8b, 0xa4, 0x86, 0x47, 0xb2, 0x44, 0x33, 0x5e, 0x91, 0xcb, + 0xb5, 0x77, 0xed, 0x4d, 0x4c, 0xed, 0x7a, 0x93, 0xcd, 0xae, 0x93, 0xf5, 0x2e, 0x6f, 0x92, 0x28, + 0xeb, 0xe6, 0xa1, 0xe4, 0xbd, 0x7d, 0x9b, 0xf9, 0x86, 0x9c, 0x23, 0x6a, 0x3e, 0x91, 0x33, 0xf4, + 0x9c, 0xa1, 0x6d, 0x25, 0x40, 0x10, 0x24, 0x5f, 0x80, 0xc2, 0xe8, 0x43, 0x0a, 0xb4, 0x7d, 0x69, + 0x5d, 0x14, 0x4d, 0x02, 0xf4, 0xa1, 0x41, 0xd1, 0xa0, 0x68, 0xfb, 0xd8, 0xc7, 0x3c, 0xa4, 0x45, + 0x90, 0xa2, 0x68, 0xda, 0x07, 0xa5, 0x55, 0x5e, 0xfa, 0xd0, 0x87, 0xa2, 0x8f, 0x7e, 0x2a, 0xce, + 0x65, 0x86, 0x43, 0x8a, 0xa2, 0x28, 0xd9, 0x49, 0x5e, 0x64, 0xce, 0xff, 0x9c, 0xf3, 0x3b, 0x97, + 0xf9, 0x9f, 0xff, 0x7d, 0x0c, 0xf3, 0xff, 0xcf, 0xaa, 0x93, 0x25, 0xfa, 0xa7, 0x53, 0x67, 0xff, + 0xe4, 0x3b, 0xb6, 0xe5, 0x58, 0xe8, 0x72, 0xc3, 0x6a, 0x1c, 0xd8, 0x96, 0xd6, 0xd8, 0xcf, 0x93, + 0x07, 0xad, 0x3c, 0x6b, 0xe1, 0xbd, 0xd2, 0x97, 0xb0, 0x6d, 0x5b, 0x36, 0xed, 0xcf, 0x7f, 0xf0, + 0x11, 0xe9, 0xb9, 0xa6, 0xd5, 0xb4, 0xd8, 0xcf, 0x25, 0xfa, 0x4b, 0x50, 0x93, 0x0c, 0xa3, 0x53, + 0x5f, 0xd2, 0x3a, 0x86, 0x20, 0x21, 0x97, 0xa4, 0x6b, 0x8e, 0x26, 0x68, 0x29, 0x97, 0x66, 0x58, + 0x37, 0xf7, 0x2c, 0xbb, 0xad, 0x39, 0x2e, 0xec, 0x2b, 0xe4, 0x41, 0x6b, 0xa9, 0xa1, 0x39, 0x5a, + 0xcb, 0x6a, 0x2e, 0xe9, 0x98, 0x34, 0x3a, 0xf5, 0x25, 0xe2, 0xd8, 0xdd, 0x86, 0xd3, 0xb5, 0xb1, + 0x2e, 0x3a, 0x65, 0x86, 0x74, 0x72, 0xb0, 0xa9, 0x99, 0x8e, 0x8b, 0xdf, 0x75, 0x8c, 0xd6, 0xd2, + 0x7e, 0xab, 0xb1, 0xe4, 0x18, 0x6d, 0x4c, 0x1c, 0xad, 0xdd, 0x11, 0x2d, 0x2f, 0xd3, 0xa1, 0xa4, + 0xb1, 0x8f, 0xdb, 0x5a, 0x63, 0x5f, 0x33, 0x9b, 0xd8, 0x5e, 0xe2, 0x73, 0x34, 0x3a, 0x75, 0xd1, + 0xe5, 0x6a, 0xa3, 0xd5, 0x25, 0x0e, 0xb6, 0x1f, 0x62, 0x9b, 0x18, 0x96, 0xb9, 0x24, 0x1e, 0x55, + 0xf1, 0xec, 0xae, 0xa1, 0x69, 0x59, 0xcd, 0x16, 0x5e, 0x62, 0x4f, 0xf5, 0xee, 0xde, 0xe0, 0x4c, + 0xb9, 0x1f, 0x06, 0x60, 0xa1, 0xa8, 0x35, 0x0e, 0xba, 0x9d, 0x8a, 0xd9, 0xb0, 0x0f, 0x3b, 0x8e, + 0x61, 0x99, 0x5b, 0xec, 0x2f, 0x41, 0x32, 0x4c, 0x1e, 0xe0, 0xc3, 0x94, 0x94, 0x95, 0xae, 0xc7, + 0x14, 0xfa, 0x13, 0xbd, 0x07, 0xc1, 0xb6, 0xa5, 0xe3, 0x54, 0x20, 0x2b, 0x5d, 0x4f, 0xdc, 0xba, + 0x91, 0x3f, 0xf5, 0x7d, 0xe4, 0x7b, 0x68, 0x1b, 0x96, 0x8e, 0x15, 0x36, 0x0c, 0xd5, 0x21, 0x72, + 0xd0, 0x26, 0xaa, 0x61, 0xee, 0x59, 0xa9, 0xc9, 0xac, 0x74, 0x7d, 0xfa, 0xd6, 0xed, 0x11, 0x10, + 0xa7, 0x2c, 0x2b, 0x7f, 0x77, 0xa3, 0x56, 0x35, 0xf7, 0xac, 0xe2, 0xf4, 0xf1, 0x51, 0x26, 0x2c, + 0x1e, 0x94, 0xf0, 0x41, 0x9b, 0xd0, 0x1f, 0xe9, 0x2d, 0x70, 0x69, 0x74, 0xfd, 0x5d, 0xdb, 0x60, + 0xeb, 0x8f, 0x2a, 0xf4, 0x27, 0xfa, 0x02, 0x20, 0xcc, 0xf1, 0xb0, 0xae, 0xd2, 0x37, 0xad, 0xd2, + 0x0d, 0x06, 0xd8, 0x06, 0x65, 0xaf, 0xa5, 0xac, 0x39, 0xda, 0x5d, 0x7c, 0x78, 0x3b, 0xf8, 0x9f, + 0x7f, 0x9a, 0x91, 0xf8, 0xdf, 0xdc, 0xb7, 0x26, 0x21, 0xd1, 0x5b, 0x0a, 0x83, 0x5f, 0x85, 0x10, + 0x7b, 0x45, 0x98, 0xcd, 0x90, 0xb8, 0xf5, 0xc6, 0x58, 0xc7, 0x41, 0x87, 0xe6, 0x6b, 0x6c, 0x9c, + 0x22, 0xc6, 0x23, 0x04, 0x41, 0xa2, 0xb5, 0x1c, 0xb1, 0x10, 0xf6, 0x1b, 0xfd, 0x91, 0x04, 0xd9, + 0xc1, 0x15, 0x15, 0x0f, 0xef, 0x6e, 0xd4, 0x36, 0x34, 0xfa, 0x9e, 0xef, 0xe2, 0xc3, 0x6a, 0x39, + 0x35, 0x99, 0x9d, 0xbc, 0x3e, 0x7d, 0x6b, 0x6b, 0xfc, 0x89, 0x2b, 0x67, 0x20, 0x56, 0x4c, 0xc7, + 0x3e, 0x54, 0xce, 0x9c, 0x38, 0x5d, 0x83, 0x6b, 0x63, 0x41, 0xf9, 0x79, 0x28, 0xca, 0x79, 0x68, + 0x0e, 0xa6, 0x1e, 0x6a, 0xad, 0x2e, 0x16, 0xbb, 0xe5, 0x0f, 0xb7, 0x03, 0xef, 0x48, 0xb9, 0x05, + 0x08, 0xf1, 0x83, 0x41, 0x71, 0x88, 0x16, 0x2a, 0xb5, 0x5b, 0x5f, 0x7a, 0x7b, 0xa5, 0xb4, 0x21, + 0x4f, 0x88, 0x57, 0xf0, 0xbb, 0x01, 0x98, 0xaf, 0x39, 0x36, 0xd6, 0xda, 0x55, 0xb3, 0x89, 0x09, + 0xdd, 0x53, 0x19, 0x3b, 0x9a, 0xd1, 0x22, 0xe8, 0x1a, 0x24, 0x08, 0x6b, 0x51, 0x35, 0x5d, 0xb7, + 0x31, 0x21, 0x62, 0xc2, 0x38, 0xa7, 0x16, 0x38, 0x11, 0xdd, 0x80, 0xa8, 0xe8, 0x66, 0xe8, 0xa9, + 0x60, 0x56, 0xba, 0x1e, 0x2c, 0xc6, 0x8e, 0x8f, 0x32, 0x11, 0x81, 0x5a, 0x56, 0x22, 0xbc, 0xb9, + 0xaa, 0xa3, 0x37, 0x21, 0x48, 0x3a, 0x9a, 0xc9, 0x16, 0x39, 0x7d, 0x6b, 0xc1, 0x77, 0xc2, 0x42, + 0x28, 0xe4, 0x6b, 0x1d, 0xcd, 0x2c, 0x06, 0x7f, 0x72, 0x94, 0x99, 0x50, 0x58, 0x57, 0x54, 0x04, + 0x20, 0x8e, 0x66, 0x3b, 0x2a, 0xbd, 0x63, 0x82, 0xbf, 0x5f, 0xf2, 0x0d, 0xa4, 0xb7, 0x3d, 0xbf, + 0xdf, 0x6a, 0xe4, 0x77, 0xdc, 0x3b, 0x28, 0x86, 0x47, 0xd9, 0x30, 0x4a, 0xa5, 0x2b, 0xe4, 0x22, + 0x82, 0xae, 0x70, 0xaa, 0xb7, 0xc2, 0x1d, 0x46, 0xa4, 0x2b, 0xe4, 0xcd, 0x55, 0x3d, 0xf7, 0x2f, + 0x93, 0xb0, 0x30, 0x70, 0x1c, 0xdb, 0xb6, 0xd5, 0x64, 0x1b, 0x5d, 0x86, 0x58, 0xa3, 0xeb, 0x58, + 0x0f, 0xb1, 0xcd, 0x17, 0x23, 0x8d, 0xbf, 0x98, 0x69, 0x31, 0x90, 0x2d, 0xe7, 0x9b, 0x80, 0x3a, + 0x9a, 0xed, 0x18, 0x14, 0x5c, 0xed, 0x08, 0xf4, 0x54, 0x80, 0x71, 0x5d, 0x75, 0x04, 0xd7, 0x9d, + 0xb2, 0xae, 0xfc, 0xb6, 0x0b, 0xe6, 0x52, 0x18, 0x93, 0x88, 0x99, 0x93, 0x9d, 0xc1, 0xd6, 0x74, + 0x13, 0x92, 0x27, 0x86, 0x20, 0x05, 0x90, 0xc1, 0x90, 0xb1, 0xae, 0x7a, 0xe2, 0xec, 0x3c, 0x5b, + 0x4c, 0xba, 0xc3, 0xbd, 0x86, 0xf4, 0x13, 0x09, 0xe6, 0x87, 0x2f, 0x6e, 0x08, 0x07, 0x7f, 0xe2, + 0xe7, 0xe0, 0xe9, 0x5b, 0xe5, 0x17, 0x71, 0x10, 0xfe, 0x7b, 0x50, 0x85, 0x14, 0x1f, 0xa7, 0xe0, + 0x4e, 0xcb, 0x68, 0x68, 0x7e, 0x4e, 0xbf, 0x09, 0x53, 0x94, 0xd9, 0x28, 0x83, 0x4f, 0x8e, 0x60, + 0x4c, 0x85, 0xf7, 0xca, 0x69, 0x70, 0xf9, 0x04, 0x94, 0x77, 0x90, 0x65, 0x00, 0xfc, 0xb8, 0x63, + 0xd8, 0x8c, 0x2a, 0x0e, 0x30, 0x9d, 0xe7, 0x1a, 0x23, 0xef, 0x6a, 0x0c, 0xdf, 0xe9, 0x45, 0xe8, + 0xe9, 0x7d, 0xef, 0x97, 0x19, 0x49, 0xf1, 0x8d, 0xcb, 0xfd, 0x28, 0x00, 0x97, 0xe9, 0xb5, 0xd5, + 0xbb, 0x2d, 0xbc, 0xbd, 0x53, 0x2b, 0xed, 0x6b, 0x86, 0x69, 0x98, 0x4d, 0x05, 0x37, 0x2c, 0x5b, + 0x47, 0xbf, 0x27, 0x41, 0x9a, 0x42, 0xe1, 0x46, 0xdf, 0xeb, 0x52, 0x6d, 0xd6, 0xcc, 0x75, 0x4b, + 0xb1, 0xf6, 0x6f, 0x47, 0x99, 0xb7, 0x9a, 0x86, 0xb3, 0xdf, 0xad, 0xe7, 0x1b, 0x56, 0x7b, 0xc9, + 0xdb, 0x93, 0x5e, 0xef, 0xfd, 0x5e, 0xea, 0x1c, 0x34, 0x97, 0x98, 0xc6, 0xec, 0x76, 0x0d, 0x3d, + 0xbf, 0xbb, 0x5b, 0x2d, 0x1f, 0x1f, 0x65, 0x52, 0xdb, 0x2e, 0xb8, 0xb7, 0x4e, 0x3e, 0xb3, 0x92, + 0xea, 0x9c, 0xd2, 0x82, 0xee, 0x43, 0x48, 0x6b, 0xb0, 0x3d, 0x73, 0x3d, 0x76, 0x67, 0xd4, 0x0b, + 0x3c, 0x6d, 0x67, 0xf9, 0xed, 0x9d, 0x5a, 0x81, 0xa1, 0x28, 0x02, 0x2d, 0x77, 0x15, 0xa2, 0x1e, + 0x11, 0x01, 0x84, 0x76, 0xb7, 0xcb, 0x85, 0x9d, 0x8a, 0x3c, 0x81, 0xa6, 0x21, 0xac, 0x54, 0xd6, + 0x2b, 0x85, 0x5a, 0x45, 0x96, 0x72, 0xff, 0x14, 0x86, 0x38, 0x57, 0x6d, 0xee, 0x3b, 0xed, 0x17, + 0x1c, 0xd2, 0x85, 0x04, 0xc7, 0x1d, 0x88, 0x60, 0x93, 0x1f, 0xb0, 0x60, 0xcb, 0xb1, 0x10, 0xc2, + 0xd8, 0x64, 0xc7, 0x83, 0x2e, 0x73, 0x5d, 0x49, 0xa5, 0x56, 0xb4, 0x18, 0x3e, 0x3e, 0xca, 0x4c, + 0xee, 0x2a, 0x55, 0xae, 0x34, 0xbf, 0x23, 0xc1, 0x6c, 0xd7, 0x36, 0x88, 0x5a, 0x3f, 0x54, 0x5b, + 0x56, 0x43, 0x6b, 0x19, 0xce, 0xa1, 0x7a, 0xf0, 0x30, 0x35, 0xc5, 0x38, 0xf0, 0xce, 0x99, 0x1a, + 0x5c, 0x6c, 0x33, 0xbf, 0x6b, 0x1b, 0xa4, 0x78, 0xb8, 0x2e, 0x10, 0xee, 0x3e, 0xe4, 0x77, 0x7f, + 0xee, 0xf8, 0x28, 0x23, 0xef, 0x2a, 0x55, 0x7f, 0xd3, 0x7d, 0x45, 0xee, 0x0e, 0x74, 0x46, 0x5f, + 0x85, 0xb4, 0x8e, 0x3b, 0x36, 0x6e, 0x68, 0x94, 0x91, 0xea, 0x0c, 0x59, 0x6d, 0x6b, 0xa6, 0xb1, + 0x87, 0x89, 0xc3, 0x84, 0x79, 0x4c, 0x49, 0xf5, 0x7a, 0xf0, 0xa9, 0x37, 0x44, 0x3b, 0xd2, 0x3c, + 0xc5, 0x4f, 0x25, 0x99, 0xc5, 0x2d, 0x89, 0x54, 0x88, 0x1d, 0xd4, 0xad, 0xf3, 0xdb, 0x20, 0x4a, + 0x12, 0x9f, 0xb0, 0x96, 0x14, 0x98, 0xf1, 0x4d, 0xc1, 0x6c, 0x9c, 0x28, 0xc3, 0xbf, 0x31, 0xb6, + 0x7a, 0x56, 0x12, 0xb8, 0xdf, 0xc4, 0x38, 0xe3, 0xf6, 0x84, 0x7f, 0x1b, 0xb7, 0xe7, 0x1d, 0x48, + 0x34, 0xac, 0x56, 0x0b, 0x33, 0x36, 0x57, 0x77, 0x95, 0x6a, 0x2a, 0xc2, 0x98, 0x26, 0x79, 0x7c, + 0x94, 0x89, 0x97, 0xbc, 0x16, 0xca, 0x3e, 0xf1, 0x86, 0xff, 0x11, 0xfd, 0xbe, 0x04, 0x57, 0x88, + 0xb8, 0x4f, 0x6a, 0xc7, 0x21, 0x6a, 0x43, 0xdc, 0x28, 0x77, 0x3f, 0xc0, 0xce, 0xeb, 0x8b, 0x17, + 0xb9, 0x8e, 0xc5, 0x97, 0x8e, 0x8f, 0x32, 0xa7, 0xcb, 0x21, 0xe5, 0xb2, 0x3b, 0xf1, 0xb6, 0x43, + 0xfa, 0x9b, 0xd2, 0x25, 0xb8, 0x34, 0x94, 0x35, 0xcf, 0xb2, 0x5d, 0xa2, 0x7e, 0x99, 0x2d, 0x43, + 0x82, 0xf3, 0x8a, 0x2b, 0x5d, 0x73, 0x7f, 0xb2, 0x00, 0x09, 0x05, 0x13, 0xc7, 0xb2, 0xb1, 0x7b, + 0xd1, 0xfd, 0x97, 0x34, 0x78, 0x81, 0x4b, 0xfa, 0x63, 0x09, 0x66, 0xa9, 0x23, 0x61, 0x1b, 0x1d, + 0xc7, 0xb2, 0x55, 0x1b, 0x3f, 0xb2, 0x0d, 0x07, 0xbb, 0x0a, 0xb9, 0x30, 0xe2, 0xdc, 0xfa, 0x17, + 0x92, 0x2f, 0x7b, 0x20, 0x8a, 0xc0, 0xe0, 0x97, 0xf1, 0xce, 0xb7, 0x7f, 0x99, 0xb9, 0x3d, 0x16, + 0x2b, 0x9d, 0xf4, 0x6d, 0xf2, 0xd5, 0xb2, 0x82, 0xf4, 0x13, 0xc0, 0xe8, 0x0a, 0x04, 0xe9, 0x65, + 0x66, 0xb6, 0x6a, 0xb4, 0x18, 0x39, 0x3e, 0xca, 0x04, 0xe9, 0x75, 0x57, 0x18, 0x15, 0x39, 0x30, + 0x27, 0xee, 0xb2, 0x27, 0x5a, 0xd8, 0xd5, 0x09, 0xb3, 0x2d, 0x7d, 0x75, 0xfc, 0x2d, 0xf1, 0xd3, + 0x77, 0x5f, 0x21, 0x73, 0x10, 0xf8, 0xe9, 0xa1, 0xfa, 0x89, 0x16, 0xb4, 0x0d, 0x09, 0x6a, 0xfd, + 0xd7, 0x35, 0x82, 0x55, 0xba, 0x64, 0x92, 0x92, 0xd9, 0x7c, 0x83, 0x57, 0x95, 0x3c, 0x68, 0xd1, + 0x3e, 0xf9, 0xb2, 0xe8, 0xec, 0x3b, 0xb7, 0xb8, 0xee, 0xa3, 0x11, 0xb4, 0x02, 0xd3, 0x8e, 0x56, + 0x6f, 0xb9, 0x70, 0x5c, 0x36, 0xbe, 0x7a, 0x0a, 0xdc, 0x0e, 0xed, 0xe9, 0xc3, 0x02, 0xc7, 0x25, + 0x30, 0xa5, 0xec, 0x1c, 0x76, 0x5c, 0x9c, 0x04, 0xc3, 0xb9, 0x76, 0x1a, 0xce, 0x61, 0xc7, 0x0f, + 0x13, 0x75, 0xc4, 0x33, 0x41, 0x6b, 0x10, 0xe3, 0xee, 0xa3, 0xc0, 0x99, 0x61, 0x38, 0xaf, 0x9d, + 0x82, 0xc3, 0xac, 0x6e, 0xcd, 0x87, 0x34, 0x4d, 0x3c, 0x0a, 0xc5, 0x0a, 0x73, 0xa3, 0x93, 0xa4, + 0x2e, 0x31, 0x98, 0xd7, 0x4f, 0x5b, 0x0e, 0x37, 0x4d, 0xcd, 0x3d, 0xeb, 0x43, 0xc3, 0xd9, 0xdf, + 0x25, 0x5a, 0x13, 0xbb, 0x1c, 0x2c, 0x00, 0xd0, 0x12, 0x4c, 0x53, 0xe3, 0xd2, 0x36, 0x74, 0xac, + 0xea, 0x75, 0x26, 0x80, 0xa3, 0xc5, 0xc4, 0xf1, 0x51, 0x06, 0xb6, 0x04, 0xb9, 0x5c, 0x54, 0xc0, + 0xed, 0x52, 0xae, 0xa3, 0xcf, 0x43, 0xb2, 0x63, 0xe3, 0x8e, 0x66, 0x63, 0xb5, 0x61, 0xb5, 0x3b, + 0x2d, 0xec, 0x60, 0x9d, 0x09, 0x9c, 0x88, 0x22, 0x8b, 0x86, 0x92, 0x4b, 0xe7, 0x6e, 0x80, 0xe6, + 0x50, 0x0f, 0x93, 0x60, 0x9b, 0xf6, 0x8c, 0xb2, 0x9e, 0x71, 0x46, 0xad, 0x0a, 0x22, 0x3a, 0x84, + 0x79, 0x72, 0x48, 0x1c, 0xdc, 0x56, 0xd9, 0xb9, 0x13, 0xb5, 0x6d, 0x34, 0x6d, 0xaa, 0x34, 0x52, + 0x49, 0xb6, 0xbf, 0xd2, 0xf8, 0x5c, 0x57, 0x63, 0x38, 0xec, 0x7d, 0x92, 0x0d, 0x81, 0xc2, 0x7d, + 0xa8, 0x39, 0x32, 0xa4, 0x09, 0xbd, 0x05, 0x97, 0x7a, 0x57, 0x84, 0xa8, 0x9d, 0x6e, 0xbd, 0x65, + 0x90, 0x7d, 0xcc, 0x45, 0x5f, 0x44, 0x99, 0xf3, 0x35, 0x6e, 0xbb, 0x6d, 0xe8, 0xb0, 0xef, 0xd6, + 0x37, 0xe8, 0xe9, 0x68, 0x4d, 0x9c, 0x9a, 0xce, 0x4a, 0xd7, 0xa7, 0x8a, 0xab, 0xcf, 0x8e, 0x32, + 0xe5, 0xb1, 0xaf, 0x2c, 0xc1, 0xed, 0x25, 0xc7, 0xc6, 0xd8, 0x27, 0x01, 0x4a, 0x02, 0xcf, 0x7f, + 0x79, 0x5d, 0x1a, 0x52, 0x00, 0x7a, 0x2a, 0x29, 0x15, 0xbb, 0xb0, 0xbe, 0xf4, 0xa1, 0x20, 0x13, + 0x90, 0x8d, 0x1f, 0x6a, 0x2d, 0x43, 0xd7, 0x1c, 0xac, 0x1a, 0xa6, 0x8e, 0x1f, 0x63, 0x92, 0x42, + 0xec, 0xe8, 0xdf, 0x1d, 0xff, 0xe8, 0x15, 0x0f, 0xa3, 0x4a, 0x21, 0x5c, 0xdb, 0xde, 0xee, 0x27, + 0x63, 0x82, 0xfe, 0x52, 0x02, 0xe4, 0xdd, 0xf6, 0xb6, 0xa5, 0x1b, 0x7b, 0x06, 0xb6, 0x49, 0x6a, + 0x96, 0x4d, 0xf8, 0xc1, 0x39, 0x84, 0xa6, 0xc0, 0xd8, 0x70, 0x21, 0x5e, 0x8c, 0xcc, 0x4c, 0xea, + 0x83, 0xb8, 0xe8, 0x2a, 0x24, 0x74, 0x5c, 0xef, 0x36, 0xd5, 0x8e, 0xd6, 0x25, 0x58, 0xb5, 0xcc, + 0xd4, 0x1c, 0xd3, 0x37, 0x31, 0x46, 0xdd, 0xa6, 0xc4, 0x2d, 0x33, 0xfd, 0x67, 0x01, 0x48, 0x9e, + 0x10, 0xe4, 0x68, 0x07, 0x02, 0x06, 0xb7, 0xab, 0xe3, 0x45, 0xaa, 0xe2, 0x03, 0xd5, 0xf2, 0xb3, + 0xa3, 0xe7, 0x5a, 0x60, 0xc0, 0xd0, 0x51, 0x13, 0xa2, 0xf4, 0xaa, 0x71, 0xbf, 0x34, 0xc0, 0xc0, + 0xd7, 0xa8, 0x5f, 0xba, 0xcd, 0x88, 0xcf, 0x3d, 0x45, 0x84, 0x83, 0x57, 0x75, 0x94, 0x81, 0x69, + 0xc7, 0x52, 0xf1, 0x63, 0x83, 0x38, 0x86, 0xd9, 0x64, 0xf6, 0x68, 0x44, 0x01, 0xc7, 0xaa, 0x08, + 0x0a, 0xba, 0x09, 0xd3, 0x26, 0x7e, 0xa4, 0xea, 0x75, 0xd5, 0xd4, 0x84, 0x1a, 0x8d, 0x16, 0xe3, + 0xc7, 0x47, 0x99, 0xe8, 0x26, 0x7e, 0x54, 0x2e, 0x6e, 0x6a, 0x6d, 0xac, 0x44, 0x4d, 0xfc, 0xa8, + 0x5c, 0xa7, 0x3f, 0xd3, 0x7f, 0x1c, 0x00, 0x74, 0x52, 0x35, 0xa0, 0xbf, 0x93, 0xe0, 0x8a, 0x6b, + 0xd3, 0x5a, 0xb6, 0xd1, 0x34, 0x4c, 0xad, 0xd5, 0x67, 0xdc, 0x72, 0xf7, 0xea, 0x93, 0xe7, 0xd1, + 0x3f, 0xc2, 0xe0, 0xdd, 0x12, 0xf0, 0x83, 0x86, 0xef, 0x15, 0x6a, 0x7f, 0x71, 0xc3, 0xf7, 0x44, + 0x97, 0xfb, 0x4a, 0xaa, 0x7b, 0xca, 0xe0, 0xf4, 0x5d, 0x78, 0x69, 0x24, 0xf0, 0x79, 0xcc, 0x96, + 0xf4, 0xb7, 0x25, 0x58, 0x38, 0xc5, 0x18, 0xf0, 0xe3, 0xc4, 0x39, 0xce, 0xbd, 0x7e, 0xc7, 0xf7, + 0x2b, 0xcf, 0x61, 0x70, 0xf8, 0x17, 0xb1, 0x02, 0x97, 0x4f, 0x95, 0xa3, 0x67, 0xed, 0x26, 0xe2, + 0x07, 0xfa, 0x57, 0x09, 0x66, 0x06, 0xc4, 0x02, 0xfa, 0xd8, 0x77, 0x1f, 0xaa, 0xc7, 0x47, 0x99, + 0x30, 0x9b, 0xe4, 0x85, 0x5c, 0x8a, 0x83, 0x93, 0x97, 0x62, 0x93, 0xce, 0xc0, 0x26, 0x66, 0x33, + 0xbc, 0x7f, 0xe1, 0x19, 0x38, 0x44, 0xef, 0x62, 0xa4, 0xff, 0x5e, 0x02, 0x79, 0x50, 0x02, 0xa1, + 0x2d, 0x90, 0xf1, 0x63, 0xc7, 0xd6, 0x54, 0x9f, 0xc9, 0x20, 0x9d, 0xc7, 0x64, 0x48, 0xb0, 0xe1, + 0x3b, 0x9e, 0xdd, 0xf0, 0x29, 0xc4, 0x6d, 0xdc, 0xa4, 0x86, 0x7d, 0xc3, 0x32, 0xf7, 0x8c, 0xa6, + 0x78, 0xd3, 0x6f, 0x8f, 0x6d, 0x17, 0xe5, 0x15, 0x36, 0xbc, 0xc4, 0x46, 0x2b, 0x31, 0xdb, 0xf7, + 0x94, 0xfe, 0x96, 0x04, 0xf3, 0xc3, 0x85, 0xe8, 0x10, 0x5e, 0xdb, 0xee, 0xe7, 0xb5, 0xdb, 0x17, + 0x97, 0xd3, 0x3e, 0x0e, 0x59, 0x0b, 0x46, 0x24, 0x39, 0xb0, 0x16, 0x8c, 0xc4, 0xe5, 0x44, 0xee, + 0x0d, 0xca, 0x2c, 0x6c, 0xa4, 0x17, 0x11, 0x79, 0x09, 0x60, 0xdf, 0x68, 0xee, 0xab, 0x8f, 0x34, + 0x07, 0xdb, 0x22, 0xf0, 0x1d, 0xa5, 0x94, 0x0f, 0x29, 0x21, 0xf7, 0xf3, 0x18, 0xc4, 0xab, 0xed, + 0x8e, 0x65, 0x3b, 0xae, 0x45, 0xbf, 0x0e, 0x21, 0x6e, 0x43, 0x88, 0x63, 0xcf, 0x8f, 0x58, 0x66, + 0xdf, 0x48, 0x6e, 0x03, 0x0a, 0xa5, 0x25, 0x30, 0xd0, 0x16, 0x84, 0xb9, 0xe1, 0x45, 0x52, 0x0b, + 0x0c, 0x6e, 0x69, 0x6c, 0x38, 0x6e, 0xc2, 0xb9, 0xe6, 0x96, 0x40, 0x41, 0x55, 0x98, 0xa2, 0x9c, + 0x41, 0x52, 0x69, 0x06, 0x77, 0x73, 0xfc, 0xd5, 0x1d, 0x76, 0xdc, 0xc5, 0x71, 0x04, 0xcf, 0x8c, + 0x0f, 0x0c, 0x35, 0xe3, 0xdf, 0x83, 0x10, 0xcf, 0x90, 0x88, 0xb8, 0x67, 0x66, 0x48, 0x5c, 0xaa, + 0xba, 0xb5, 0x6c, 0xb4, 0xf0, 0x32, 0xeb, 0xe6, 0x6e, 0x9c, 0x0f, 0x42, 0xaf, 0x42, 0x84, 0x10, + 0x47, 0x25, 0xc6, 0xd7, 0xb9, 0x44, 0x9f, 0xe4, 0xc1, 0xfd, 0x5a, 0x6d, 0xa7, 0x66, 0x7c, 0x1d, + 0x2b, 0x61, 0x42, 0x1c, 0xfa, 0x03, 0x2d, 0x02, 0xb3, 0x0d, 0x89, 0x46, 0x2d, 0x3e, 0x66, 0xdc, + 0x4d, 0x2a, 0x3e, 0x0a, 0xc3, 0x39, 0x30, 0x3a, 0xea, 0xde, 0x01, 0xe1, 0x16, 0x95, 0xc0, 0x39, + 0x30, 0x3a, 0xcb, 0x77, 0x89, 0x12, 0xa6, 0x8d, 0xcb, 0x07, 0x04, 0xa5, 0x21, 0xf2, 0x48, 0x6b, + 0xb5, 0x98, 0x23, 0x36, 0xc5, 0x50, 0xbc, 0xe7, 0x7e, 0x55, 0x17, 0xfa, 0xf5, 0xaa, 0x3a, 0xe1, + 0xfa, 0x74, 0x34, 0x67, 0x9f, 0x39, 0xf3, 0x51, 0x05, 0x38, 0x69, 0x5b, 0x73, 0xf6, 0x51, 0x0a, + 0xc2, 0x7c, 0x5f, 0x24, 0x15, 0xc9, 0x4e, 0x5e, 0x8f, 0x29, 0xee, 0x23, 0x7a, 0x0d, 0x66, 0x78, + 0x0c, 0x53, 0xd5, 0x0d, 0x1b, 0x37, 0x9c, 0xd6, 0x21, 0xb3, 0x06, 0x23, 0x4a, 0x82, 0x93, 0xcb, + 0x82, 0x8a, 0x6e, 0x80, 0x3c, 0x68, 0x3e, 0x33, 0x2b, 0x2e, 0xa2, 0xcc, 0x0c, 0x58, 0xcf, 0xd4, + 0xd2, 0x16, 0x6c, 0xe3, 0x33, 0x4b, 0x53, 0xdc, 0xd2, 0x16, 0x0d, 0x3d, 0x93, 0xf4, 0x06, 0xc8, + 0xc2, 0x76, 0xee, 0xf5, 0x8d, 0x73, 0x5c, 0x4e, 0xef, 0x75, 0xcd, 0xc3, 0x6c, 0x47, 0xb3, 0x09, + 0x56, 0xeb, 0x5d, 0x53, 0x6f, 0x61, 0x95, 0x63, 0xa5, 0x12, 0xac, 0x77, 0x92, 0x35, 0x15, 0x59, + 0x0b, 0x67, 0xe1, 0xb3, 0x62, 0x1e, 0xf3, 0xbf, 0x8d, 0x98, 0xc7, 0x75, 0x90, 0x75, 0xbc, 0xa7, + 0x75, 0x5b, 0x8e, 0x6a, 0x98, 0x82, 0x4f, 0x2f, 0x53, 0xf3, 0x5b, 0x49, 0x08, 0x7a, 0xd5, 0xe4, + 0x1c, 0xfa, 0x4d, 0x58, 0xf0, 0x6c, 0xcd, 0x8e, 0x6d, 0xb4, 0x35, 0xfb, 0x50, 0xe5, 0x42, 0x30, + 0xf5, 0x39, 0x66, 0xaa, 0x2c, 0x3f, 0x3b, 0xca, 0x14, 0x2f, 0xca, 0x3f, 0x5c, 0xb8, 0x32, 0x1b, + 0xe7, 0x92, 0x3b, 0xcd, 0x36, 0x9f, 0x85, 0x37, 0xa5, 0x7f, 0x18, 0x80, 0x29, 0x26, 0x5a, 0xd0, + 0x6d, 0x08, 0xd2, 0x51, 0x22, 0x9e, 0x38, 0xae, 0x2b, 0xca, 0xc6, 0x20, 0x04, 0x41, 0x66, 0x5d, + 0x21, 0xc6, 0x93, 0xec, 0x37, 0x5a, 0x80, 0x30, 0xc1, 0x0f, 0xd4, 0x87, 0x5a, 0x2b, 0x35, 0xcb, + 0xae, 0x4c, 0x88, 0xe0, 0x07, 0xf7, 0xb5, 0x16, 0xba, 0x04, 0x21, 0x83, 0xa8, 0x26, 0x7e, 0xc4, + 0xac, 0xd4, 0x88, 0x32, 0x65, 0x90, 0x4d, 0xfc, 0x08, 0x7d, 0x0e, 0xa2, 0x8f, 0x34, 0xa2, 0xe2, + 0x76, 0xc7, 0x39, 0x64, 0x6f, 0x2d, 0x42, 0x2f, 0x19, 0xa9, 0xd0, 0x67, 0x66, 0xe6, 0x69, 0x76, + 0x13, 0x3b, 0x6a, 0xc3, 0x6a, 0x71, 0xbf, 0x32, 0x4a, 0xdd, 0x60, 0x4a, 0x2a, 0x59, 0x2d, 0xb2, + 0x16, 0x8c, 0x04, 0xe4, 0xc9, 0xb5, 0x60, 0x64, 0x52, 0x0e, 0xae, 0x05, 0x23, 0x41, 0x79, 0x6a, + 0x2d, 0x18, 0x99, 0x92, 0x43, 0x6b, 0xc1, 0x48, 0x48, 0x0e, 0xaf, 0x05, 0x23, 0x61, 0x39, 0xb2, + 0x16, 0x8c, 0x44, 0xe4, 0xe8, 0x5a, 0x30, 0x12, 0x95, 0x61, 0x2d, 0x18, 0x01, 0x79, 0x7a, 0x2d, + 0x18, 0x99, 0x96, 0x63, 0x6b, 0xc1, 0x48, 0x4c, 0x8e, 0x73, 0x21, 0xbf, 0x16, 0x8c, 0x24, 0xe4, + 0x99, 0xb5, 0x60, 0x64, 0x46, 0x96, 0xd7, 0x82, 0x11, 0x59, 0x4e, 0xae, 0x05, 0x23, 0x49, 0x19, + 0xa5, 0x2b, 0x22, 0xd7, 0xa4, 0xa1, 0xaf, 0xf4, 0x9d, 0xd3, 0xd8, 0x2e, 0x32, 0x1b, 0x94, 0x2e, + 0x40, 0x90, 0x8a, 0x4a, 0xf4, 0x6e, 0x1f, 0xc8, 0x98, 0xca, 0x97, 0x0d, 0xc9, 0xfd, 0x58, 0x02, + 0xb9, 0x86, 0x1f, 0x74, 0xb1, 0xd9, 0xc0, 0xf7, 0xb5, 0x56, 0x69, 0xbf, 0x6b, 0x1e, 0xa0, 0x57, + 0x61, 0xa6, 0x41, 0x7f, 0xa8, 0x3c, 0x30, 0x4c, 0x0f, 0x5d, 0x62, 0x87, 0x1e, 0x67, 0xe4, 0x1a, + 0xa5, 0xd2, 0xb3, 0x7f, 0x09, 0x40, 0xf4, 0xa3, 0x2c, 0x19, 0x60, 0x5d, 0xa2, 0xbc, 0x0b, 0xe5, + 0xc6, 0x01, 0x18, 0xdb, 0x7a, 0xc4, 0xe4, 0x73, 0x1f, 0x8c, 0x62, 0x3d, 0x42, 0x4b, 0x30, 0x67, + 0xe2, 0xc7, 0x8e, 0x3a, 0xd8, 0x99, 0xc9, 0x62, 0x25, 0x49, 0xdb, 0x4a, 0xfe, 0x01, 0xb9, 0x7f, + 0x08, 0xc0, 0x8c, 0xbb, 0x68, 0x57, 0x17, 0xee, 0x81, 0x4c, 0x19, 0xc4, 0xd0, 0x55, 0xc7, 0xe2, + 0x48, 0xae, 0x56, 0x7c, 0x6f, 0x54, 0x44, 0xaf, 0x1f, 0x85, 0x3e, 0x57, 0xf5, 0x1d, 0x8b, 0x4d, + 0xc7, 0x8d, 0x03, 0x25, 0x4e, 0xfc, 0xb4, 0xf4, 0x2e, 0x24, 0xdc, 0x41, 0x9c, 0x82, 0x4a, 0x10, + 0xea, 0x9b, 0xef, 0xf3, 0x63, 0xcc, 0xe7, 0x1e, 0xb5, 0x22, 0x86, 0xa6, 0xbf, 0x01, 0xe8, 0xe4, + 0xdc, 0x7e, 0xc3, 0x64, 0x8a, 0x1b, 0x26, 0x5b, 0xfd, 0x86, 0xc9, 0xbb, 0xe7, 0xdb, 0x9b, 0x6f, + 0xd9, 0xfe, 0xf0, 0xe1, 0x77, 0x27, 0x21, 0xc1, 0x35, 0xb0, 0x67, 0x8b, 0x50, 0x79, 0x4c, 0xc5, + 0xbd, 0x61, 0x36, 0x7b, 0xa9, 0x37, 0xba, 0xbf, 0x80, 0x22, 0xbb, 0x0d, 0x5e, 0xe7, 0x57, 0xa8, + 0xdd, 0xa6, 0xe9, 0xfd, 0x39, 0xba, 0x00, 0xb5, 0xbf, 0x34, 0xdd, 0xeb, 0x74, 0x0d, 0x12, 0xcc, + 0xf6, 0xee, 0xf5, 0x9a, 0x64, 0xbd, 0xe2, 0x8c, 0xea, 0x75, 0x2b, 0x42, 0x9c, 0x74, 0x34, 0x5f, + 0xbe, 0x2f, 0x38, 0x32, 0xd5, 0x24, 0x54, 0x79, 0x8c, 0x8e, 0xf1, 0x1b, 0x52, 0x36, 0x26, 0xdd, + 0x36, 0x56, 0x3b, 0x16, 0x8f, 0x86, 0x4d, 0x2a, 0x51, 0x4e, 0xd9, 0xb6, 0x08, 0xda, 0x65, 0xac, + 0xc2, 0xce, 0x42, 0xd5, 0xf9, 0xe1, 0xa4, 0x42, 0x43, 0x63, 0x4b, 0x23, 0x8e, 0x53, 0x99, 0x21, + 0x03, 0x1c, 0xf8, 0x01, 0x84, 0x49, 0xb7, 0x4d, 0xa5, 0x21, 0xd3, 0xa6, 0xd3, 0xb7, 0xb2, 0x43, + 0xd6, 0x5c, 0xec, 0xb6, 0x0e, 0xb6, 0x3a, 0x35, 0xde, 0xcf, 0x33, 0x98, 0xf8, 0x63, 0xee, 0xaf, + 0x24, 0x58, 0xa0, 0xb7, 0x94, 0x5f, 0xf7, 0x12, 0xab, 0xbd, 0x70, 0xd1, 0x35, 0x08, 0x33, 0x33, + 0xdb, 0x73, 0x27, 0x56, 0x8f, 0x8f, 0x32, 0x21, 0xda, 0xfb, 0xb9, 0x8d, 0x82, 0x10, 0x05, 0xae, + 0xb2, 0xf0, 0x90, 0x63, 0x6b, 0x26, 0x61, 0xa9, 0x41, 0xfa, 0xe2, 0xdb, 0xb8, 0x5d, 0xc7, 0x36, + 0x7f, 0x9d, 0x31, 0x65, 0xae, 0xaf, 0x71, 0x83, 0xb7, 0xe5, 0xd2, 0x90, 0x1a, 0x5c, 0xb2, 0x17, + 0x84, 0xfe, 0x3f, 0x30, 0xbf, 0x89, 0x1f, 0x0d, 0xdb, 0x4d, 0x11, 0xc2, 0x5c, 0xdc, 0xba, 0x97, + 0xe6, 0xfa, 0xa0, 0xd0, 0xf2, 0x97, 0x9f, 0xe4, 0xd9, 0x4a, 0x77, 0xd8, 0x00, 0xc5, 0x1d, 0x98, + 0xfb, 0x14, 0x16, 0x06, 0xd0, 0x3d, 0x06, 0xf8, 0x00, 0x42, 0xc4, 0xd1, 0x1c, 0x61, 0x18, 0x27, + 0xc6, 0x41, 0xaf, 0x39, 0x9a, 0xd3, 0x25, 0x8a, 0x18, 0x97, 0xbb, 0x06, 0xaf, 0x14, 0xba, 0x8e, + 0x45, 0x59, 0x4c, 0x78, 0x13, 0xb8, 0x61, 0x99, 0x0d, 0xa3, 0x65, 0xf8, 0x13, 0xa2, 0xb9, 0x57, + 0xe1, 0xea, 0xa8, 0x6e, 0xde, 0x49, 0x28, 0x2c, 0x1a, 0xdf, 0x6d, 0x63, 0xda, 0x73, 0xdd, 0x20, + 0x0e, 0xfa, 0x00, 0x62, 0x82, 0x47, 0xc7, 0xc9, 0xa8, 0xba, 0xe9, 0x71, 0xdb, 0x03, 0x21, 0xb9, + 0xbf, 0x96, 0x60, 0xb6, 0x6c, 0x5b, 0x9d, 0x0e, 0xd6, 0x85, 0x1e, 0xe5, 0x67, 0xeb, 0xaa, 0x4f, + 0xc9, 0xa7, 0x3e, 0x37, 0x21, 0x50, 0x2d, 0x0b, 0x2f, 0xf1, 0xce, 0xf3, 0x3a, 0x9f, 0xd5, 0x32, + 0x7a, 0x97, 0x1f, 0x70, 0x97, 0x30, 0x89, 0x9e, 0xb8, 0xf5, 0xf2, 0xc8, 0x2c, 0x74, 0xef, 0x64, + 0xbb, 0x24, 0xf7, 0x83, 0x30, 0x5c, 0xf2, 0xbf, 0xb4, 0x95, 0x92, 0xbb, 0xf0, 0xcf, 0x20, 0xec, + 0xc6, 0xe3, 0xc6, 0x90, 0xdc, 0xc3, 0x20, 0xf2, 0xe2, 0x3c, 0xfc, 0x31, 0x39, 0x17, 0x13, 0xd5, + 0x20, 0x69, 0x98, 0x0e, 0xb6, 0x5b, 0x58, 0x7b, 0x48, 0x6d, 0x3b, 0x7a, 0x66, 0x22, 0x11, 0x32, + 0xae, 0x7d, 0x22, 0xfb, 0x00, 0xb8, 0x9d, 0xf3, 0x19, 0xcc, 0xfa, 0x41, 0xdd, 0xf5, 0x8f, 0x8e, + 0xc0, 0xb3, 0xe5, 0xf5, 0x60, 0xdd, 0x54, 0x81, 0x0f, 0xc8, 0x8d, 0x1e, 0x7e, 0xe4, 0x79, 0x78, + 0x3c, 0xcb, 0x72, 0xfb, 0xc2, 0x27, 0x52, 0x1e, 0xf0, 0xf6, 0xfa, 0x1c, 0x0d, 0xa6, 0x96, 0x7f, + 0x4d, 0x8e, 0xc6, 0x7d, 0x08, 0xf1, 0xf8, 0xbb, 0x48, 0x78, 0xde, 0xb9, 0xe8, 0x16, 0x78, 0x80, + 0x5f, 0x11, 0x68, 0xe9, 0x3f, 0x94, 0x20, 0xe6, 0x7f, 0xdd, 0xc8, 0x80, 0x08, 0x3b, 0x7e, 0x57, + 0x44, 0x4e, 0xbe, 0xf0, 0x78, 0x08, 0x67, 0xa5, 0xaa, 0x4e, 0xad, 0x4b, 0xdd, 0xb6, 0x3a, 0xbd, + 0x84, 0xf7, 0xa4, 0x12, 0xa1, 0x04, 0x6a, 0xb9, 0xa7, 0xbf, 0x09, 0x51, 0xef, 0xd0, 0x7d, 0x01, + 0xd1, 0xc9, 0x17, 0x18, 0x10, 0x1d, 0x39, 0x7f, 0x19, 0xe2, 0x7d, 0x27, 0x86, 0xe6, 0xbd, 0x35, + 0x04, 0x8b, 0x21, 0xbe, 0x86, 0x33, 0x51, 0x72, 0x3f, 0x0a, 0xc3, 0xec, 0x30, 0xc9, 0xfd, 0x31, + 0xc8, 0x3e, 0xb9, 0xa5, 0xb6, 0x0c, 0xe2, 0x08, 0xde, 0xbc, 0x31, 0x3a, 0x48, 0xe2, 0x13, 0x7e, + 0x82, 0x15, 0x13, 0x76, 0xbf, 0x48, 0xfc, 0x14, 0x12, 0x3a, 0x5f, 0xb8, 0x48, 0x8d, 0x88, 0x0a, + 0xb3, 0x51, 0x61, 0x8d, 0x21, 0x02, 0x50, 0xa0, 0xc7, 0x75, 0x5f, 0x13, 0x41, 0x0d, 0x88, 0x7b, + 0xe0, 0x2c, 0x28, 0x41, 0x9d, 0xda, 0xe7, 0x17, 0x86, 0x31, 0x77, 0x16, 0x16, 0xa6, 0x68, 0xc2, + 0x8c, 0x3b, 0x89, 0x1b, 0x4a, 0x89, 0xbe, 0x90, 0x69, 0xdc, 0x83, 0xa9, 0x89, 0xd0, 0xca, 0x77, + 0x24, 0x98, 0x75, 0x67, 0xf2, 0x3c, 0x3e, 0x51, 0x56, 0x16, 0x2f, 0xd6, 0x8e, 0x8f, 0x32, 0x49, + 0x71, 0x32, 0x6e, 0x3c, 0xea, 0xb9, 0xf9, 0x2e, 0xa9, 0x0f, 0x00, 0xea, 0xd4, 0x26, 0xa1, 0xed, + 0x6e, 0xb5, 0x98, 0xb0, 0x49, 0xa8, 0x60, 0x7b, 0x7e, 0x9b, 0x84, 0xfe, 0xac, 0xea, 0xe8, 0xbb, + 0x12, 0x24, 0x79, 0x6a, 0xb3, 0xdd, 0x75, 0x34, 0x5e, 0xdc, 0xe0, 0x06, 0x46, 0x3e, 0x3e, 0x3e, + 0xca, 0xcc, 0xb0, 0xd7, 0xbb, 0x21, 0xda, 0xd8, 0xb4, 0x17, 0xf6, 0x6f, 0x7b, 0x28, 0x22, 0x8e, + 0xe0, 0x11, 0x74, 0x74, 0x17, 0x12, 0x3c, 0x5a, 0xe4, 0x96, 0xb8, 0x32, 0x1b, 0x2f, 0x5e, 0xbc, + 0xfa, 0xec, 0x28, 0x93, 0x1d, 0x72, 0x4f, 0x78, 0xa0, 0xe9, 0x3e, 0xef, 0xab, 0xc4, 0xf7, 0xfc, + 0x8f, 0x68, 0x1d, 0x66, 0xb8, 0x29, 0xdc, 0x2b, 0x20, 0x83, 0xf1, 0x13, 0xf2, 0xdc, 0x8c, 0xf6, + 0xa8, 0x3c, 0xaa, 0x98, 0x9b, 0x87, 0xb9, 0xa1, 0x36, 0xd8, 0x2f, 0x42, 0x30, 0xdf, 0x2f, 0x56, + 0x3d, 0x2b, 0x49, 0x1d, 0xd4, 0xb7, 0xef, 0x8f, 0x2d, 0x9a, 0xbd, 0x52, 0x32, 0x26, 0x1a, 0xdd, + 0xa7, 0x41, 0x8d, 0xfb, 0xd9, 0x80, 0xf6, 0xba, 0x00, 0x3e, 0x7b, 0xbd, 0x03, 0xf8, 0xae, 0x0a, + 0xfb, 0xc8, 0xd3, 0x2c, 0x3c, 0xec, 0xf7, 0xc1, 0x05, 0xe0, 0xd9, 0x78, 0xaf, 0x0c, 0xce, 0xd5, + 0x2d, 0x3f, 0x95, 0x20, 0xde, 0xb7, 0xb3, 0xdf, 0xa4, 0x72, 0xd9, 0xf6, 0x6c, 0x2b, 0x5e, 0x20, + 0xf6, 0xce, 0xf9, 0xb7, 0xd5, 0x6f, 0x72, 0xa5, 0xff, 0x56, 0x82, 0x78, 0xdf, 0x41, 0xfe, 0x9a, + 0xd4, 0xd2, 0x8b, 0x5f, 0x79, 0x1d, 0x12, 0xfd, 0xaf, 0xc8, 0x37, 0x87, 0xf4, 0x62, 0xe6, 0xc8, + 0x7d, 0x19, 0x42, 0x9c, 0x82, 0x10, 0x24, 0x3e, 0x2c, 0x54, 0x77, 0xaa, 0x9b, 0x2b, 0xea, 0xf2, + 0x96, 0xa2, 0xae, 0x94, 0xe4, 0x09, 0x14, 0x83, 0x48, 0xb9, 0xb2, 0x5e, 0xa1, 0x44, 0x59, 0x42, + 0xd3, 0x10, 0x66, 0x4f, 0x95, 0xb2, 0x1c, 0xc8, 0x15, 0x41, 0xe6, 0xd8, 0x7b, 0x98, 0xaa, 0x19, + 0xea, 0x95, 0xa0, 0x3c, 0xcc, 0x32, 0x0f, 0xa2, 0x4d, 0x2d, 0x2b, 0x7a, 0xbd, 0x55, 0x9f, 0x2d, + 0x9e, 0xf4, 0x9a, 0xe8, 0xed, 0xdd, 0xd4, 0xda, 0x38, 0xf7, 0x37, 0x41, 0x48, 0xf6, 0x40, 0x5c, + 0x25, 0xfb, 0x17, 0x52, 0xcf, 0x3f, 0x0a, 0x9d, 0x99, 0x9a, 0x3e, 0x31, 0x5e, 0xb8, 0x4a, 0x22, + 0x45, 0xfc, 0x21, 0xbd, 0x34, 0xcf, 0x8e, 0x32, 0xc9, 0xc1, 0xc5, 0x92, 0xe7, 0xcc, 0x1d, 0xbb, + 0x4b, 0x64, 0x81, 0x6f, 0xc3, 0x3c, 0x50, 0x7b, 0x35, 0x7c, 0x3c, 0xf0, 0x6d, 0x98, 0x07, 0xbb, + 0x4a, 0x55, 0x09, 0xd3, 0xc6, 0x5d, 0xdb, 0x40, 0x6b, 0x10, 0xb4, 0x3a, 0x8e, 0xeb, 0xd2, 0xbf, + 0x7d, 0xae, 0x2d, 0x6d, 0x75, 0xc4, 0x7e, 0x14, 0x86, 0x81, 0xd6, 0x78, 0xb5, 0x45, 0xef, 0xa0, + 0x85, 0xd3, 0x3d, 0x96, 0x08, 0x8d, 0xf7, 0xbd, 0x88, 0x74, 0x13, 0x62, 0xfe, 0x13, 0x1b, 0x92, + 0x0f, 0x2a, 0xf4, 0x87, 0x5d, 0x3e, 0x3f, 0xd6, 0xd2, 0x85, 0xc3, 0xea, 0x4b, 0x11, 0x7e, 0x19, + 0xa2, 0xde, 0x3e, 0xce, 0x93, 0x29, 0xf5, 0x32, 0x47, 0x3c, 0x2c, 0x39, 0x25, 0x87, 0x72, 0x3f, + 0x08, 0x40, 0x4c, 0xc1, 0xc4, 0x6a, 0x3d, 0xc4, 0x3a, 0xb5, 0xa0, 0xbc, 0x9a, 0x71, 0x69, 0xfc, + 0x9a, 0xf1, 0x02, 0x44, 0x7b, 0x1a, 0xe8, 0x1c, 0x75, 0x9b, 0xbd, 0x51, 0xe8, 0x63, 0x88, 0xd7, + 0xad, 0xae, 0xa9, 0x6b, 0xf6, 0x21, 0xb3, 0xab, 0x98, 0x05, 0x92, 0x18, 0x59, 0x45, 0xe7, 0x5f, + 0x75, 0xbe, 0x28, 0x06, 0x53, 0xfb, 0x49, 0x89, 0xd5, 0x7d, 0x4f, 0xb9, 0xf7, 0x20, 0xe6, 0x6f, + 0x45, 0x11, 0x08, 0x6e, 0x6e, 0x6d, 0x56, 0xf8, 0x9d, 0x2c, 0x16, 0x4a, 0x77, 0x97, 0xab, 0xeb, + 0xeb, 0xb2, 0x44, 0xe9, 0x95, 0x8f, 0xaa, 0x3b, 0x72, 0x80, 0x57, 0xba, 0xd6, 0x76, 0x0a, 0xca, + 0x8e, 0x1b, 0xbd, 0xcd, 0x61, 0x88, 0xfb, 0xe7, 0xa3, 0x92, 0x8f, 0x9a, 0x9d, 0x8c, 0xd0, 0xe7, + 0x79, 0xbf, 0x36, 0xe6, 0x8a, 0x5d, 0x0e, 0xb2, 0xfd, 0xa8, 0xb9, 0x7f, 0x0e, 0x00, 0xea, 0xbd, + 0x78, 0x4f, 0x58, 0x7d, 0x04, 0xd0, 0xd8, 0xc7, 0x8d, 0x83, 0x8e, 0x65, 0x98, 0x8e, 0xf0, 0x35, + 0xdf, 0x19, 0x8b, 0x77, 0x3c, 0x61, 0x55, 0xf2, 0xc6, 0x2b, 0x3e, 0x2c, 0xf4, 0x07, 0xa3, 0xf3, + 0x14, 0x93, 0x2c, 0x4f, 0xc1, 0x6e, 0xfe, 0x6f, 0x34, 0x57, 0x91, 0x2e, 0x00, 0xf4, 0x56, 0x8c, + 0xde, 0x1a, 0xaf, 0x5e, 0xdc, 0xcd, 0xf5, 0xb1, 0xbe, 0x7e, 0x5e, 0xcf, 0xfd, 0x4f, 0x10, 0x50, + 0xc9, 0xc6, 0x9a, 0x83, 0xa9, 0x88, 0x26, 0xa3, 0x42, 0x1c, 0x45, 0x98, 0xe2, 0x2e, 0x7d, 0xe0, + 0x3c, 0x2e, 0xbd, 0x97, 0x66, 0x64, 0xde, 0xfc, 0xd7, 0x20, 0xd6, 0xb0, 0x5a, 0xdd, 0xb6, 0xa9, + 0xb2, 0x9a, 0x2d, 0xe1, 0x7f, 0x7c, 0x69, 0xd4, 0x1b, 0x3b, 0xb1, 0xb8, 0x7c, 0xc9, 0x6a, 0xd1, + 0x67, 0xef, 0x8b, 0x06, 0x06, 0xc8, 0x7a, 0xa0, 0x2b, 0x10, 0xf5, 0x24, 0x0f, 0x2f, 0x1e, 0x51, + 0x7a, 0x04, 0x74, 0x0b, 0xa6, 0x34, 0xa2, 0x5a, 0x7b, 0xcc, 0x98, 0x3e, 0xeb, 0x2a, 0x2a, 0x41, + 0x8d, 0x6c, 0xed, 0xa1, 0xd7, 0x21, 0xd9, 0xd6, 0x1e, 0xab, 0x7b, 0x36, 0xaf, 0x02, 0x57, 0x0d, + 0xbd, 0xc5, 0x25, 0xa1, 0xa4, 0xcc, 0xb4, 0xb5, 0xc7, 0xcb, 0x82, 0x5e, 0xd5, 0x5b, 0x18, 0xbd, + 0x05, 0xf1, 0xbd, 0x07, 0xdc, 0xb5, 0xe2, 0x5a, 0x89, 0x17, 0xc0, 0xcd, 0x1c, 0x1f, 0x65, 0xa6, + 0x97, 0xef, 0xb1, 0x83, 0x61, 0xc9, 0x9d, 0xe9, 0xbd, 0x07, 0xde, 0x43, 0xfa, 0xbf, 0x25, 0x08, + 0x8b, 0x1d, 0xa1, 0x0e, 0x80, 0x38, 0x1e, 0x43, 0xe7, 0xef, 0x34, 0x5e, 0xbc, 0x77, 0x7c, 0x94, + 0x89, 0x96, 0x18, 0xb5, 0x5a, 0x26, 0xcf, 0x8e, 0x32, 0x1f, 0x5c, 0x54, 0xa3, 0xb8, 0x20, 0x4a, + 0x94, 0x4f, 0x52, 0xd5, 0x59, 0x64, 0x79, 0x5f, 0x23, 0xea, 0xbe, 0x41, 0x1c, 0xab, 0x69, 0x6b, + 0x6d, 0x51, 0x75, 0x11, 0xdb, 0xd7, 0xc8, 0xaa, 0x4b, 0x43, 0x69, 0x6a, 0x9b, 0x3d, 0xe4, 0x25, + 0x77, 0xbc, 0x64, 0xc7, 0x7b, 0x46, 0xb7, 0xe0, 0x92, 0x37, 0x58, 0xa5, 0x27, 0x55, 0xef, 0x36, + 0x0e, 0x30, 0xd3, 0x41, 0x54, 0xb8, 0xcf, 0x7a, 0x8d, 0x1b, 0xda, 0xe3, 0x22, 0x6f, 0xca, 0x5d, + 0x82, 0x59, 0xdf, 0x6b, 0xf5, 0x2c, 0x69, 0x0c, 0x32, 0x2f, 0x0e, 0xf1, 0x7d, 0x10, 0x71, 0x0f, + 0x66, 0x06, 0x3e, 0x7d, 0x13, 0xf2, 0xd7, 0x1f, 0x71, 0xec, 0xff, 0x56, 0x2e, 0x5f, 0xe2, 0x8f, + 0xae, 0x6f, 0x90, 0x68, 0xf4, 0x3d, 0xe7, 0xde, 0x84, 0xa4, 0x37, 0x8d, 0x27, 0x48, 0xae, 0x40, + 0x94, 0x55, 0x05, 0xb4, 0x35, 0xfb, 0xc0, 0xad, 0x0c, 0xf0, 0x08, 0xb9, 0x0c, 0xbc, 0xc4, 0xa2, + 0x90, 0xf7, 0xd6, 0xd9, 0x8a, 0x4b, 0x56, 0xbb, 0xc3, 0x5f, 0xbb, 0x1b, 0xa6, 0xcc, 0xc2, 0xe2, + 0xf0, 0x0e, 0xde, 0xe6, 0x7e, 0x2a, 0x43, 0x78, 0x5b, 0x3b, 0x6c, 0x59, 0x9a, 0x8e, 0xb2, 0x30, + 0xed, 0x16, 0xe3, 0xb9, 0x1b, 0x8a, 0x2a, 0x7e, 0x52, 0x3f, 0x1f, 0xcb, 0x2c, 0x7d, 0xe6, 0xe3, + 0x63, 0x03, 0x12, 0x5d, 0x82, 0x6d, 0xca, 0x62, 0x2a, 0xfb, 0x90, 0x83, 0xeb, 0xb3, 0x62, 0xf1, + 0xd9, 0x51, 0xe6, 0xce, 0x78, 0xdc, 0x81, 0x1b, 0x5d, 0xdb, 0x70, 0x0e, 0xf3, 0xb5, 0x7b, 0xeb, + 0xbb, 0x02, 0x8a, 0x0a, 0x23, 0x4b, 0x89, 0x77, 0xfd, 0x8f, 0xa2, 0xe6, 0x92, 0xbe, 0x69, 0xb5, + 0x6d, 0x34, 0x6c, 0x8b, 0xb8, 0x19, 0x26, 0x41, 0xdd, 0x60, 0x44, 0xf4, 0x1a, 0xcc, 0xec, 0x19, + 0x26, 0xcb, 0x08, 0xbb, 0xfd, 0x78, 0x72, 0x29, 0xe1, 0x92, 0x45, 0xc7, 0x87, 0x90, 0xf0, 0x15, + 0x3b, 0x52, 0x2e, 0x0f, 0x31, 0x2e, 0xdf, 0x3a, 0x3e, 0xca, 0xc4, 0x7b, 0x52, 0x83, 0x73, 0xfa, + 0xf3, 0xd8, 0x4e, 0xf1, 0xde, 0x34, 0x94, 0xcf, 0xe7, 0x60, 0x8a, 0x7d, 0x39, 0xca, 0xab, 0xd9, + 0x15, 0xfe, 0x80, 0x2a, 0x10, 0x17, 0xb1, 0x16, 0xfe, 0x59, 0xa9, 0xa8, 0x10, 0xf5, 0xe7, 0x15, + 0xdc, 0x0f, 0x4f, 0xf3, 0x15, 0xb3, 0x61, 0xe9, 0x58, 0xaf, 0xd0, 0x67, 0x45, 0x84, 0x96, 0xd9, + 0x03, 0x41, 0x2b, 0x90, 0x68, 0xb4, 0xb0, 0x66, 0x76, 0x3b, 0x2e, 0x0e, 0x1a, 0x13, 0x27, 0x2e, + 0xc6, 0x09, 0xa0, 0x4d, 0x40, 0x7b, 0xac, 0x56, 0xcd, 0xbf, 0x2a, 0x96, 0x8f, 0x1d, 0x07, 0x4c, + 0x66, 0x63, 0x95, 0xde, 0xca, 0xd0, 0x55, 0x88, 0x9b, 0x96, 0xd9, 0xd0, 0xcc, 0x06, 0x6e, 0x31, + 0xd1, 0xcd, 0x53, 0xb8, 0xfd, 0x44, 0x54, 0x84, 0x10, 0x2f, 0x4b, 0x10, 0x4e, 0xf2, 0xf5, 0x71, + 0xbf, 0xf9, 0x58, 0x9d, 0x50, 0xc4, 0x48, 0x54, 0x81, 0xb0, 0xcd, 0xab, 0x6d, 0x58, 0xa9, 0xc2, + 0x99, 0xc1, 0x2a, 0x5f, 0x45, 0xcf, 0xea, 0x84, 0xe2, 0x8e, 0x45, 0x3b, 0x6e, 0x61, 0x33, 0x57, + 0xd4, 0xa2, 0x24, 0x35, 0x3f, 0xa6, 0x0b, 0xd2, 0x03, 0xec, 0x43, 0xa1, 0x1b, 0x34, 0x58, 0xf6, + 0x8d, 0x15, 0x31, 0x8c, 0xde, 0x60, 0x5f, 0xa1, 0x0c, 0xdd, 0x20, 0x1f, 0x89, 0x36, 0xa9, 0xa5, + 0xe1, 0x1a, 0x0f, 0xac, 0xbc, 0x61, 0xfa, 0xd6, 0x17, 0xce, 0x63, 0x60, 0xaf, 0x4e, 0x28, 0x3e, + 0x04, 0x74, 0x0f, 0xa6, 0x1b, 0x3d, 0x19, 0x98, 0x9a, 0x61, 0x80, 0x37, 0xcf, 0xa5, 0x08, 0x57, + 0xa9, 0xf2, 0xeb, 0x51, 0xd1, 0x27, 0x90, 0x20, 0x7d, 0x0e, 0x59, 0xea, 0x12, 0x43, 0x7d, 0xe3, + 0xbc, 0x01, 0xe1, 0xd5, 0x09, 0x65, 0x00, 0x09, 0xfd, 0x5f, 0x90, 0x9d, 0x81, 0x2c, 0x14, 0xcb, + 0xfa, 0x8f, 0xae, 0x17, 0x3e, 0x25, 0xd7, 0xb6, 0x3a, 0xa1, 0x9c, 0x40, 0x43, 0x9f, 0xc1, 0x0c, + 0xe9, 0xff, 0x9c, 0x2e, 0xb5, 0xc0, 0x26, 0x78, 0x73, 0xfc, 0x0f, 0xf0, 0x7a, 0xf8, 0x83, 0x58, + 0x14, 0xde, 0xec, 0x4f, 0x66, 0xb1, 0xea, 0x97, 0xd1, 0xf0, 0xc3, 0x93, 0x6b, 0x14, 0x7e, 0x00, + 0x0b, 0xdd, 0x85, 0x68, 0xdb, 0x55, 0x2a, 0xac, 0x76, 0x64, 0xb4, 0x0f, 0x33, 0xa8, 0xe7, 0x56, + 0x27, 0x94, 0xde, 0x78, 0xf4, 0xff, 0x25, 0xb8, 0xa2, 0x8d, 0xc8, 0x7a, 0xb1, 0x5a, 0x93, 0xd1, + 0x81, 0xfe, 0x31, 0x72, 0x6b, 0xab, 0x13, 0xca, 0xc8, 0x59, 0x90, 0x0d, 0xf3, 0xda, 0x50, 0xa5, + 0x96, 0x5a, 0x3c, 0xd3, 0xd0, 0x1e, 0xa9, 0x2e, 0x57, 0x27, 0x94, 0x53, 0x90, 0x51, 0x03, 0x92, + 0x64, 0xf0, 0x8b, 0xc6, 0xd4, 0xcb, 0x6c, 0xba, 0xb7, 0xce, 0xe4, 0x83, 0x93, 0x1f, 0x54, 0xae, + 0x4e, 0x28, 0x27, 0xf1, 0xd0, 0xcb, 0x10, 0xe3, 0xa5, 0xd7, 0x36, 0xd6, 0x88, 0x65, 0xa6, 0xae, + 0x70, 0x05, 0xcc, 0x68, 0x0a, 0x23, 0xa1, 0x6f, 0x40, 0xc6, 0xc6, 0x8e, 0x6d, 0x30, 0x5b, 0x0e, + 0x3f, 0xc6, 0x8d, 0x2e, 0xb3, 0xfe, 0xf6, 0x34, 0xa3, 0xd5, 0xb5, 0xb1, 0xda, 0xb2, 0x9a, 0xa9, + 0x2c, 0x93, 0xf1, 0xa3, 0x1d, 0x31, 0x81, 0x50, 0x71, 0x01, 0x96, 0xf9, 0x78, 0xe5, 0x8a, 0x7d, + 0x5a, 0xd3, 0xba, 0xd5, 0x2c, 0x46, 0x21, 0x2c, 0xd2, 0xe6, 0x5e, 0x59, 0x0c, 0x2f, 0x88, 0xe1, + 0xa5, 0x30, 0x69, 0xf9, 0x73, 0xb9, 0x7f, 0x8c, 0x41, 0xc4, 0x33, 0x5e, 0x96, 0x00, 0x79, 0xf6, + 0x69, 0xef, 0x33, 0x0a, 0x6a, 0x56, 0x04, 0xe8, 0x01, 0xb8, 0x6d, 0xbd, 0x2f, 0x29, 0xee, 0xf4, + 0x15, 0x42, 0x8e, 0xf3, 0x2d, 0x33, 0x65, 0x50, 0xaf, 0x52, 0x92, 0xaa, 0x7b, 0x51, 0x69, 0xef, + 0xa9, 0x7b, 0x9e, 0xde, 0x48, 0xb8, 0x64, 0xa1, 0xee, 0xaf, 0x41, 0xc2, 0xee, 0x9a, 0x2c, 0xd7, + 0x2d, 0x82, 0x4a, 0xdc, 0x28, 0x8f, 0x0b, 0xaa, 0x88, 0x0b, 0x95, 0x06, 0x34, 0xd0, 0x8d, 0x33, + 0x35, 0x90, 0xbb, 0xf7, 0x55, 0xc9, 0x53, 0x41, 0xcb, 0x83, 0x2a, 0xe8, 0xf5, 0xb3, 0x55, 0x90, + 0x0f, 0xc6, 0xd3, 0x41, 0xbb, 0x43, 0x75, 0xd0, 0xd2, 0x98, 0x42, 0xd4, 0x87, 0xd8, 0xaf, 0x84, + 0x4a, 0x03, 0x4a, 0xe8, 0xc6, 0x99, 0x4a, 0xc8, 0xbf, 0x47, 0xa1, 0x85, 0xb6, 0x86, 0x68, 0xa1, + 0x9b, 0xe7, 0xf2, 0x77, 0x57, 0xa5, 0x3e, 0x35, 0xa4, 0x0c, 0x53, 0x43, 0xf9, 0xf1, 0xd4, 0x90, + 0x0f, 0xb2, 0x4f, 0x0f, 0x7d, 0x7a, 0x42, 0x0f, 0xc9, 0x67, 0x0b, 0xf2, 0xa1, 0x91, 0xc4, 0x55, + 0xe9, 0x84, 0x22, 0xd2, 0x86, 0x28, 0xa2, 0xe4, 0x99, 0xf2, 0xe1, 0xb4, 0x0a, 0x8a, 0x55, 0x69, + 0x88, 0x26, 0xfa, 0x08, 0x62, 0x7e, 0xed, 0xc1, 0xca, 0xe4, 0x46, 0xeb, 0xb9, 0x53, 0xbe, 0x03, + 0x67, 0x3c, 0xe0, 0x6b, 0x42, 0x5f, 0x3b, 0xa9, 0x84, 0x66, 0xcf, 0x04, 0x3f, 0xa5, 0x06, 0x63, + 0x55, 0x3a, 0xa9, 0x85, 0xd6, 0xfd, 0x5a, 0x68, 0xee, 0x4c, 0x1b, 0xe5, 0x84, 0x1b, 0xb4, 0x2a, + 0xf9, 0xd5, 0xd0, 0x77, 0x25, 0xb8, 0x32, 0x4a, 0x8f, 0x08, 0x03, 0xe0, 0xfd, 0x0b, 0xaa, 0x21, + 0xdf, 0xa4, 0x23, 0xa7, 0x41, 0xe4, 0x54, 0x3d, 0xb4, 0x70, 0x66, 0x8d, 0xd6, 0x68, 0xaf, 0x6c, + 0x55, 0x3a, 0x55, 0x11, 0xe9, 0xc3, 0x14, 0x51, 0xea, 0xec, 0x2f, 0x58, 0x4f, 0xfb, 0x1c, 0x7f, + 0x55, 0x1a, 0xa6, 0x89, 0x1e, 0x40, 0xc4, 0xb1, 0xb5, 0x06, 0x4b, 0x2d, 0x5e, 0x62, 0xf9, 0xe3, + 0xfb, 0x22, 0xa4, 0x54, 0x1a, 0x3f, 0xa4, 0x44, 0x11, 0x0c, 0xb3, 0xe9, 0xfe, 0x4b, 0xd9, 0x9d, + 0x62, 0xb2, 0x10, 0x53, 0x58, 0xfc, 0x54, 0xc2, 0x6c, 0x9e, 0xaa, 0x5e, 0x04, 0x88, 0xb8, 0xa5, + 0x5f, 0x3e, 0x45, 0x93, 0xfb, 0xbe, 0x04, 0x93, 0x6b, 0x56, 0x1d, 0xbd, 0xe4, 0x4b, 0x5c, 0xc4, + 0xc5, 0x5a, 0xa6, 0xd6, 0xac, 0xba, 0xc8, 0x40, 0xbc, 0xdf, 0x1b, 0x2d, 0xe2, 0x40, 0xaf, 0x8c, + 0x38, 0x0d, 0x2f, 0xef, 0xe3, 0x0d, 0x42, 0x5f, 0x85, 0x70, 0x87, 0xbb, 0xc1, 0x42, 0xef, 0xe4, + 0x46, 0x8d, 0xe7, 0x3d, 0x15, 0x77, 0x48, 0xee, 0xbf, 0x02, 0x70, 0xf9, 0x54, 0xad, 0x8a, 0xe6, + 0xfb, 0x52, 0x17, 0x51, 0x37, 0x01, 0x81, 0xbe, 0x08, 0xf3, 0x3d, 0x15, 0xce, 0xcb, 0x1f, 0xfb, + 0xb4, 0xd6, 0x9c, 0xd7, 0xca, 0x2a, 0x20, 0x85, 0xee, 0x7a, 0x03, 0x7a, 0x74, 0x15, 0x9b, 0x03, + 0x0e, 0x30, 0xf2, 0xda, 0x2a, 0xa6, 0xab, 0xed, 0x4c, 0x98, 0x36, 0x4c, 0xe2, 0x50, 0xcf, 0xca, + 0xcd, 0x15, 0x4f, 0x15, 0x37, 0xc4, 0x21, 0x7e, 0x79, 0xac, 0x17, 0xca, 0xab, 0x51, 0xef, 0xad, + 0x57, 0x05, 0x0e, 0x7b, 0x89, 0xd0, 0x7b, 0x52, 0xc0, 0x9d, 0xa1, 0xaa, 0xa3, 0xb7, 0x5d, 0xa7, + 0x76, 0x6a, 0x4c, 0x0f, 0x51, 0xb8, 0xbd, 0xaf, 0xc1, 0x8c, 0x63, 0x77, 0x4d, 0xfe, 0xad, 0x3d, + 0x47, 0x60, 0x91, 0x2a, 0x25, 0xe1, 0x91, 0x59, 0xff, 0xd7, 0x6f, 0xf8, 0xff, 0x57, 0x9c, 0x0d, + 0x4b, 0xc7, 0x28, 0x01, 0xb0, 0xad, 0x11, 0xd2, 0xd9, 0xb7, 0x35, 0x82, 0xe5, 0x09, 0x14, 0x86, + 0xc9, 0xbb, 0x1b, 0x35, 0x59, 0x7a, 0xfd, 0x23, 0x7f, 0x92, 0xa7, 0xac, 0x14, 0xaa, 0x9b, 0xd5, + 0xcd, 0x15, 0x75, 0xb3, 0xb0, 0x51, 0xa9, 0xc9, 0x13, 0x28, 0x05, 0x73, 0x1f, 0x16, 0xaa, 0x3b, + 0x22, 0xeb, 0xa3, 0x56, 0x37, 0x77, 0x2a, 0xca, 0xfd, 0xc2, 0xba, 0x2c, 0xa1, 0x79, 0x40, 0xca, + 0x56, 0xe9, 0x6e, 0xad, 0x5c, 0x54, 0x4b, 0x5b, 0x1b, 0xdb, 0x85, 0xd2, 0x4e, 0x75, 0x6b, 0x53, + 0x0e, 0xa0, 0x08, 0x04, 0xcb, 0x5b, 0x9b, 0x15, 0x19, 0x5e, 0xff, 0xf9, 0x94, 0xa8, 0xc2, 0xbd, + 0x0a, 0xd3, 0xbb, 0x9b, 0xb5, 0xed, 0x4a, 0xa9, 0xba, 0x5c, 0xad, 0x94, 0xe5, 0x89, 0xf4, 0xec, + 0x93, 0xa7, 0xd9, 0x19, 0xda, 0xb4, 0x6b, 0x92, 0x0e, 0x6e, 0x30, 0xc3, 0x03, 0xa5, 0x21, 0x54, + 0x2c, 0x94, 0xee, 0xee, 0x6e, 0xcb, 0x52, 0x3a, 0xf1, 0xe4, 0x69, 0x16, 0xd8, 0xc7, 0x0e, 0xdc, + 0x44, 0xb8, 0xc2, 0xe3, 0xd7, 0x5b, 0x4a, 0x45, 0x0e, 0xa4, 0x67, 0x9e, 0x3c, 0xcd, 0x4e, 0xb3, + 0xb0, 0xb8, 0x50, 0xfc, 0xaf, 0x41, 0xbc, 0x56, 0x5a, 0xad, 0x6c, 0x14, 0xd4, 0xd2, 0x6a, 0x61, + 0x73, 0xa5, 0x22, 0x4f, 0xa6, 0xe7, 0x9e, 0x3c, 0xcd, 0xca, 0x83, 0xca, 0x83, 0x4e, 0x51, 0xdd, + 0xd8, 0xde, 0x52, 0x76, 0xe4, 0x60, 0x6f, 0x0a, 0xae, 0xb3, 0x51, 0x0e, 0x80, 0x8f, 0x5e, 0xae, + 0x54, 0xca, 0xf2, 0x54, 0x1a, 0x3d, 0x79, 0x9a, 0x4d, 0xd0, 0xf6, 0x9e, 0x2a, 0x46, 0xd7, 0x20, + 0x56, 0x52, 0x2a, 0x85, 0x9d, 0x8a, 0x5a, 0xdb, 0x29, 0xec, 0xd4, 0xe4, 0x50, 0x6f, 0x27, 0x3e, + 0xf5, 0x8a, 0xf2, 0x90, 0x2c, 0xec, 0xee, 0x6c, 0xa9, 0x7d, 0x7d, 0xc3, 0xe9, 0x85, 0x27, 0x4f, + 0xb3, 0xb3, 0xb4, 0x2f, 0x95, 0x6d, 0xfe, 0xfe, 0x5f, 0x00, 0xb9, 0x6f, 0xfd, 0xea, 0x4a, 0x49, + 0x8e, 0xa4, 0xe7, 0x9f, 0x3c, 0xcd, 0xa2, 0xc1, 0x2d, 0xac, 0x94, 0xe8, 0xa5, 0xd8, 0xf9, 0x78, + 0xbb, 0x52, 0xae, 0xd4, 0x4a, 0x6a, 0xff, 0xb6, 0xa3, 0xe9, 0xd4, 0x93, 0xa7, 0xd9, 0x39, 0x3a, + 0xe6, 0xc4, 0xd6, 0x6f, 0x82, 0x5c, 0xdb, 0x51, 0x2a, 0x85, 0x0d, 0xb5, 0xba, 0xb9, 0x52, 0xa9, + 0xb1, 0x97, 0x05, 0xbd, 0x25, 0x0d, 0x28, 0x42, 0xba, 0x85, 0xcd, 0xca, 0x87, 0x03, 0xf8, 0xd3, + 0xbd, 0xfe, 0x03, 0xba, 0x0d, 0x65, 0x21, 0xba, 0x51, 0x5d, 0x51, 0x0a, 0x0c, 0x37, 0x96, 0x4e, + 0x3e, 0x79, 0x9a, 0x8d, 0xd3, 0x7e, 0x9e, 0xa6, 0x42, 0x55, 0xc8, 0xb0, 0x43, 0xa9, 0x6d, 0x17, + 0x36, 0xd5, 0xd2, 0xd6, 0xe6, 0x72, 0x75, 0x45, 0x55, 0x2a, 0xa5, 0xad, 0xcd, 0x52, 0x75, 0xbd, + 0xca, 0xc7, 0xc5, 0xd3, 0x57, 0x9f, 0x3c, 0xcd, 0x66, 0xdd, 0x23, 0x3a, 0x55, 0xaf, 0xbc, 0x07, + 0x97, 0x39, 0xd4, 0xbd, 0x75, 0x7e, 0xb8, 0x7e, 0x0e, 0x4c, 0xa4, 0x17, 0x9f, 0x3c, 0xcd, 0xa6, + 0x3d, 0x90, 0x93, 0x1a, 0xe2, 0x4d, 0x40, 0xe2, 0x28, 0x94, 0xca, 0xf6, 0x7a, 0xb5, 0xc4, 0x27, + 0x9f, 0x49, 0x5f, 0x7e, 0xf2, 0x34, 0x7b, 0xa9, 0x77, 0x18, 0x3e, 0x71, 0x9f, 0x8e, 0xfc, 0xce, + 0xf7, 0x17, 0x27, 0xfe, 0xfc, 0x07, 0x8b, 0x13, 0xc5, 0xeb, 0x3f, 0xf9, 0x8f, 0xc5, 0x89, 0x9f, + 0x1c, 0x2f, 0x4a, 0x3f, 0x3b, 0x5e, 0x94, 0x7e, 0x71, 0xbc, 0x28, 0xfd, 0xfb, 0xf1, 0xa2, 0xf4, + 0xbd, 0x5f, 0x2d, 0x4e, 0xfc, 0xec, 0x57, 0x8b, 0x13, 0xbf, 0xf8, 0xd5, 0xe2, 0xc4, 0x27, 0x21, + 0x2e, 0x03, 0xeb, 0x21, 0x16, 0xe1, 0x7b, 0xeb, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x77, + 0x4e, 0xe2, 0x36, 0x4d, 0x00, 0x00, } func (this *BackupEncryptionOptions) Equal(that interface{}) bool { @@ -3849,6 +3961,74 @@ func (m *StreamIngestionProgress_PartitionProgress) MarshalToSizedBuffer(dAtA [] return len(dAtA) - i, nil } +func (m *StreamReplicationDetails) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StreamReplicationDetails) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StreamReplicationDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Spans) > 0 { + for iNdEx := len(m.Spans) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Spans[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintJobs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *StreamReplicationProgress) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StreamReplicationProgress) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StreamReplicationProgress) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expiration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration):]) + if err7 != nil { + return 0, err7 + } + i -= n7 + i = encodeVarintJobs(dAtA, i, uint64(n7)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *SchedulePTSChainingRecord) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5104,21 +5284,21 @@ func (m *ImportProgress) MarshalToSizedBuffer(dAtA []byte) (int, error) { } } if len(m.ResumePos) > 0 { - dAtA24 := make([]byte, len(m.ResumePos)*10) - var j23 int + dAtA25 := make([]byte, len(m.ResumePos)*10) + var j24 int for _, num1 := range m.ResumePos { num := uint64(num1) for num >= 1<<7 { - dAtA24[j23] = uint8(uint64(num)&0x7f | 0x80) + dAtA25[j24] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j23++ + j24++ } - dAtA24[j23] = uint8(num) - j23++ + dAtA25[j24] = uint8(num) + j24++ } - i -= j23 - copy(dAtA[i:], dAtA24[:j23]) - i = encodeVarintJobs(dAtA, i, uint64(j23)) + i -= j24 + copy(dAtA[i:], dAtA25[:j24]) + i = encodeVarintJobs(dAtA, i, uint64(j24)) i-- dAtA[i] = 0x2a } @@ -5138,9 +5318,9 @@ func (m *ImportProgress) MarshalToSizedBuffer(dAtA []byte) (int, error) { } if len(m.WriteProgress) > 0 { for iNdEx := len(m.WriteProgress) - 1; iNdEx >= 0; iNdEx-- { - f25 := math.Float32bits(float32(m.WriteProgress[iNdEx])) + f26 := math.Float32bits(float32(m.WriteProgress[iNdEx])) i -= 4 - encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(f25)) + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(f26)) } i = encodeVarintJobs(dAtA, i, uint64(len(m.WriteProgress)*4)) i-- @@ -5148,9 +5328,9 @@ func (m *ImportProgress) MarshalToSizedBuffer(dAtA []byte) (int, error) { } if len(m.ReadProgress) > 0 { for iNdEx := len(m.ReadProgress) - 1; iNdEx >= 0; iNdEx-- { - f26 := math.Float32bits(float32(m.ReadProgress[iNdEx])) + f27 := math.Float32bits(float32(m.ReadProgress[iNdEx])) i -= 4 - encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(f26)) + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(f27)) } i = encodeVarintJobs(dAtA, i, uint64(len(m.ReadProgress)*4)) i-- @@ -5158,9 +5338,9 @@ func (m *ImportProgress) MarshalToSizedBuffer(dAtA []byte) (int, error) { } if len(m.SamplingProgress) > 0 { for iNdEx := len(m.SamplingProgress) - 1; iNdEx >= 0; iNdEx-- { - f27 := math.Float32bits(float32(m.SamplingProgress[iNdEx])) + f28 := math.Float32bits(float32(m.SamplingProgress[iNdEx])) i -= 4 - encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(f27)) + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(f28)) } i = encodeVarintJobs(dAtA, i, uint64(len(m.SamplingProgress)*4)) i-- @@ -5287,20 +5467,20 @@ func (m *NewSchemaChangeProgress) MarshalToSizedBuffer(dAtA []byte) (int, error) var l int _ = l if len(m.States) > 0 { - dAtA29 := make([]byte, len(m.States)*10) - var j28 int + dAtA30 := make([]byte, len(m.States)*10) + var j29 int for _, num := range m.States { for num >= 1<<7 { - dAtA29[j28] = uint8(uint64(num)&0x7f | 0x80) + dAtA30[j29] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j28++ + j29++ } - dAtA29[j28] = uint8(num) - j28++ + dAtA30[j29] = uint8(num) + j29++ } - i -= j28 - copy(dAtA[i:], dAtA29[:j28]) - i = encodeVarintJobs(dAtA, i, uint64(j28)) + i -= j29 + copy(dAtA[i:], dAtA30[:j29]) + i = encodeVarintJobs(dAtA, i, uint64(j29)) i-- dAtA[i] = 0xa } @@ -5654,38 +5834,38 @@ func (m *SchemaChangeDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x52 if len(m.DroppedSchemas) > 0 { - dAtA34 := make([]byte, len(m.DroppedSchemas)*10) - var j33 int + dAtA35 := make([]byte, len(m.DroppedSchemas)*10) + var j34 int for _, num := range m.DroppedSchemas { for num >= 1<<7 { - dAtA34[j33] = uint8(uint64(num)&0x7f | 0x80) + dAtA35[j34] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j33++ + j34++ } - dAtA34[j33] = uint8(num) - j33++ + dAtA35[j34] = uint8(num) + j34++ } - i -= j33 - copy(dAtA[i:], dAtA34[:j33]) - i = encodeVarintJobs(dAtA, i, uint64(j33)) + i -= j34 + copy(dAtA[i:], dAtA35[:j34]) + i = encodeVarintJobs(dAtA, i, uint64(j34)) i-- dAtA[i] = 0x4a } if len(m.DroppedTypes) > 0 { - dAtA36 := make([]byte, len(m.DroppedTypes)*10) - var j35 int + dAtA37 := make([]byte, len(m.DroppedTypes)*10) + var j36 int for _, num := range m.DroppedTypes { for num >= 1<<7 { - dAtA36[j35] = uint8(uint64(num)&0x7f | 0x80) + dAtA37[j36] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j35++ + j36++ } - dAtA36[j35] = uint8(num) - j35++ + dAtA37[j36] = uint8(num) + j36++ } - i -= j35 - copy(dAtA[i:], dAtA36[:j35]) - i = encodeVarintJobs(dAtA, i, uint64(j35)) + i -= j36 + copy(dAtA[i:], dAtA37[:j36]) + i = encodeVarintJobs(dAtA, i, uint64(j36)) i-- dAtA[i] = 0x42 } @@ -6340,20 +6520,20 @@ func (m *CreateStatsDetails_ColStat) MarshalToSizedBuffer(dAtA []byte) (int, err dAtA[i] = 0x10 } if len(m.ColumnIDs) > 0 { - dAtA46 := make([]byte, len(m.ColumnIDs)*10) - var j45 int + dAtA47 := make([]byte, len(m.ColumnIDs)*10) + var j46 int for _, num := range m.ColumnIDs { for num >= 1<<7 { - dAtA46[j45] = uint8(uint64(num)&0x7f | 0x80) + dAtA47[j46] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j45++ + j46++ } - dAtA46[j45] = uint8(num) - j45++ + dAtA47[j46] = uint8(num) + j46++ } - i -= j45 - copy(dAtA[i:], dAtA46[:j45]) - i = encodeVarintJobs(dAtA, i, uint64(j45)) + i -= j46 + copy(dAtA[i:], dAtA47[:j46]) + i = encodeVarintJobs(dAtA, i, uint64(j46)) i-- dAtA[i] = 0xa } @@ -6514,6 +6694,15 @@ func (m *Payload) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Details != nil { + { + size := m.Details.Size() + i -= size + if _, err := m.Details.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } if len(m.RetriableExecutionFailureLog) > 0 { for iNdEx := len(m.RetriableExecutionFailureLog) - 1; iNdEx >= 0; iNdEx-- { { @@ -6530,15 +6719,6 @@ func (m *Payload) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x82 } } - if m.Details != nil { - { - size := m.Details.Size() - i -= size - if _, err := m.Details.MarshalTo(dAtA[i:]); err != nil { - return 0, err - } - } - } if len(m.PauseReason) > 0 { i -= len(m.PauseReason) copy(dAtA[i:], m.PauseReason) @@ -6625,20 +6805,20 @@ func (m *Payload) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x42 } if len(m.DescriptorIDs) > 0 { - dAtA50 := make([]byte, len(m.DescriptorIDs)*10) - var j49 int + dAtA51 := make([]byte, len(m.DescriptorIDs)*10) + var j50 int for _, num := range m.DescriptorIDs { for num >= 1<<7 { - dAtA50[j49] = uint8(uint64(num)&0x7f | 0x80) + dAtA51[j50] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j49++ + j50++ } - dAtA50[j49] = uint8(num) - j49++ + dAtA51[j50] = uint8(num) + j50++ } - i -= j49 - copy(dAtA[i:], dAtA50[:j49]) - i = encodeVarintJobs(dAtA, i, uint64(j49)) + i -= j50 + copy(dAtA[i:], dAtA51[:j50]) + i = encodeVarintJobs(dAtA, i, uint64(j50)) i-- dAtA[i] = 0x32 } @@ -6956,6 +7136,29 @@ func (m *Payload_AutoSQLStatsCompaction) MarshalToSizedBuffer(dAtA []byte) (int, } return len(dAtA) - i, nil } +func (m *Payload_StreamReplication) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Payload_StreamReplication) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.StreamReplication != nil { + { + size, err := m.StreamReplication.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintJobs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x8a + } + return len(dAtA) - i, nil +} func (m *Progress) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7337,6 +7540,29 @@ func (m *Progress_AutoSQLStatsCompaction) MarshalToSizedBuffer(dAtA []byte) (int } return len(dAtA) - i, nil } +func (m *Progress_StreamReplication) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Progress_StreamReplication) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.StreamReplication != nil { + { + size, err := m.StreamReplication.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintJobs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc2 + } + return len(dAtA) - i, nil +} func (m *Job) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7583,6 +7809,32 @@ func (m *StreamIngestionProgress_PartitionProgress) Size() (n int) { return n } +func (m *StreamReplicationDetails) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Spans) > 0 { + for _, e := range m.Spans { + l = e.Size() + n += 1 + l + sovJobs(uint64(l)) + } + } + return n +} + +func (m *StreamReplicationProgress) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration) + n += 1 + l + sovJobs(uint64(l)) + return n +} + func (m *SchedulePTSChainingRecord) Size() (n int) { if m == nil { return 0 @@ -8867,6 +9119,18 @@ func (m *Payload_AutoSQLStatsCompaction) Size() (n int) { } return n } +func (m *Payload_StreamReplication) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StreamReplication != nil { + l = m.StreamReplication.Size() + n += 2 + l + sovJobs(uint64(l)) + } + return n +} func (m *Progress) Size() (n int) { if m == nil { return 0 @@ -9069,6 +9333,18 @@ func (m *Progress_AutoSQLStatsCompaction) Size() (n int) { } return n } +func (m *Progress_StreamReplication) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.StreamReplication != nil { + l = m.StreamReplication.Size() + n += 2 + l + sovJobs(uint64(l)) + } + return n +} func (m *Job) Size() (n int) { if m == nil { return 0 @@ -10092,6 +10368,173 @@ func (m *StreamIngestionProgress_PartitionProgress) Unmarshal(dAtA []byte) error } return nil } +func (m *StreamReplicationDetails) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowJobs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StreamReplicationDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StreamReplicationDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spans", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowJobs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthJobs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthJobs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Spans = append(m.Spans, &roachpb.Span{}) + if err := m.Spans[len(m.Spans)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipJobs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthJobs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StreamReplicationProgress) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowJobs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StreamReplicationProgress: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StreamReplicationProgress: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Expiration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowJobs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthJobs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthJobs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expiration, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipJobs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthJobs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *SchedulePTSChainingRecord) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -18459,6 +18902,41 @@ func (m *Payload) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 33: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StreamReplication", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowJobs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthJobs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthJobs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &StreamReplicationDetails{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Details = &Payload_StreamReplication{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipJobs(dAtA[iNdEx:]) @@ -19080,6 +19558,41 @@ func (m *Progress) Unmarshal(dAtA []byte) error { } m.Details = &Progress_AutoSQLStatsCompaction{v} iNdEx = postIndex + case 24: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StreamReplication", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowJobs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthJobs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthJobs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &StreamReplicationProgress{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Details = &Progress_StreamReplication{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipJobs(dAtA[iNdEx:]) diff --git a/pkg/jobs/jobspb/jobs.proto b/pkg/jobs/jobspb/jobs.proto index f5b2d37da465..0cba181da776 100644 --- a/pkg/jobs/jobspb/jobs.proto +++ b/pkg/jobs/jobspb/jobs.proto @@ -22,6 +22,7 @@ import "sql/catalog/descpb/tenant.proto"; import "util/hlc/timestamp.proto"; import "sql/schemachanger/scpb/scpb.proto"; import "clusterversion/cluster_version.proto"; +import "google/protobuf/timestamp.proto"; enum EncryptionMode { Passphrase = 0; @@ -105,6 +106,16 @@ message StreamIngestionProgress { } +message StreamReplicationDetails { + // Key spans we are replicating + repeated roachpb.Span spans = 1; +} + +message StreamReplicationProgress { + // Expiration timestamp of consumer heartbeat + google.protobuf.Timestamp expiration = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; +} + message SchedulePTSChainingRecord { enum PTSAction { UPDATE = 0; @@ -785,6 +796,7 @@ message Payload { MigrationDetails migration = 25; AutoSpanConfigReconciliationDetails autoSpanConfigReconciliation = 27; AutoSQLStatsCompactionDetails autoSQLStatsCompaction = 30; + StreamReplicationDetails streamReplication = 33; } reserved 26; // PauseReason is used to describe the reason that the job is currently paused @@ -796,7 +808,7 @@ message Payload { // the jobs.execution_errors.max_entries cluster setting. repeated RetriableExecutionFailure retriable_execution_failure_log = 32; - // NEXT ID: 33. + // NEXT ID: 34. } message Progress { @@ -821,6 +833,7 @@ message Progress { MigrationProgress migration = 20; AutoSpanConfigReconciliationProgress AutoSpanConfigReconciliation = 22; AutoSQLStatsCompactionProgress autoSQLStatsCompaction = 23; + StreamReplicationProgress streamReplication = 24; } uint64 trace_id = 21 [(gogoproto.nullable) = false, (gogoproto.customname) = "TraceID", (gogoproto.customtype) = "github.com/cockroachdb/cockroach/pkg/util/tracing/tracingpb.TraceID"]; @@ -847,6 +860,7 @@ enum Type { MIGRATION = 12 [(gogoproto.enumvalue_customname) = "TypeMigration"]; AUTO_SPAN_CONFIG_RECONCILIATION = 13 [(gogoproto.enumvalue_customname) = "TypeAutoSpanConfigReconciliation"]; AUTO_SQL_STATS_COMPACTION = 14 [(gogoproto.enumvalue_customname) = "TypeAutoSQLStatsCompaction"]; + STREAM_REPLICATION = 15 [(gogoproto.enumvalue_customname) = "TypeStreamReplication"]; } message Job { diff --git a/pkg/jobs/jobspb/wrap.go b/pkg/jobs/jobspb/wrap.go index 3720c4f28092..03bce66df7bd 100644 --- a/pkg/jobs/jobspb/wrap.go +++ b/pkg/jobs/jobspb/wrap.go @@ -46,6 +46,7 @@ var _ Details = NewSchemaChangeDetails{} var _ Details = MigrationDetails{} var _ Details = AutoSpanConfigReconciliationDetails{} var _ Details = ImportDetails{} +var _ Details = StreamReplicationDetails{} // ProgressDetails is a marker interface for job progress details proto structs. type ProgressDetails interface{} @@ -60,6 +61,7 @@ var _ ProgressDetails = StreamIngestionProgress{} var _ ProgressDetails = NewSchemaChangeProgress{} var _ ProgressDetails = MigrationProgress{} var _ ProgressDetails = AutoSpanConfigReconciliationDetails{} +var _ ProgressDetails = StreamReplicationProgress{} // Type returns the payload's job type. func (p *Payload) Type() Type { @@ -119,6 +121,8 @@ func DetailsType(d isPayload_Details) Type { return TypeAutoSpanConfigReconciliation case *Payload_AutoSQLStatsCompaction: return TypeAutoSQLStatsCompaction + case *Payload_StreamReplication: + return TypeStreamReplication default: panic(errors.AssertionFailedf("Payload.Type called on a payload with an unknown details type: %T", d)) } @@ -159,6 +163,8 @@ func WrapProgressDetails(details ProgressDetails) interface { return &Progress_AutoSpanConfigReconciliation{AutoSpanConfigReconciliation: &d} case AutoSQLStatsCompactionProgress: return &Progress_AutoSQLStatsCompaction{AutoSQLStatsCompaction: &d} + case StreamReplicationProgress: + return &Progress_StreamReplication{StreamReplication: &d} default: panic(errors.AssertionFailedf("WrapProgressDetails: unknown details type %T", d)) } @@ -194,6 +200,8 @@ func (p *Payload) UnwrapDetails() Details { return *d.AutoSpanConfigReconciliation case *Payload_AutoSQLStatsCompaction: return *d.AutoSQLStatsCompaction + case *Payload_StreamReplication: + return *d.StreamReplication default: return nil } @@ -229,6 +237,8 @@ func (p *Progress) UnwrapDetails() ProgressDetails { return *d.AutoSpanConfigReconciliation case *Progress_AutoSQLStatsCompaction: return *d.AutoSQLStatsCompaction + case *Progress_StreamReplication: + return *d.StreamReplication default: return nil } @@ -277,6 +287,8 @@ func WrapPayloadDetails(details Details) interface { return &Payload_AutoSpanConfigReconciliation{AutoSpanConfigReconciliation: &d} case AutoSQLStatsCompactionDetails: return &Payload_AutoSQLStatsCompaction{AutoSQLStatsCompaction: &d} + case StreamReplicationDetails: + return &Payload_StreamReplication{StreamReplication: &d} default: panic(errors.AssertionFailedf("jobs.WrapPayloadDetails: unknown details type %T", d)) } @@ -312,7 +324,7 @@ const ( func (Type) SafeValue() {} // NumJobTypes is the number of jobs types. -const NumJobTypes = 15 +const NumJobTypes = 16 // MarshalJSONPB implements jsonpb.JSONPBMarshaller to redact sensitive sink URI // parameters from ChangefeedDetails. diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index 806d9f63716c..3d60d6a3f5f9 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -3307,7 +3307,7 @@ type PrivilegedAccessor interface { ctx context.Context, parentID int64, name string, ) (DInt, bool, error) - // LookupZoneConfig returns the zone config given a namespace id. + // LookupZoneConfigByNamespaceID returns the zone config given a namespace id. // It is meant as a replacement for looking up system.zones directly. // Returns the config byte array, a bool representing whether the namespace exists, // and an error if there is one. diff --git a/pkg/ts/catalog/chart_catalog.go b/pkg/ts/catalog/chart_catalog.go index 5b57da403df8..0b60d364e88c 100644 --- a/pkg/ts/catalog/chart_catalog.go +++ b/pkg/ts/catalog/chart_catalog.go @@ -2593,6 +2593,7 @@ var charts = []sectionDescription{ "jobs.migration.currently_running", "jobs.auto_span_config_reconciliation.currently_running", "jobs.auto_sql_stats_compaction.currently_running", + "jobs.stream_replication.currently_running", }, }, { @@ -2726,6 +2727,17 @@ var charts = []sectionDescription{ "jobs.stream_ingestion.resume_retry_error", }, }, + { + Title: "Stream Replication", + Metrics: []string{ + "jobs.stream_replication.fail_or_cancel_completed", + "jobs.stream_replication.fail_or_cancel_failed", + "jobs.stream_replication.fail_or_cancel_retry_error", + "jobs.stream_replication.resume_completed", + "jobs.stream_replication.resume_failed", + "jobs.stream_replication.resume_retry_error", + }, + }, { Title: "Long Running Migrations", Metrics: []string{ From c9882b56ead07d3f045068557749619fcbfc38fb Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Tue, 2 Nov 2021 09:17:24 +0100 Subject: [PATCH 161/205] kvserver: remove unused snapshot log entry code We used to have to send the raft log along with snapshots as a result of (in the olden days) requiring all replicas to agree on the truncated state. This hasn't been (generally) true as of v19.1 (#35701), though it was still a possibility until v22.1 (#58088). This commit removes the corresponding code. Release note: None --- pkg/kv/kvserver/raft.pb.go | 214 +++++++++------------- pkg/kv/kvserver/raft.proto | 7 +- pkg/kv/kvserver/replica_raftstorage.go | 73 +------- pkg/kv/kvserver/replica_sideload_test.go | 219 ----------------------- pkg/kv/kvserver/store_snapshot.go | 129 +------------ 5 files changed, 87 insertions(+), 555 deletions(-) diff --git a/pkg/kv/kvserver/raft.pb.go b/pkg/kv/kvserver/raft.pb.go index 5135d28b9ca3..8a643561373c 100644 --- a/pkg/kv/kvserver/raft.pb.go +++ b/pkg/kv/kvserver/raft.pb.go @@ -385,11 +385,7 @@ type SnapshotRequest struct { Header *SnapshotRequest_Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` // A RocksDB BatchRepr. Multiple kv_batches may be sent across multiple request messages. KVBatch []byte `protobuf:"bytes,2,opt,name=kv_batch,json=kvBatch,proto3" json:"kv_batch,omitempty"` - // These are really raftpb.Entry, but we model them as raw bytes to avoid - // roundtripping through memory. They are separate from the kv_batch to - // allow flexibility in log implementations. - LogEntries [][]byte `protobuf:"bytes,3,rep,name=log_entries,json=logEntries,proto3" json:"log_entries,omitempty"` - Final bool `protobuf:"varint,4,opt,name=final,proto3" json:"final,omitempty"` + Final bool `protobuf:"varint,4,opt,name=final,proto3" json:"final,omitempty"` } func (m *SnapshotRequest) Reset() { *m = SnapshotRequest{} } @@ -570,87 +566,86 @@ func init() { func init() { proto.RegisterFile("kv/kvserver/raft.proto", fileDescriptor_acdcf79fd972c844) } var fileDescriptor_acdcf79fd972c844 = []byte{ - // 1280 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0xdf, 0x6e, 0x1a, 0x47, - 0x17, 0x67, 0xcd, 0x02, 0xcb, 0x60, 0xe2, 0xfd, 0xe6, 0xf3, 0xe7, 0x6f, 0x6b, 0xb5, 0xe0, 0xae, - 0xd2, 0xd6, 0xfd, 0xb7, 0x28, 0x4e, 0xda, 0x8b, 0xde, 0x54, 0x80, 0x89, 0x8c, 0x71, 0x6c, 0x32, - 0x60, 0x57, 0x6d, 0xd5, 0xae, 0x16, 0x18, 0x60, 0x05, 0xec, 0x6c, 0x66, 0x07, 0x5a, 0xf2, 0x14, - 0x95, 0xfa, 0x02, 0xbd, 0xeb, 0x53, 0xf4, 0xde, 0x97, 0xb9, 0xcc, 0x45, 0x85, 0x5a, 0x72, 0xdd, - 0x17, 0xc8, 0x55, 0x35, 0xb3, 0xb3, 0x40, 0x9c, 0x38, 0x0d, 0x52, 0x23, 0xb5, 0x57, 0x3e, 0x33, - 0x9c, 0xf3, 0xfb, 0xcd, 0x9c, 0xf3, 0x3b, 0x67, 0xd6, 0x60, 0x67, 0x30, 0x29, 0x0c, 0x26, 0x01, - 0xa6, 0x13, 0x4c, 0x0b, 0xd4, 0xe9, 0x32, 0xcb, 0xa7, 0x84, 0x11, 0xf8, 0xbf, 0x36, 0x69, 0x0f, - 0x28, 0x71, 0xda, 0x7d, 0x6b, 0x30, 0xb1, 0x22, 0x8f, 0xdd, 0x6d, 0xb1, 0xe5, 0xb7, 0x0a, 0x98, - 0x52, 0x42, 0x83, 0xd0, 0x79, 0x77, 0x27, 0xda, 0x1d, 0x61, 0xe6, 0x74, 0x1c, 0xe6, 0xc8, 0x7d, - 0x6b, 0x15, 0x7c, 0xe8, 0x4e, 0xb0, 0x87, 0x83, 0x60, 0x61, 0xf8, 0xad, 0x85, 0x29, 0xfd, 0xcd, - 0x55, 0xff, 0xc8, 0xf0, 0x5b, 0x85, 0x80, 0x39, 0x0c, 0x4b, 0x9f, 0x1c, 0x66, 0xed, 0x8e, 0x38, - 0x69, 0x61, 0x72, 0x5b, 0xfc, 0xf5, 0x5b, 0x2b, 0x07, 0xdf, 0xdd, 0xee, 0x91, 0x1e, 0x11, 0x66, - 0x81, 0x5b, 0xe1, 0xae, 0xf9, 0x87, 0x0a, 0xb2, 0xc8, 0xe9, 0xb2, 0x23, 0xec, 0x50, 0xd6, 0xc2, - 0x0e, 0x83, 0xdf, 0x02, 0x8d, 0x3a, 0x5e, 0x0f, 0xdb, 0x6e, 0xc7, 0x50, 0xf6, 0x94, 0x7d, 0xb5, - 0x54, 0x9e, 0xcf, 0xf2, 0x29, 0xc4, 0xf7, 0xaa, 0x87, 0x4f, 0x67, 0xf9, 0x3b, 0x3d, 0x97, 0xf5, - 0xc7, 0x2d, 0xab, 0x4d, 0x46, 0x85, 0x45, 0x32, 0x3a, 0xad, 0xa5, 0x5d, 0xf0, 0x07, 0xbd, 0x82, - 0xbc, 0xb9, 0x25, 0xe3, 0x50, 0x4a, 0x80, 0x56, 0x3b, 0x30, 0x00, 0x5b, 0x5d, 0x4a, 0x46, 0x36, - 0xc5, 0xfe, 0xd0, 0x6d, 0x3b, 0x9c, 0x66, 0x63, 0x4f, 0xd9, 0xcf, 0x96, 0x6a, 0xf3, 0x59, 0x3e, - 0x7b, 0x97, 0x92, 0x11, 0x0a, 0x7f, 0x11, 0x64, 0x9f, 0xae, 0x47, 0x16, 0x45, 0xa2, 0x6c, 0x77, - 0x05, 0xa8, 0x03, 0x47, 0x20, 0xcb, 0xc8, 0x2a, 0x65, 0x5c, 0x50, 0x56, 0xe7, 0xb3, 0x7c, 0xa6, - 0x49, 0xfe, 0x0e, 0xc2, 0x0c, 0x23, 0x4b, 0x3a, 0x08, 0x54, 0x86, 0xe9, 0xc8, 0x50, 0x79, 0xfe, - 0x90, 0xb0, 0xe1, 0x0e, 0x48, 0xb6, 0xc9, 0x68, 0xe4, 0x32, 0x23, 0x21, 0x76, 0xe5, 0x0a, 0x1a, - 0x20, 0xf5, 0x60, 0xec, 0xe2, 0xa0, 0x8d, 0x8d, 0xe4, 0x9e, 0xb2, 0xaf, 0xa1, 0x68, 0x09, 0x1f, - 0x82, 0x37, 0x87, 0x4e, 0xaf, 0xe7, 0x7a, 0x3d, 0xbb, 0x4b, 0x86, 0x43, 0xf2, 0x1d, 0xa6, 0x81, - 0x4d, 0x3c, 0x3b, 0x72, 0xd7, 0xf6, 0xe2, 0xfb, 0x99, 0x83, 0xdb, 0xd6, 0x0b, 0x15, 0x69, 0x2d, - 0x24, 0xb4, 0x94, 0x95, 0x75, 0x22, 0xcd, 0x92, 0x7a, 0x39, 0xcb, 0xc7, 0xd0, 0x1b, 0x12, 0xfe, - 0x6e, 0x84, 0x7e, 0xe6, 0xdd, 0x97, 0xdc, 0x75, 0xf0, 0xce, 0xcb, 0xb8, 0x6d, 0xa7, 0xdd, 0x1e, - 0x53, 0x87, 0x61, 0x03, 0x88, 0x33, 0xbf, 0x7d, 0x2d, 0x52, 0x51, 0x3a, 0x1e, 0xab, 0x5a, 0x4a, - 0xd7, 0xcc, 0x9f, 0x93, 0x00, 0x72, 0xbd, 0xdd, 0xc3, 0x41, 0xe0, 0xf4, 0x30, 0xc2, 0x0f, 0xc6, - 0x38, 0x78, 0xfd, 0xa2, 0xfb, 0x06, 0x6c, 0x85, 0xf8, 0x01, 0x73, 0x28, 0xb3, 0x07, 0x78, 0x6a, - 0x68, 0x7b, 0xca, 0xfe, 0x66, 0xe9, 0x93, 0xa7, 0xb3, 0xfc, 0xad, 0xf5, 0xb0, 0x6b, 0x78, 0x8a, - 0xb2, 0x02, 0xad, 0xc1, 0xc1, 0x6a, 0x78, 0x0a, 0xef, 0x81, 0xcd, 0x55, 0x4d, 0x0b, 0x41, 0x67, - 0x0e, 0x6e, 0xae, 0x54, 0xe6, 0x8a, 0x60, 0x0e, 0x71, 0xd0, 0xa6, 0xae, 0xcf, 0x08, 0x95, 0xa5, - 0xc8, 0xac, 0xe8, 0x15, 0x56, 0x01, 0x58, 0xaa, 0x55, 0x48, 0x75, 0x3d, 0xb0, 0xf4, 0x42, 0x8b, - 0xb0, 0x00, 0x52, 0xa3, 0x30, 0xd5, 0x42, 0x8c, 0x99, 0x83, 0x2d, 0x2b, 0x1c, 0x0d, 0x96, 0xac, - 0x80, 0x0c, 0x89, 0xbc, 0x56, 0xe5, 0x98, 0x58, 0x4f, 0x8e, 0xe9, 0x7f, 0x93, 0x1c, 0xe1, 0x31, - 0x00, 0xfd, 0x68, 0xe6, 0x05, 0x46, 0x52, 0x9c, 0xfd, 0xe6, 0x35, 0x67, 0x7f, 0x66, 0x40, 0xca, - 0xc3, 0xae, 0x44, 0xc3, 0x06, 0xd8, 0x5a, 0xac, 0x6c, 0x8a, 0x03, 0x3f, 0x30, 0x52, 0x6b, 0x03, - 0xde, 0x58, 0x40, 0x20, 0x8e, 0x60, 0x76, 0xc1, 0xff, 0x9f, 0x6f, 0x94, 0x92, 0xc3, 0xda, 0x7d, - 0x58, 0x03, 0x1a, 0x0d, 0xd7, 0x81, 0xa1, 0x08, 0xa2, 0xf7, 0x5f, 0x42, 0x74, 0x05, 0x21, 0x64, - 0x5b, 0x00, 0x98, 0x75, 0x60, 0x3c, 0xe3, 0x15, 0xf8, 0xc4, 0x0b, 0xf0, 0xb9, 0xe7, 0x12, 0x0f, - 0x5a, 0x20, 0x21, 0xde, 0x33, 0xd1, 0x93, 0x99, 0x03, 0xe3, 0x2a, 0x8b, 0xdf, 0xb2, 0x2a, 0xfc, - 0x77, 0x14, 0xba, 0x7d, 0xa6, 0x5e, 0xfe, 0x94, 0x57, 0xcc, 0x5f, 0x37, 0xc0, 0x7f, 0x5f, 0x00, - 0xf9, 0xda, 0x9b, 0xfc, 0x9f, 0xdb, 0x85, 0x35, 0x90, 0x18, 0xf3, 0x84, 0xca, 0x1e, 0x2c, 0xbc, - 0x4a, 0xb5, 0x56, 0xea, 0x20, 0x01, 0x43, 0x0c, 0xf3, 0x51, 0x12, 0x6c, 0x35, 0x3c, 0xc7, 0x0f, - 0xfa, 0x84, 0x45, 0xf3, 0xb3, 0x02, 0x92, 0x7d, 0xec, 0x74, 0x70, 0x54, 0xa9, 0x8f, 0xaf, 0x61, - 0xb8, 0x12, 0x67, 0x1d, 0x89, 0x20, 0x24, 0x83, 0xe1, 0xbb, 0x40, 0x1b, 0x4c, 0xec, 0x16, 0x17, - 0x99, 0xc8, 0xde, 0x66, 0x29, 0xc3, 0x2b, 0x54, 0xbb, 0x10, 0xba, 0x43, 0xa9, 0xc1, 0x24, 0x14, - 0x60, 0x1e, 0x64, 0x86, 0xa4, 0x67, 0x63, 0x8f, 0x51, 0x17, 0x07, 0x46, 0x7c, 0x2f, 0xbe, 0xbf, - 0x89, 0xc0, 0x90, 0xf4, 0x2a, 0xe1, 0x0e, 0xdc, 0x06, 0x89, 0xae, 0xeb, 0x39, 0x43, 0x71, 0x61, - 0x0d, 0x85, 0x8b, 0xdd, 0x1f, 0x55, 0x90, 0x0c, 0x19, 0x61, 0x15, 0x24, 0xc4, 0xc7, 0x8b, 0x18, - 0x32, 0xd7, 0x9f, 0x37, 0x60, 0x84, 0x3a, 0x3d, 0xbc, 0xcc, 0x72, 0x83, 0x07, 0x45, 0xf9, 0x10, - 0x08, 0xd0, 0x01, 0xdb, 0x7c, 0xa4, 0xd9, 0x72, 0x82, 0xd9, 0x52, 0xd9, 0xb2, 0xfc, 0x6b, 0x77, - 0x06, 0xa4, 0xcf, 0x3f, 0x4f, 0x6f, 0x01, 0x20, 0x9f, 0x0f, 0xf7, 0x21, 0x16, 0x52, 0x88, 0xa3, - 0x74, 0xf8, 0x04, 0xb8, 0x0f, 0x31, 0xef, 0x47, 0x9f, 0xba, 0x84, 0xba, 0x6c, 0x2a, 0xde, 0xf0, - 0x1b, 0xd7, 0x56, 0xf8, 0x6a, 0xfe, 0xeb, 0x32, 0x0c, 0x2d, 0x00, 0x38, 0x58, 0xc0, 0xf8, 0x88, - 0xea, 0x4d, 0x8d, 0xd4, 0x5a, 0x60, 0x0d, 0x19, 0x86, 0x16, 0x00, 0xf0, 0x73, 0xa0, 0xb2, 0xa9, - 0xcf, 0x67, 0x33, 0x07, 0xfa, 0xf0, 0x15, 0x81, 0x9a, 0x53, 0x1f, 0x23, 0x11, 0x08, 0xcf, 0xc1, - 0x7b, 0x1d, 0xec, 0x53, 0xdc, 0x76, 0x18, 0xee, 0xd8, 0x63, 0x4f, 0xb6, 0x03, 0x5f, 0x30, 0x3a, - 0xf6, 0x42, 0x2b, 0xac, 0xa4, 0x26, 0x4a, 0x7d, 0x73, 0xe9, 0x7e, 0xbe, 0xe2, 0xdd, 0x8c, 0x9c, - 0x45, 0x01, 0x8f, 0x55, 0x4d, 0xd1, 0x37, 0x8e, 0x55, 0x4d, 0xd5, 0x13, 0xe6, 0x1d, 0xa0, 0x45, - 0x69, 0x80, 0x19, 0x90, 0x3a, 0x3f, 0xad, 0x9d, 0x9e, 0x7d, 0x71, 0xaa, 0xc7, 0xe0, 0x26, 0xd0, - 0x50, 0xa5, 0x7c, 0x76, 0x51, 0x41, 0x5f, 0xea, 0x0a, 0xcc, 0x82, 0x34, 0xaa, 0x94, 0x8a, 0x27, - 0xc5, 0xd3, 0x72, 0x45, 0xdf, 0x30, 0x0d, 0xa0, 0x45, 0xf7, 0xe5, 0x8e, 0xb5, 0x0b, 0xbb, 0x54, - 0x6c, 0x96, 0x8f, 0xf4, 0x98, 0x79, 0x0b, 0xa8, 0xfc, 0x02, 0x70, 0x07, 0xc0, 0x8b, 0x6a, 0xd1, - 0x6e, 0x9c, 0x16, 0xeb, 0x8d, 0xa3, 0xb3, 0xa6, 0x7d, 0xff, 0xbc, 0x72, 0x5e, 0xd1, 0x63, 0x9c, - 0xa3, 0x7a, 0x5a, 0x6d, 0x56, 0x8b, 0x27, 0xba, 0x62, 0xaa, 0xda, 0x86, 0xbe, 0x61, 0xfe, 0xa2, - 0x00, 0x7d, 0x99, 0x04, 0x39, 0xae, 0xee, 0x82, 0x24, 0xbf, 0xd8, 0x38, 0x10, 0x3d, 0x75, 0xe3, - 0xc0, 0xfa, 0xcb, 0xec, 0x85, 0x81, 0x56, 0x43, 0x44, 0x21, 0x19, 0xcd, 0x5f, 0xd4, 0xe8, 0x09, - 0xe6, 0x92, 0x4c, 0x2f, 0xde, 0x5a, 0xb3, 0x08, 0x92, 0xa1, 0xef, 0x73, 0xf7, 0x2e, 0x96, 0xcb, - 0x95, 0x7a, 0xb3, 0x72, 0xa8, 0x2b, 0xfc, 0xa7, 0x62, 0xbd, 0x7e, 0x52, 0xad, 0x1c, 0xea, 0x1b, - 0x30, 0x0d, 0x12, 0x15, 0x84, 0xce, 0x90, 0x1e, 0x37, 0x79, 0xf2, 0xd4, 0x63, 0x55, 0x8b, 0xeb, - 0xaa, 0xf9, 0x35, 0xf8, 0x4f, 0x99, 0x78, 0xdd, 0x72, 0x9f, 0x4b, 0xb2, 0x4c, 0x3c, 0x86, 0xbf, - 0x67, 0xf0, 0x23, 0x00, 0xf8, 0x27, 0xa6, 0xe3, 0x75, 0xa2, 0x81, 0x9b, 0x2e, 0x65, 0xe7, 0xb3, - 0x7c, 0xba, 0x1c, 0xee, 0x56, 0x0f, 0x51, 0x5a, 0x3a, 0x54, 0x3b, 0xfc, 0x94, 0xbe, 0x33, 0x1d, - 0x12, 0x27, 0xfc, 0x1c, 0xdf, 0x44, 0xd1, 0xb2, 0xf4, 0xc1, 0xe5, 0xef, 0xb9, 0xd8, 0xe5, 0x3c, - 0xa7, 0x3c, 0x9a, 0xe7, 0x94, 0xc7, 0xf3, 0x9c, 0xf2, 0xdb, 0x3c, 0xa7, 0xfc, 0xf0, 0x24, 0x17, - 0x7b, 0xf4, 0x24, 0x17, 0x7b, 0xfc, 0x24, 0x17, 0xfb, 0x4a, 0x8b, 0x72, 0xd1, 0x4a, 0x8a, 0xff, - 0x2a, 0x6e, 0xff, 0x19, 0x00, 0x00, 0xff, 0xff, 0x38, 0x9a, 0x07, 0xc0, 0x3e, 0x0d, 0x00, 0x00, + // 1257 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0xcf, 0x73, 0x22, 0xc5, + 0x17, 0x67, 0x92, 0x01, 0x86, 0x26, 0x6c, 0xe6, 0xdb, 0xdf, 0x18, 0xc7, 0x94, 0x42, 0x9c, 0x5a, + 0x35, 0xfe, 0x1a, 0x6a, 0xb3, 0xab, 0x07, 0x2f, 0x16, 0x10, 0xb6, 0x42, 0xc8, 0x26, 0x6c, 0x43, + 0x62, 0xa9, 0xa5, 0x53, 0x03, 0x34, 0x30, 0x05, 0x4c, 0xcf, 0xf6, 0x34, 0x28, 0xfb, 0x57, 0x58, + 0xe5, 0x3f, 0xe0, 0xcd, 0xbf, 0xc0, 0xa3, 0xf7, 0x1c, 0xf7, 0xb8, 0x07, 0x8b, 0x52, 0xf6, 0xec, + 0x3f, 0xb0, 0x27, 0xab, 0x7b, 0x7a, 0x80, 0x4d, 0x36, 0xeb, 0x52, 0xe5, 0x56, 0xe9, 0x29, 0xaf, + 0x9b, 0xcf, 0xfb, 0xbc, 0xd7, 0xef, 0x7d, 0xfa, 0xf5, 0x04, 0x6c, 0xf7, 0xc7, 0xf9, 0xfe, 0x38, + 0xc0, 0x74, 0x8c, 0x69, 0x9e, 0x3a, 0x1d, 0x66, 0xf9, 0x94, 0x30, 0x02, 0x5f, 0x6b, 0x91, 0x56, + 0x9f, 0x12, 0xa7, 0xd5, 0xb3, 0xfa, 0x63, 0x2b, 0x42, 0xec, 0x6c, 0x89, 0x2d, 0xbf, 0x99, 0xc7, + 0x94, 0x12, 0x1a, 0x84, 0xe0, 0x9d, 0xed, 0x68, 0x77, 0x88, 0x99, 0xd3, 0x76, 0x98, 0x23, 0xf7, + 0xad, 0x65, 0xf2, 0x81, 0x3b, 0xc6, 0x1e, 0x0e, 0x82, 0xb9, 0xe1, 0x37, 0xe7, 0xa6, 0xc4, 0x9b, + 0xcb, 0xf8, 0xc8, 0xf0, 0x9b, 0xf9, 0x80, 0x39, 0x0c, 0x4b, 0x4c, 0x16, 0xb3, 0x56, 0x5b, 0x64, + 0x9a, 0x1f, 0xdf, 0x16, 0x7f, 0xfd, 0xe6, 0x52, 0xe2, 0x3b, 0x5b, 0x5d, 0xd2, 0x25, 0xc2, 0xcc, + 0x73, 0x2b, 0xdc, 0x35, 0xff, 0x54, 0x41, 0x06, 0x39, 0x1d, 0x76, 0x88, 0x1d, 0xca, 0x9a, 0xd8, + 0x61, 0xf0, 0x5b, 0xa0, 0x51, 0xc7, 0xeb, 0x62, 0xdb, 0x6d, 0x1b, 0xca, 0xae, 0xb2, 0xa7, 0x16, + 0x4b, 0xb3, 0x69, 0x2e, 0x89, 0xf8, 0x5e, 0xe5, 0xe0, 0xe9, 0x34, 0x77, 0xa7, 0xeb, 0xb2, 0xde, + 0xa8, 0x69, 0xb5, 0xc8, 0x30, 0x3f, 0x2f, 0x46, 0xbb, 0xb9, 0xb0, 0xf3, 0x7e, 0xbf, 0x9b, 0x97, + 0x27, 0xb7, 0xa4, 0x1f, 0x4a, 0x0a, 0xd2, 0x4a, 0x1b, 0x06, 0x60, 0xb3, 0x43, 0xc9, 0xd0, 0xa6, + 0xd8, 0x1f, 0xb8, 0x2d, 0x87, 0x87, 0x59, 0xdb, 0x55, 0xf6, 0x32, 0xc5, 0xea, 0x6c, 0x9a, 0xcb, + 0xdc, 0xa5, 0x64, 0x88, 0xc2, 0x5f, 0x44, 0xb0, 0x4f, 0x57, 0x0b, 0x16, 0x79, 0xa2, 0x4c, 0x67, + 0x89, 0xa8, 0x0d, 0x87, 0x20, 0xc3, 0xc8, 0x72, 0xc8, 0x75, 0x11, 0xb2, 0x32, 0x9b, 0xe6, 0xd2, + 0x0d, 0xf2, 0x4f, 0x04, 0x4c, 0x33, 0xb2, 0x08, 0x07, 0x81, 0xca, 0x30, 0x1d, 0x1a, 0x2a, 0xaf, + 0x1f, 0x12, 0x36, 0xdc, 0x06, 0x89, 0x16, 0x19, 0x0e, 0x5d, 0x66, 0xc4, 0xc5, 0xae, 0x5c, 0x41, + 0x03, 0x24, 0x1f, 0x8c, 0x5c, 0x1c, 0xb4, 0xb0, 0x91, 0xd8, 0x55, 0xf6, 0x34, 0x14, 0x2d, 0xe1, + 0x43, 0xf0, 0xe6, 0xc0, 0xe9, 0x76, 0x5d, 0xaf, 0x6b, 0x77, 0xc8, 0x60, 0x40, 0xbe, 0xc3, 0x34, + 0xb0, 0x89, 0x67, 0x47, 0x70, 0x6d, 0x77, 0x7d, 0x2f, 0xbd, 0x7f, 0xdb, 0x7a, 0xae, 0x22, 0xad, + 0xb9, 0x84, 0x16, 0xb2, 0xb2, 0x8e, 0xa5, 0x59, 0x54, 0x2f, 0xa6, 0xb9, 0x18, 0x7a, 0x43, 0xd2, + 0xdf, 0x8d, 0xd8, 0x4f, 0xbd, 0xfb, 0x32, 0x76, 0x0d, 0xbc, 0xf3, 0xa2, 0xd8, 0xb6, 0xd3, 0x6a, + 0x8d, 0xa8, 0xc3, 0xb0, 0x01, 0x44, 0xce, 0x6f, 0x5f, 0xcb, 0x54, 0x90, 0xc0, 0x23, 0x55, 0x4b, + 0xea, 0x9a, 0xf9, 0x73, 0x02, 0x40, 0xae, 0xb7, 0x7b, 0x38, 0x08, 0x9c, 0x2e, 0x46, 0xf8, 0xc1, + 0x08, 0x07, 0xaf, 0x5e, 0x74, 0xdf, 0x80, 0xcd, 0x90, 0x3f, 0x60, 0x0e, 0x65, 0x76, 0x1f, 0x4f, + 0x0c, 0x6d, 0x57, 0xd9, 0xdb, 0x28, 0x7e, 0xf2, 0x74, 0x9a, 0xbb, 0xb5, 0x1a, 0x77, 0x15, 0x4f, + 0x50, 0x46, 0xb0, 0xd5, 0x39, 0x59, 0x15, 0x4f, 0xe0, 0x3d, 0xb0, 0xb1, 0xac, 0x69, 0x21, 0xe8, + 0xf4, 0xfe, 0xcd, 0xa5, 0xce, 0x5c, 0x12, 0xcc, 0x01, 0x0e, 0x5a, 0xd4, 0xf5, 0x19, 0xa1, 0xb2, + 0x15, 0xe9, 0x25, 0xbd, 0xc2, 0x0a, 0x00, 0x0b, 0xb5, 0x0a, 0xa9, 0xae, 0x46, 0x96, 0x9a, 0x6b, + 0x11, 0xe6, 0x41, 0x72, 0x18, 0x96, 0x5a, 0x88, 0x31, 0xbd, 0xbf, 0x69, 0x85, 0xa3, 0xc1, 0x92, + 0x1d, 0x90, 0x2e, 0x11, 0x6a, 0x59, 0x8e, 0xf1, 0xd5, 0xe4, 0x98, 0xfa, 0x2f, 0xc9, 0x11, 0x1e, + 0x01, 0xd0, 0x8b, 0x66, 0x5e, 0x60, 0x24, 0x44, 0xee, 0x37, 0xaf, 0xc9, 0xfd, 0x99, 0x01, 0x29, + 0x93, 0x5d, 0xf2, 0x86, 0x75, 0xb0, 0x39, 0x5f, 0xd9, 0x14, 0x07, 0x7e, 0x60, 0x24, 0x57, 0x26, + 0xbc, 0x31, 0xa7, 0x40, 0x9c, 0xc1, 0xec, 0x80, 0xd7, 0xaf, 0x5e, 0x94, 0xa2, 0xc3, 0x5a, 0x3d, + 0x58, 0x05, 0x1a, 0x0d, 0xd7, 0x81, 0xa1, 0x88, 0x40, 0xef, 0xbf, 0x20, 0xd0, 0x25, 0x86, 0x30, + 0xda, 0x9c, 0xc0, 0xac, 0x01, 0xe3, 0x19, 0x54, 0xe0, 0x13, 0x2f, 0xc0, 0x67, 0x9e, 0x4b, 0x3c, + 0x68, 0x81, 0xb8, 0x78, 0xcf, 0xc4, 0x9d, 0x4c, 0xef, 0x1b, 0x97, 0xa3, 0xf8, 0x4d, 0xab, 0xcc, + 0x7f, 0x47, 0x21, 0xec, 0x33, 0xf5, 0xe2, 0xa7, 0x9c, 0x62, 0xfe, 0xb6, 0x06, 0xfe, 0xff, 0x1c, + 0xca, 0x57, 0x7e, 0xc9, 0xff, 0xbd, 0xb7, 0xb0, 0x0a, 0xe2, 0x23, 0x5e, 0x50, 0x79, 0x07, 0xf3, + 0x2f, 0xd3, 0xad, 0xa5, 0x3e, 0x48, 0xc2, 0x90, 0xc3, 0xfc, 0x25, 0x01, 0x36, 0xeb, 0x9e, 0xe3, + 0x07, 0x3d, 0xc2, 0xa2, 0xf9, 0x59, 0x06, 0x89, 0x1e, 0x76, 0xda, 0x38, 0xea, 0xd4, 0xc7, 0xd7, + 0x44, 0xb8, 0xe4, 0x67, 0x1d, 0x0a, 0x27, 0x24, 0x9d, 0xe1, 0xbb, 0x40, 0xeb, 0x8f, 0xed, 0x26, + 0x17, 0x99, 0xa8, 0xde, 0x46, 0x31, 0xcd, 0x3b, 0x54, 0x3d, 0x17, 0xba, 0x43, 0xc9, 0xfe, 0x38, + 0x14, 0xe0, 0x16, 0x88, 0x77, 0x5c, 0xcf, 0x19, 0x88, 0xf3, 0x68, 0x28, 0x5c, 0xec, 0xfc, 0xa8, + 0x82, 0x44, 0x48, 0x08, 0x2b, 0x20, 0x2e, 0xbe, 0x4d, 0xc4, 0x0c, 0xb9, 0x3e, 0x9d, 0x80, 0x11, + 0xea, 0x74, 0xf1, 0xa2, 0x88, 0x75, 0xee, 0x14, 0x1d, 0x57, 0x30, 0x40, 0x07, 0x6c, 0xf1, 0x89, + 0x65, 0xcb, 0x01, 0x65, 0x4b, 0xe1, 0xca, 0xee, 0xae, 0x2c, 0x7c, 0x48, 0xaf, 0xbe, 0x3e, 0x6f, + 0x01, 0x20, 0x5f, 0x07, 0xf7, 0x21, 0x16, 0x9d, 0x5e, 0x47, 0xa9, 0x70, 0xc2, 0xbb, 0x0f, 0x31, + 0xbf, 0x6e, 0x3e, 0x75, 0x09, 0x75, 0xd9, 0x44, 0x3c, 0xd1, 0x37, 0xae, 0x6d, 0xe0, 0xe5, 0xf2, + 0xd6, 0xa4, 0x1b, 0x9a, 0x13, 0x70, 0xb2, 0x80, 0xf1, 0x09, 0xd4, 0x9d, 0x18, 0xc9, 0x95, 0xc8, + 0xea, 0xd2, 0x0d, 0xcd, 0x09, 0xe0, 0xe7, 0x40, 0x65, 0x13, 0x9f, 0x8f, 0x5e, 0x4e, 0xf4, 0xe1, + 0x4b, 0x12, 0x35, 0x26, 0x3e, 0x46, 0xc2, 0x11, 0x9e, 0x81, 0xf7, 0xda, 0xd8, 0xa7, 0xb8, 0xe5, + 0x30, 0xdc, 0xb6, 0x47, 0x9e, 0x54, 0x3b, 0x5f, 0x30, 0x3a, 0xf2, 0x42, 0x2b, 0xec, 0xa4, 0x26, + 0x5a, 0x7d, 0x73, 0x01, 0x3f, 0x5b, 0x42, 0x37, 0x22, 0xb0, 0x68, 0xe0, 0x91, 0xaa, 0x29, 0xfa, + 0xda, 0x91, 0xaa, 0xa9, 0x7a, 0xdc, 0xbc, 0x03, 0xb4, 0xa8, 0x0c, 0x30, 0x0d, 0x92, 0x67, 0x27, + 0xd5, 0x93, 0xd3, 0x2f, 0x4e, 0xf4, 0x18, 0xdc, 0x00, 0x1a, 0x2a, 0x97, 0x4e, 0xcf, 0xcb, 0xe8, + 0x4b, 0x5d, 0x81, 0x19, 0x90, 0x42, 0xe5, 0x62, 0xe1, 0xb8, 0x70, 0x52, 0x2a, 0xeb, 0x6b, 0xa6, + 0x01, 0xb4, 0xe8, 0xbc, 0x1c, 0x58, 0x3d, 0xb7, 0x8b, 0x85, 0x46, 0xe9, 0x50, 0x8f, 0x99, 0xb7, + 0x80, 0xca, 0x0f, 0x00, 0xb7, 0x01, 0x3c, 0xaf, 0x14, 0xec, 0xfa, 0x49, 0xa1, 0x56, 0x3f, 0x3c, + 0x6d, 0xd8, 0xf7, 0xcf, 0xca, 0x67, 0x65, 0x3d, 0xc6, 0x63, 0x54, 0x4e, 0x2a, 0x8d, 0x4a, 0xe1, + 0x58, 0x57, 0x4c, 0x55, 0x5b, 0x13, 0x89, 0xac, 0xeb, 0xaa, 0xf9, 0xab, 0x02, 0xf4, 0x45, 0x29, + 0xe4, 0x4c, 0xba, 0x0b, 0x12, 0xfc, 0x78, 0xa3, 0x40, 0x5c, 0x9c, 0x1b, 0xfb, 0xd6, 0xdf, 0xd6, + 0x30, 0x74, 0xb4, 0xea, 0xc2, 0x0b, 0x49, 0x6f, 0xfe, 0x6c, 0x46, 0xef, 0x2c, 0x17, 0x66, 0x6a, + 0xfe, 0xa0, 0x9a, 0x05, 0x90, 0x08, 0xb1, 0x57, 0x4e, 0x5f, 0x28, 0x95, 0xca, 0xb5, 0x46, 0xf9, + 0x40, 0x57, 0xf8, 0x4f, 0x85, 0x5a, 0xed, 0xb8, 0x52, 0x3e, 0xd0, 0xd7, 0x60, 0x0a, 0xc4, 0xcb, + 0x08, 0x9d, 0x22, 0x7d, 0xdd, 0xe4, 0x25, 0x54, 0x65, 0xfe, 0x5f, 0x83, 0xff, 0x95, 0x88, 0xd7, + 0x29, 0xf5, 0xb8, 0x30, 0x4b, 0xc4, 0x63, 0xf8, 0x7b, 0x06, 0x3f, 0x02, 0x80, 0x7f, 0x47, 0x3a, + 0x5e, 0x3b, 0x9a, 0xaa, 0xa9, 0x62, 0x66, 0x36, 0xcd, 0xa5, 0x4a, 0xe1, 0x6e, 0xe5, 0x00, 0xa5, + 0x24, 0xa0, 0xd2, 0xe6, 0x59, 0xfa, 0xce, 0x64, 0x40, 0x9c, 0xf0, 0x9b, 0x7b, 0x03, 0x45, 0xcb, + 0xe2, 0x07, 0x17, 0x7f, 0x64, 0x63, 0x17, 0xb3, 0xac, 0xf2, 0x68, 0x96, 0x55, 0x1e, 0xcf, 0xb2, + 0xca, 0xef, 0xb3, 0xac, 0xf2, 0xc3, 0x93, 0x6c, 0xec, 0xd1, 0x93, 0x6c, 0xec, 0xf1, 0x93, 0x6c, + 0xec, 0x2b, 0x2d, 0xaa, 0x45, 0x33, 0x21, 0xfe, 0x75, 0xb8, 0xfd, 0x57, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x6c, 0xef, 0x4b, 0x67, 0x23, 0x0d, 0x00, 0x00, } func (m *RaftHeartbeat) Marshal() (dAtA []byte, err error) { @@ -1022,15 +1017,6 @@ func (m *SnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x20 } - if len(m.LogEntries) > 0 { - for iNdEx := len(m.LogEntries) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.LogEntries[iNdEx]) - copy(dAtA[i:], m.LogEntries[iNdEx]) - i = encodeVarintRaft(dAtA, i, uint64(len(m.LogEntries[iNdEx]))) - i-- - dAtA[i] = 0x1a - } - } if len(m.KVBatch) > 0 { i -= len(m.KVBatch) copy(dAtA[i:], m.KVBatch) @@ -1351,12 +1337,6 @@ func (m *SnapshotRequest) Size() (n int) { if l > 0 { n += 1 + l + sovRaft(uint64(l)) } - if len(m.LogEntries) > 0 { - for _, b := range m.LogEntries { - l = len(b) - n += 1 + l + sovRaft(uint64(l)) - } - } if m.Final { n += 2 } @@ -2446,38 +2426,6 @@ func (m *SnapshotRequest) Unmarshal(dAtA []byte) error { m.KVBatch = []byte{} } iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LogEntries", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowRaft - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthRaft - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthRaft - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.LogEntries = append(m.LogEntries, make([]byte, postIndex-iNdEx)) - copy(m.LogEntries[len(m.LogEntries)-1], dAtA[iNdEx:postIndex]) - iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Final", wireType) diff --git a/pkg/kv/kvserver/raft.proto b/pkg/kv/kvserver/raft.proto index d74c92f26967..901068a10c8e 100644 --- a/pkg/kv/kvserver/raft.proto +++ b/pkg/kv/kvserver/raft.proto @@ -195,12 +195,9 @@ message SnapshotRequest { // A RocksDB BatchRepr. Multiple kv_batches may be sent across multiple request messages. bytes kv_batch = 2 [(gogoproto.customname) = "KVBatch"]; - // These are really raftpb.Entry, but we model them as raw bytes to avoid - // roundtripping through memory. They are separate from the kv_batch to - // allow flexibility in log implementations. - repeated bytes log_entries = 3; - bool final = 4; + + reserved 3; } message SnapshotResponse { diff --git a/pkg/kv/kvserver/replica_raftstorage.go b/pkg/kv/kvserver/replica_raftstorage.go index 8587db094378..aaa0e477aea8 100644 --- a/pkg/kv/kvserver/replica_raftstorage.go +++ b/pkg/kv/kvserver/replica_raftstorage.go @@ -14,7 +14,6 @@ import ( "context" "fmt" "math" - "strconv" "time" "github.com/cockroachdb/cockroach/pkg/keys" @@ -516,8 +515,6 @@ type IncomingSnapshot struct { SnapUUID uuid.UUID // The storage interface for the underlying SSTs. SSTStorageScratch *SSTSnapshotStorageScratch - // The Raft log entries for this snapshot. - LogEntries [][]byte // The replica state at the time the snapshot was generated (never nil). State *kvserverpb.ReplicaState snapType SnapshotRequest_Type @@ -852,29 +849,6 @@ func (r *Replica) applySnapshot( } // Update Raft entries. - var lastTerm uint64 - var raftLogSize int64 - if len(inSnap.LogEntries) > 0 { - logEntries := make([]raftpb.Entry, len(inSnap.LogEntries)) - for i, bytes := range inSnap.LogEntries { - if err := protoutil.Unmarshal(bytes, &logEntries[i]); err != nil { - return err - } - } - var sideloadedEntriesSize int64 - var err error - logEntries, sideloadedEntriesSize, err = r.maybeSideloadEntriesRaftMuLocked(ctx, logEntries) - if err != nil { - return err - } - raftLogSize += sideloadedEntriesSize - _, lastTerm, raftLogSize, err = r.append(ctx, &unreplicatedSST, 0, invalidLastTerm, raftLogSize, logEntries) - if err != nil { - return err - } - } else { - lastTerm = invalidLastTerm - } r.store.raftEntryCache.Drop(r.RangeID) if err := r.raftMu.stateLoader.SetRaftTruncatedState( @@ -899,26 +873,6 @@ func (r *Replica) applySnapshot( s.RaftAppliedIndex, nonemptySnap.Metadata.Index) } - if expLen := s.RaftAppliedIndex - s.TruncatedState.Index; expLen != uint64(len(inSnap.LogEntries)) { - entriesRange, err := extractRangeFromEntries(inSnap.LogEntries) - if err != nil { - return err - } - - tag := fmt.Sprintf("r%d_%s", r.RangeID, inSnap.SnapUUID.String()) - dir, err := r.store.checkpoint(ctx, tag) - if err != nil { - log.Warningf(ctx, "unable to create checkpoint %s: %+v", dir, err) - } else { - log.Warningf(ctx, "created checkpoint %s", dir) - } - - log.Fatalf(ctx, "missing log entries in snapshot (%s): got %d entries, expected %d "+ - "(TruncatedState.Index=%d, HardState=%s, LogEntries=%s)", - inSnap.String(), len(inSnap.LogEntries), expLen, s.TruncatedState.Index, - hs.String(), entriesRange) - } - // If we're subsuming a replica below, we don't have its last NextReplicaID, // nor can we obtain it. That's OK: we can just be conservative and use the // maximum possible replica ID. preDestroyRaftMuLocked will write a replica @@ -989,8 +943,8 @@ func (r *Replica) applySnapshot( // feelings about this ever change, we can add a LastIndex field to // raftpb.SnapshotMetadata. r.mu.lastIndex = s.RaftAppliedIndex - r.mu.lastTerm = lastTerm - r.mu.raftLogSize = raftLogSize + r.mu.lastTerm = invalidLastTerm + r.mu.raftLogSize = 0 // Update the store stats for the data in the snapshot. r.store.metrics.subtractMVCCStats(ctx, r.mu.tenantID, *r.mu.state.Stats) r.store.metrics.addMVCCStats(ctx, r.mu.tenantID, *s.Stats) @@ -1200,29 +1154,6 @@ func (r *Replica) clearSubsumedReplicaInMemoryData( return nil } -// extractRangeFromEntries returns a string representation of the range of -// marshaled list of raft log entries in the form of [first-index, last-index]. -// If the list is empty, "[n/a, n/a]" is returned instead. -func extractRangeFromEntries(logEntries [][]byte) (string, error) { - var firstIndex, lastIndex string - if len(logEntries) == 0 { - firstIndex = "n/a" - lastIndex = "n/a" - } else { - firstAndLastLogEntries := make([]raftpb.Entry, 2) - if err := protoutil.Unmarshal(logEntries[0], &firstAndLastLogEntries[0]); err != nil { - return "", err - } - if err := protoutil.Unmarshal(logEntries[len(logEntries)-1], &firstAndLastLogEntries[1]); err != nil { - return "", err - } - - firstIndex = strconv.FormatUint(firstAndLastLogEntries[0].Index, 10) - lastIndex = strconv.FormatUint(firstAndLastLogEntries[1].Index, 10) - } - return fmt.Sprintf("[%s, %s]", firstIndex, lastIndex), nil -} - type raftCommandEncodingVersion byte // Raft commands are encoded with a 1-byte version (currently 0 or 1), an 8-byte diff --git a/pkg/kv/kvserver/replica_sideload_test.go b/pkg/kv/kvserver/replica_sideload_test.go index 752d4e7ba403..8eaee788fcfe 100644 --- a/pkg/kv/kvserver/replica_sideload_test.go +++ b/pkg/kv/kvserver/replica_sideload_test.go @@ -13,7 +13,6 @@ import ( "bytes" "context" "fmt" - "io" "math" "math/rand" "path/filepath" @@ -28,7 +27,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/raftentry" - "github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/storage" @@ -668,33 +666,6 @@ func testRaftSSTableSideloadingProposal(t *testing.T, eng storage.Engine) { verifyLogSizeInSync(t, tc.repl) } -type mockSender struct { - logEntries [][]byte - done bool -} - -func (mr *mockSender) Send(req *SnapshotRequest) error { - if req.LogEntries != nil { - if mr.logEntries != nil { - return errors.New("already have log entries") - } - mr.logEntries = req.LogEntries - } - return nil -} - -func (mr *mockSender) Recv() (*SnapshotResponse, error) { - if mr.done { - return nil, io.EOF - } - status := SnapshotResponse_ACCEPTED - if len(mr.logEntries) > 0 { - status = SnapshotResponse_APPLIED - mr.done = true - } - return &SnapshotResponse{Status: status}, nil -} - func newOnDiskEngine(t *testing.T) (func(), storage.Engine) { dir, cleanup := testutils.TempDir(t) eng, err := storage.Open( @@ -707,196 +678,6 @@ func newOnDiskEngine(t *testing.T) (func(), storage.Engine) { return cleanup, eng } -// This test verifies that when a snapshot is sent, sideloaded proposals are -// inlined. -func TestRaftSSTableSideloadingSnapshot(t *testing.T) { - defer leaktest.AfterTest(t)() - defer log.Scope(t).Close(t) - defer SetMockAddSSTable()() - - ctx := context.Background() - tc := testContext{} - - cleanup, eng := newOnDiskEngine(t) - tc.engine = eng - defer cleanup() - defer eng.Close() - - stopper := stop.NewStopper() - defer stopper.Stop(ctx) - tc.Start(t, stopper) - - var ba roachpb.BatchRequest - ba.RangeID = tc.repl.RangeID - - // Disable log truncation as we want to be sure that we get to create - // snapshots that have our sideloaded proposal in them. - tc.store.SetRaftLogQueueActive(false) - - // Put a sideloaded proposal on the Range. - key, val := "don't", "care" - origSSTData, _ := MakeSSTable(key, val, hlc.Timestamp{}.Add(0, 1)) - { - - var addReq roachpb.AddSSTableRequest - addReq.Data = origSSTData - addReq.Key = roachpb.Key(key) - addReq.EndKey = addReq.Key.Next() - ba.Add(&addReq) - - _, pErr := tc.store.Send(ctx, ba) - if pErr != nil { - t.Fatal(pErr) - } - } - - // Run a happy case snapshot. Check that it properly inlines the payload in - // the contained log entries. - inlinedEntry := func() raftpb.Entry { - os, err := tc.repl.GetSnapshot(ctx, SnapshotRequest_VIA_SNAPSHOT_QUEUE, tc.store.StoreID()) - if err != nil { - t.Fatal(err) - } - defer os.Close() - - mockSender := &mockSender{} - if err := sendSnapshot( - ctx, - tc.store.cfg.Settings, - mockSender, - &fakeStorePool{}, - SnapshotRequest_Header{State: os.State, Priority: SnapshotRequest_RECOVERY}, - os, - tc.repl.store.Engine().NewBatch, - func() {}, - ); err != nil { - t.Fatal(err) - } - - var ent raftpb.Entry - var cmd kvserverpb.RaftCommand - var finalEnt raftpb.Entry - for _, entryBytes := range mockSender.logEntries { - if err := protoutil.Unmarshal(entryBytes, &ent); err != nil { - t.Fatal(err) - } - if sniffSideloadedRaftCommand(ent.Data) { - _, cmdBytes := DecodeRaftCommand(ent.Data) - if err := protoutil.Unmarshal(cmdBytes, &cmd); err != nil { - t.Fatal(err) - } - if as := cmd.ReplicatedEvalResult.AddSSTable; as == nil { - t.Fatalf("no AddSSTable found in sideloaded command %+v", cmd) - } else if len(as.Data) == 0 { - t.Fatalf("empty payload in sideloaded command: %+v", cmd) - } - finalEnt = ent - } - } - if finalEnt.Index == 0 { - t.Fatal("no sideloaded command found") - } - return finalEnt - }() - - sideloadedIndex := inlinedEntry.Index - - // This happens to be a good point in time to check the `entries()` method - // which has special handling to accommodate `term()`: when an empty - // sideload storage is passed in, `entries()` should not inline, and in turn - // also not populate the entries cache (since its contents must always be - // fully inlined). - func() { - tc.repl.raftMu.Lock() - defer tc.repl.raftMu.Unlock() - tc.repl.mu.Lock() - defer tc.repl.mu.Unlock() - for _, withSS := range []bool{false, true} { - tc.store.raftEntryCache.Clear(tc.repl.RangeID, sideloadedIndex+1) - - var ss SideloadStorage - if withSS { - ss = tc.repl.raftMu.sideloaded - } - rsl := stateloader.Make(tc.repl.RangeID) - entries, err := entries( - ctx, rsl, tc.store.Engine(), tc.repl.RangeID, tc.store.raftEntryCache, - ss, sideloadedIndex, sideloadedIndex+1, 1<<20, - ) - if err != nil { - t.Fatal(err) - } - if len(entries) != 1 { - t.Fatalf("no or too many entries returned from cache: %+v", entries) - } - ents, _, _, _ := tc.store.raftEntryCache.Scan(nil, tc.repl.RangeID, sideloadedIndex, sideloadedIndex+1, 1<<20) - if withSS { - // We passed the sideload storage, so we expect to get our - // inlined index back from the cache. - if len(ents) != 1 { - t.Fatalf("no or too many entries returned from cache: %+v", ents) - } - if err := entryEq(inlinedEntry, ents[0]); err != nil { - t.Fatalf("withSS=%t: %+v", withSS, err) - } - } else { - // Without sideload storage, expect the cache to remain - // unpopulated and the entry returned from entries() to not have - // been inlined. - if len(ents) != 0 { - t.Fatalf("expected no cached entries, but got %+v", ents) - } - if expErr, err := `ReplicatedEvalResult.AddSSTable.Data: \[\]uint8\[\d+\] != \[\]uint8\[0\]`, - entryEq(inlinedEntry, entries[0]); !testutils.IsError( - err, - expErr, - ) { - t.Fatalf("expected specific mismatch on `Data` field, but got %v\nwanted: %s", err, expErr) - } - } - } - }() - - // Now run a snapshot that will fail since it doesn't find one of its on-disk - // payloads. This can happen if the Raft log queue runs between the time the - // (engine) snapshot is taken and the log entries are actually read from the - // (engine) snapshot. We didn't run this before because we wanted the file - // to stay in sideloaded storage for the previous test. - func() { - failingOS, err := tc.repl.GetSnapshot(ctx, SnapshotRequest_VIA_SNAPSHOT_QUEUE, tc.store.StoreID()) - if err != nil { - t.Fatal(err) - } - defer failingOS.Close() - - // Remove the actual file. - tc.repl.raftMu.Lock() - if err := tc.repl.raftMu.sideloaded.Clear(ctx); err != nil { - tc.repl.raftMu.Unlock() - t.Fatal(err) - } - tc.repl.raftMu.Unlock() - // Additionally we need to clear out the entry from the cache because - // that would still save the day. - tc.store.raftEntryCache.Clear(tc.repl.RangeID, sideloadedIndex+1) - - mockSender := &mockSender{} - err = sendSnapshot( - ctx, - tc.store.cfg.Settings, - mockSender, - &fakeStorePool{}, - SnapshotRequest_Header{State: failingOS.State, Priority: SnapshotRequest_RECOVERY}, - failingOS, - tc.repl.store.Engine().NewBatch, - func() {}, - ) - if !errors.HasType(err, (*errMustRetrySnapshotDueToTruncation)(nil)) { - t.Fatal(err) - } - }() -} - func TestRaftSSTableSideloadingTruncation(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 4a269d5574aa..035a3251424d 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -28,7 +28,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/protoutil" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/cockroach/pkg/util/uuid" "github.com/cockroachdb/errors" @@ -241,7 +240,6 @@ func (kvSS *kvBatchSnapshotStrategy) Receive( return noSnap, err } defer msstw.Close() - var logEntries [][]byte for { req, err := stream.Recv() @@ -272,9 +270,6 @@ func (kvSS *kvBatchSnapshotStrategy) Receive( } } } - if req.LogEntries != nil { - logEntries = append(logEntries, req.LogEntries...) - } if req.Final { // We finished receiving all batches and log entries. It's possible that // we did not receive any key-value pairs for some of the key ranges, but @@ -295,22 +290,11 @@ func (kvSS *kvBatchSnapshotStrategy) Receive( inSnap := IncomingSnapshot{ SnapUUID: snapUUID, SSTStorageScratch: kvSS.scratch, - LogEntries: logEntries, State: &header.State, snapType: header.Type, } - expLen := inSnap.State.RaftAppliedIndex - inSnap.State.TruncatedState.Index - if expLen != uint64(len(logEntries)) { - // We've received a botched snapshot. We could fatal right here but opt - // to warn loudly instead, and fatal when applying the snapshot - // (in Replica.applySnapshot) in order to capture replica hard state. - log.Warningf(ctx, - "missing log entries in snapshot (%s): got %d entries, expected %d", - inSnap.String(), len(logEntries), expLen) - } - - kvSS.status = fmt.Sprintf("log entries: %d, ssts: %d", len(logEntries), len(kvSS.scratch.SSTs())) + kvSS.status = fmt.Sprintf("ssts: %d", len(kvSS.scratch.SSTs())) return inSnap, nil } } @@ -375,105 +359,7 @@ func (kvSS *kvBatchSnapshotStrategy) Send( bytesSent += int64(b.Len()) } - // Iterate over the specified range of Raft entries and send them all out - // together. - rangeID := header.State.Desc.RangeID - firstIndex := header.State.TruncatedState.Index + 1 - endIndex := snap.RaftSnap.Metadata.Index + 1 - logEntries := make([]raftpb.Entry, 0, endIndex-firstIndex) - scanFunc := func(ent raftpb.Entry) error { - logEntries = append(logEntries, ent) - return nil - } - if err := iterateEntries(ctx, snap.EngineSnap, rangeID, firstIndex, endIndex, scanFunc); err != nil { - return 0, err - } - - // Inline the payloads for all sideloaded proposals. - // - // TODO(tschottdorf): could also send slim proposals and attach sideloaded - // SSTables directly to the snapshot. Probably the better long-term - // solution, but let's see if it ever becomes relevant. Snapshots with - // inlined proposals are hopefully the exception. - // - // TODO(tbg): this code is obsolete because as of the PR linked below, - // our snapshots will never contain log entries. Trim down this code. - // - // https://github.com/cockroachdb/cockroach/pull/70464 - { - for i, ent := range logEntries { - if !sniffSideloadedRaftCommand(ent.Data) { - continue - } - if err := snap.WithSideloaded(func(ss SideloadStorage) error { - newEnt, err := maybeInlineSideloadedRaftCommand( - ctx, rangeID, ent, ss, snap.RaftEntryCache, - ) - if err != nil { - return err - } - if newEnt != nil { - logEntries[i] = *newEnt - } - return nil - }); err != nil { - if errors.Is(err, errSideloadedFileNotFound) { - // We're creating the Raft snapshot based on a snapshot of - // the engine, but the Raft log may since have been - // truncated and corresponding on-disk sideloaded payloads - // unlinked. Luckily, we can just abort this snapshot; the - // caller can retry. - // - // TODO(tschottdorf): check how callers handle this. They - // should simply retry. In some scenarios, perhaps this can - // happen repeatedly and prevent a snapshot; not sending the - // log entries wouldn't help, though, and so we'd really - // need to make sure the entries are always here, for - // instance by pre-loading them into memory. Or we can make - // log truncation less aggressive about removing sideloaded - // files, by delaying trailing file deletion for a bit. - return 0, &errMustRetrySnapshotDueToTruncation{ - index: ent.Index, - term: ent.Term, - } - } - return 0, err - } - } - } - - // Marshal each of the log entries. - logEntriesRaw := make([][]byte, len(logEntries)) - for i := range logEntries { - entRaw, err := protoutil.Marshal(&logEntries[i]) - if err != nil { - return 0, err - } - logEntriesRaw[i] = entRaw - } - - // The difference between the snapshot index (applied index at the time of - // snapshot) and the truncated index should equal the number of log entries - // shipped over. - expLen := endIndex - firstIndex - if expLen != uint64(len(logEntries)) { - // We've generated a botched snapshot. We could fatal right here but opt - // to warn loudly instead, and fatal at the caller to capture a checkpoint - // of the underlying storage engine. - entriesRange, err := extractRangeFromEntries(logEntriesRaw) - if err != nil { - return 0, err - } - log.Warningf(ctx, "missing log entries in snapshot (%s): "+ - "got %d entries, expected %d (TruncatedState.Index=%d, LogEntries=%s)", - snap.String(), len(logEntries), expLen, snap.State.TruncatedState.Index, entriesRange) - return 0, errMalformedSnapshot - } - - kvSS.status = fmt.Sprintf("kv pairs: %d, log entries: %d", kvs, len(logEntries)) - if err := stream.Send(&SnapshotRequest{LogEntries: logEntriesRaw}); err != nil { - return 0, err - } + kvSS.status = fmt.Sprintf("kv pairs: %d", kvs) return bytesSent, nil } @@ -855,17 +741,6 @@ func snapshotRateLimit( } } -type errMustRetrySnapshotDueToTruncation struct { - index, term uint64 -} - -func (e *errMustRetrySnapshotDueToTruncation) Error() string { - return fmt.Sprintf( - "log truncation during snapshot removed sideloaded SSTable at index %d, term %d", - e.index, e.term, - ) -} - // SendEmptySnapshot creates an OutgoingSnapshot for the input range // descriptor and seeds it with an empty range. Then, it sends this // snapshot to the replica specified in the input. From 5660c324423a5a5fd50aaf37f602a37fa307f7e0 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Tue, 2 Nov 2021 10:40:01 +0100 Subject: [PATCH 162/205] kvserver: trim state used from snapshots Snapshots contain a serialized copy of the `ReplicaState`. However, the snapshot itself contains that data already. Having two sources of data that may be interpreted differently can lead to problems, as we saw in [72239]. This commit deprecates using the entire ReplicaState. Instead, we pick out the descriptor and ignore everything else. Instead of using the copy of the state to initialize the recipient's in-memory state, we now use a state loader. In 22.2 we can only send the descriptor but maybe we won't do that; for logging and debugging it's kind of nice to have everything present. Fixes https://github.com/cockroachdb/cockroach/issues/72222. [#72239]: https://github.com/cockroachdb/cockroach/pull/72239 Release note: None --- pkg/kv/kvserver/client_merge_test.go | 10 ++--- pkg/kv/kvserver/client_replica_test.go | 2 +- pkg/kv/kvserver/replica_raft.go | 6 +-- pkg/kv/kvserver/replica_raftstorage.go | 56 +++++++++++++++----------- pkg/kv/kvserver/store_snapshot.go | 3 +- pkg/kv/kvserver/store_test.go | 2 +- 6 files changed, 45 insertions(+), 34 deletions(-) diff --git a/pkg/kv/kvserver/client_merge_test.go b/pkg/kv/kvserver/client_merge_test.go index fad033662d11..51d36ba78015 100644 --- a/pkg/kv/kvserver/client_merge_test.go +++ b/pkg/kv/kvserver/client_merge_test.go @@ -757,7 +757,7 @@ func mergeCheckingTimestampCaches( // Install a filter to capture the Raft snapshot. snapshotFilter = func(inSnap kvserver.IncomingSnapshot) { - if inSnap.State.Desc.RangeID == lhsDesc.RangeID { + if inSnap.Desc.RangeID == lhsDesc.RangeID { snapChan <- inSnap } } @@ -809,7 +809,7 @@ func mergeCheckingTimestampCaches( case <-time.After(45 * time.Second): t.Fatal("timed out waiting for snapChan") } - inSnapDesc := inSnap.State.Desc + inSnapDesc := inSnap.Desc require.Equal(t, lhsDesc.StartKey, inSnapDesc.StartKey) require.Equal(t, rhsDesc.EndKey, inSnapDesc.EndKey) @@ -3731,7 +3731,7 @@ func TestStoreRangeMergeRaftSnapshot(t *testing.T) { // on in the test. This function verifies that the subsumed replicas have // been handled properly. if snapType != kvserver.SnapshotRequest_VIA_SNAPSHOT_QUEUE || - inSnap.State.Desc.RangeID != rangeIds[string(keyA)] { + inSnap.Desc.RangeID != rangeIds[string(keyA)] { return nil } @@ -3779,8 +3779,8 @@ func TestStoreRangeMergeRaftSnapshot(t *testing.T) { // Construct SSTs for the the first 4 bullets as numbered above, but only // ultimately keep the last one. - keyRanges := rditer.MakeReplicatedKeyRanges(inSnap.State.Desc) - it := rditer.NewReplicaEngineDataIterator(inSnap.State.Desc, sendingEng, true /* replicatedOnly */) + keyRanges := rditer.MakeReplicatedKeyRanges(inSnap.Desc) + it := rditer.NewReplicaEngineDataIterator(inSnap.Desc, sendingEng, true /* replicatedOnly */) defer it.Close() // Write a range deletion tombstone to each of the SSTs then put in the // kv entries from the sender of the snapshot. diff --git a/pkg/kv/kvserver/client_replica_test.go b/pkg/kv/kvserver/client_replica_test.go index 4fbd07c678c0..2a265fbb3acc 100644 --- a/pkg/kv/kvserver/client_replica_test.go +++ b/pkg/kv/kvserver/client_replica_test.go @@ -3648,7 +3648,7 @@ func TestTenantID(t *testing.T) { request_type kvserver.SnapshotRequest_Type, strings []string, ) error { - if snapshot.State.Desc.RangeID == repl.RangeID { + if snapshot.Desc.RangeID == repl.RangeID { select { case sawSnapshot <- struct{}{}: default: diff --git a/pkg/kv/kvserver/replica_raft.go b/pkg/kv/kvserver/replica_raft.go index 771f6315c3cc..51e37ba1c68d 100644 --- a/pkg/kv/kvserver/replica_raft.go +++ b/pkg/kv/kvserver/replica_raft.go @@ -507,7 +507,7 @@ func (r *Replica) handleRaftReadyRaftMuLocked( ctx context.Context, inSnap IncomingSnapshot, ) (_ handleRaftReadyStats, _ string, foo error) { var stats handleRaftReadyStats - if inSnap.State != nil { + if inSnap.Desc != nil { stats.snap.offered = true } @@ -584,7 +584,7 @@ func (r *Replica) handleRaftReadyRaftMuLocked( leaderID = roachpb.ReplicaID(rd.SoftState.Lead) } - if inSnap.State != nil { + if inSnap.Desc != nil { if !raft.IsEmptySnap(rd.Snapshot) { snapUUID, err := uuid.FromBytes(rd.Snapshot.Data) if err != nil { @@ -1719,7 +1719,7 @@ func (r *Replica) maybeAcquireSnapshotMergeLock( // installed a placeholder for snapshot's keyspace. No merge lock needed. return nil, func() {} } - for endKey.Less(inSnap.State.Desc.EndKey) { + for endKey.Less(inSnap.Desc.EndKey) { sRepl := r.store.LookupReplica(endKey) if sRepl == nil || !endKey.Equal(sRepl.Desc().StartKey) { log.Fatalf(ctx, "snapshot widens existing replica, but no replica exists for subsumed key %s", endKey) diff --git a/pkg/kv/kvserver/replica_raftstorage.go b/pkg/kv/kvserver/replica_raftstorage.go index aaa0e477aea8..a4817278bb12 100644 --- a/pkg/kv/kvserver/replica_raftstorage.go +++ b/pkg/kv/kvserver/replica_raftstorage.go @@ -515,10 +515,11 @@ type IncomingSnapshot struct { SnapUUID uuid.UUID // The storage interface for the underlying SSTs. SSTStorageScratch *SSTSnapshotStorageScratch - // The replica state at the time the snapshot was generated (never nil). - State *kvserverpb.ReplicaState - snapType SnapshotRequest_Type - placeholder *ReplicaPlaceholder + // The descriptor in the snapshot, never nil. + Desc *roachpb.RangeDescriptor + snapType SnapshotRequest_Type + placeholder *ReplicaPlaceholder + raftAppliedIndex uint64 // logging only } func (s *IncomingSnapshot) String() string { @@ -527,10 +528,10 @@ func (s *IncomingSnapshot) String() string { // SafeFormat implements the redact.SafeFormatter interface. func (s *IncomingSnapshot) SafeFormat(w redact.SafePrinter, _ rune) { - w.Printf("%s snapshot %s at applied index %d", s.snapType, s.SnapUUID.Short(), s.State.RaftAppliedIndex) + w.Printf("%s snapshot %s at applied index %d", s.snapType, s.SnapUUID.Short(), s.raftAppliedIndex) } -// snapshot creates an OutgoingSnapshot containing a rocksdb snapshot for the +// snapshot creates an OutgoingSnapshot containing a pebble snapshot for the // given range. Note that snapshot() is called without Replica.raftMu held. func snapshot( ctx context.Context, @@ -758,9 +759,9 @@ func (r *Replica) applySnapshot( hs raftpb.HardState, subsumedRepls []*Replica, ) (err error) { - s := *inSnap.State - if s.Desc.RangeID != r.RangeID { - log.Fatalf(ctx, "unexpected range ID %d", s.Desc.RangeID) + desc := inSnap.Desc + if desc.RangeID != r.RangeID { + log.Fatalf(ctx, "unexpected range ID %d", desc.RangeID) } isInitialSnap := !r.IsInitialized() @@ -852,7 +853,11 @@ func (r *Replica) applySnapshot( r.store.raftEntryCache.Drop(r.RangeID) if err := r.raftMu.stateLoader.SetRaftTruncatedState( - ctx, &unreplicatedSST, s.TruncatedState, + ctx, &unreplicatedSST, + &roachpb.RaftTruncatedState{ + Index: nonemptySnap.Metadata.Index, + Term: nonemptySnap.Metadata.Term, + }, ); err != nil { return errors.Wrapf(err, "unable to write TruncatedState to unreplicated SST writer") } @@ -868,11 +873,6 @@ func (r *Replica) applySnapshot( } } - if s.RaftAppliedIndex != nonemptySnap.Metadata.Index { - log.Fatalf(ctx, "snapshot RaftAppliedIndex %d doesn't match its metadata index %d", - s.RaftAppliedIndex, nonemptySnap.Metadata.Index) - } - // If we're subsuming a replica below, we don't have its last NextReplicaID, // nor can we obtain it. That's OK: we can just be conservative and use the // maximum possible replica ID. preDestroyRaftMuLocked will write a replica @@ -880,7 +880,7 @@ func (r *Replica) applySnapshot( // problematic, as it would prevent this store from ever having a new replica // of the removed range. In this case, however, it's copacetic, as subsumed // ranges _can't_ have new replicas. - if err := r.clearSubsumedReplicaDiskData(ctx, inSnap.SSTStorageScratch, s.Desc, subsumedRepls, mergedTombstoneReplicaID); err != nil { + if err := r.clearSubsumedReplicaDiskData(ctx, inSnap.SSTStorageScratch, desc, subsumedRepls, mergedTombstoneReplicaID); err != nil { return err } stats.subsumedReplicas = timeutil.Now() @@ -896,6 +896,16 @@ func (r *Replica) applySnapshot( } stats.ingestion = timeutil.Now() + state, err := stateloader.Make(desc.RangeID).Load(ctx, r.store.engine, desc) + if err != nil { + log.Fatalf(ctx, "unable to load replica state: %s", err) + } + + if state.RaftAppliedIndex != nonemptySnap.Metadata.Index { + log.Fatalf(ctx, "snapshot RaftAppliedIndex %d doesn't match its metadata index %d", + state.RaftAppliedIndex, nonemptySnap.Metadata.Index) + } + // The on-disk state is now committed, but the corresponding in-memory state // has not yet been updated. Any errors past this point must therefore be // treated as fatal. @@ -926,7 +936,7 @@ func (r *Replica) applySnapshot( log.Fatalf(ctx, "unable to remove placeholder: %s", err) } } - r.setDescLockedRaftMuLocked(ctx, s.Desc) + r.setDescLockedRaftMuLocked(ctx, desc) if err := r.store.maybeMarkReplicaInitializedLockedReplLocked(ctx, r); err != nil { log.Fatalf(ctx, "unable to mark replica initialized while applying snapshot: %+v", err) } @@ -942,18 +952,18 @@ func (r *Replica) applySnapshot( // performance implications are not likely to be drastic. If our // feelings about this ever change, we can add a LastIndex field to // raftpb.SnapshotMetadata. - r.mu.lastIndex = s.RaftAppliedIndex + r.mu.lastIndex = state.RaftAppliedIndex r.mu.lastTerm = invalidLastTerm r.mu.raftLogSize = 0 // Update the store stats for the data in the snapshot. r.store.metrics.subtractMVCCStats(ctx, r.mu.tenantID, *r.mu.state.Stats) - r.store.metrics.addMVCCStats(ctx, r.mu.tenantID, *s.Stats) + r.store.metrics.addMVCCStats(ctx, r.mu.tenantID, *state.Stats) lastKnownLease := r.mu.state.Lease // Update the rest of the Raft state. Changes to r.mu.state.Desc must be // managed by r.setDescRaftMuLocked and changes to r.mu.state.Lease must be handled // by r.leasePostApply, but we called those above, so now it's safe to // wholesale replace r.mu.state. - r.mu.state = s + r.mu.state = state // Snapshots typically have fewer log entries than the leaseholder. The next // time we hold the lease, recompute the log size before making decisions. r.mu.raftLogSizeTrusted = false @@ -962,13 +972,13 @@ func (r *Replica) applySnapshot( // replica according to whether it holds the lease. We allow jumps in the // lease sequence because there may be multiple lease changes accounted for // in the snapshot. - r.leasePostApplyLocked(ctx, lastKnownLease, s.Lease /* newLease */, prioReadSum, allowLeaseJump) + r.leasePostApplyLocked(ctx, lastKnownLease, state.Lease /* newLease */, prioReadSum, allowLeaseJump) // Similarly, if we subsumed any replicas through the snapshot (meaning that // we missed the application of a merge) and we are the new leaseholder, we // make sure to update the timestamp cache using the prior read summary to // account for any reads that were served on the right-hand side range(s). - if len(subsumedRepls) > 0 && s.Lease.Replica.ReplicaID == r.mu.replicaID && prioReadSum != nil { + if len(subsumedRepls) > 0 && state.Lease.Replica.ReplicaID == r.mu.replicaID && prioReadSum != nil { applyReadSummaryToTimestampCache(r.store.tsCache, r.descRLocked(), *prioReadSum) } @@ -995,7 +1005,7 @@ func (r *Replica) applySnapshot( // Update the replica's cached byte thresholds. This is a no-op if the system // config is not available, in which case we rely on the next gossip update // to perform the update. - if err := r.updateRangeInfo(ctx, s.Desc); err != nil { + if err := r.updateRangeInfo(ctx, desc); err != nil { log.Fatalf(ctx, "unable to update range info while applying snapshot: %+v", err) } diff --git a/pkg/kv/kvserver/store_snapshot.go b/pkg/kv/kvserver/store_snapshot.go index 035a3251424d..d5bf8264c207 100644 --- a/pkg/kv/kvserver/store_snapshot.go +++ b/pkg/kv/kvserver/store_snapshot.go @@ -290,8 +290,9 @@ func (kvSS *kvBatchSnapshotStrategy) Receive( inSnap := IncomingSnapshot{ SnapUUID: snapUUID, SSTStorageScratch: kvSS.scratch, - State: &header.State, + Desc: header.State.Desc, snapType: header.Type, + raftAppliedIndex: header.State.RaftAppliedIndex, } kvSS.status = fmt.Sprintf("ssts: %d", len(kvSS.scratch.SSTs())) diff --git a/pkg/kv/kvserver/store_test.go b/pkg/kv/kvserver/store_test.go index 65aa6cbf7062..586721e522c8 100644 --- a/pkg/kv/kvserver/store_test.go +++ b/pkg/kv/kvserver/store_test.go @@ -2817,7 +2817,7 @@ func TestStoreRemovePlaceholderOnRaftIgnored(t *testing.T) { if err := s.processRaftSnapshotRequest(ctx, req, IncomingSnapshot{ SnapUUID: uuid.MakeV4(), - State: &kvserverpb.ReplicaState{Desc: repl1.Desc()}, + Desc: repl1.Desc(), placeholder: placeholder, }, ); err != nil { From 4ab7711063b689c2659cc08c51557bb4d21e59d9 Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Wed, 20 Oct 2021 14:11:55 -0400 Subject: [PATCH 163/205] backupccl: create export parquet processor This commit adds a new processor for exporting a relation as a parquet file, and adds a test for exporting a relation with bools, ints, floats, and strings. This commit also updates the fraugster parquet-go vendor dependency. Future work must ensure that all crdb data types, including arrays, can be exported properly. The fraugster/parquet-go vendor was specifically chosen because it allows us to define the parquet schema at runtime. Specifically, we can write rows encoded as a dynamically created map[string]interface{}. Release note (sql change): adds ability to EXPORT PARQUET for relations with float, int, string column types. --- DEPS.bzl | 8 + go.mod | 1 + go.sum | 3 + pkg/ccl/importccl/BUILD.bazel | 5 + pkg/ccl/importccl/exportcsv_test.go | 97 +++ pkg/ccl/importccl/exportparquet.go | 430 +++++++++++++ pkg/roachpb/io-formats.pb.go | 174 ++--- pkg/roachpb/io-formats.proto | 2 - pkg/sql/colexec/colbuilder/execplan.go | 3 + pkg/sql/distsql_physical_planner.go | 25 +- pkg/sql/execinfrapb/processors_bulk_io.pb.go | 644 +++++++++++++++---- pkg/sql/execinfrapb/processors_bulk_io.proto | 6 +- pkg/sql/export.go | 51 +- pkg/sql/rowexec/processors.go | 13 + pkg/sql/sem/tree/format.go | 3 - vendor | 2 +- 16 files changed, 1159 insertions(+), 308 deletions(-) create mode 100644 pkg/ccl/importccl/exportparquet.go diff --git a/DEPS.bzl b/DEPS.bzl index 6f86e218c2ae..55b0d49effbc 100644 --- a/DEPS.bzl +++ b/DEPS.bzl @@ -1686,6 +1686,14 @@ def go_deps(): sum = "h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=", version = "v1.11.3", ) + go_repository( + name = "com_github_fraugster_parquet_go", + build_file_proto_mode = "disable_global", + importpath = "github.com/fraugster/parquet-go", + sum = "h1:1VjhmRJTlHR2vM3qXiPjsYbTYEtwIxmQZZ7AvVKAcQQ=", + version = "v0.4.0", + ) + go_repository( name = "com_github_fsnotify_fsnotify", build_file_proto_mode = "disable_global", diff --git a/go.mod b/go.mod index 59b16b06bb32..d15d5788876e 100644 --- a/go.mod +++ b/go.mod @@ -63,6 +63,7 @@ require ( github.com/elastic/gosigar v0.14.1 github.com/emicklei/dot v0.15.0 github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a + github.com/fraugster/parquet-go v0.4.0 github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 github.com/go-ole/go-ole v1.2.4 // indirect github.com/go-sql-driver/mysql v1.5.0 diff --git a/go.sum b/go.sum index 1a5e04cddfb4..f098097e7d4a 100644 --- a/go.sum +++ b/go.sum @@ -232,6 +232,7 @@ github.com/apache/arrow/go/arrow v0.0.0-20200923215132-ac86123a3f01 h1:FSqtT0UCk github.com/apache/arrow/go/arrow v0.0.0-20200923215132-ac86123a3f01/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= github.com/apache/thrift v0.0.0-20151001171628-53dd39833a08/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -646,6 +647,8 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/frankban/quicktest v1.7.3/go.mod h1:V1d2J5pfxYH6EjBAgSK7YNXcXlTWxUHdE1sVDXkjnig= github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fraugster/parquet-go v0.4.0 h1:1VjhmRJTlHR2vM3qXiPjsYbTYEtwIxmQZZ7AvVKAcQQ= +github.com/fraugster/parquet-go v0.4.0/go.mod h1:qIL8Wm6AK06QHCj9OBFW6PyS+7ukZxc20K/acSeGUas= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= diff --git a/pkg/ccl/importccl/BUILD.bazel b/pkg/ccl/importccl/BUILD.bazel index c781c34920dc..23fa509f66f0 100644 --- a/pkg/ccl/importccl/BUILD.bazel +++ b/pkg/ccl/importccl/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "importccl", srcs = [ "exportcsv.go", + "exportparquet.go", "import_job.go", "import_planning.go", "import_processor.go", @@ -92,6 +93,9 @@ go_library( "@com_github_cockroachdb_apd_v2//:apd", "@com_github_cockroachdb_errors//:errors", "@com_github_cockroachdb_logtags//:logtags", + "@com_github_fraugster_parquet_go//:parquet-go", + "@com_github_fraugster_parquet_go//parquet", + "@com_github_fraugster_parquet_go//parquetschema", "@com_github_lib_pq//oid", "@com_github_linkedin_goavro_v2//:goavro", "@io_vitess_vitess//go/sqltypes", @@ -201,6 +205,7 @@ go_test( "//pkg/workload/workloadsql", "@com_github_cockroachdb_cockroach_go_v2//crdb", "@com_github_cockroachdb_errors//:errors", + "@com_github_fraugster_parquet_go//:parquet-go", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_gogo_protobuf//proto", "@com_github_jackc_pgx_v4//:pgx", diff --git a/pkg/ccl/importccl/exportcsv_test.go b/pkg/ccl/importccl/exportcsv_test.go index 1bfdc0773f7f..ab3f194862af 100644 --- a/pkg/ccl/importccl/exportcsv_test.go +++ b/pkg/ccl/importccl/exportcsv_test.go @@ -17,6 +17,7 @@ import ( "io" "io/ioutil" "net/url" + "os" "path/filepath" "strings" "testing" @@ -35,11 +36,13 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/workload/bank" "github.com/cockroachdb/cockroach/pkg/workload/workloadsql" + goparquet "github.com/fraugster/parquet-go" "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/require" ) const exportFilePattern = "export*-n*.0.csv" +const parquetExportFilePattern = "export*-n*.0.parquet" func setupExportableBank(t *testing.T, nodes, rows int) (*sqlutils.SQLRunner, string, func()) { ctx := context.Background() @@ -263,6 +266,100 @@ func TestExportOrder(t *testing.T) { } } +// validateParquetFile reads the parquet file, converts each value in the parquet file to its +// native go type, and asserts its values match the truth. + +// TODO (MB): once we figure out how to export the column names properly, +// this function should check the column names as well +func validateParquetFile(t *testing.T, file string, truthRows [][]interface{}) error { + r, err := os.Open(file) + if err != nil { + return err + } + defer r.Close() + + fr, err := goparquet.NewFileReader(r) + if err != nil { + return err + } + t.Logf("Schema: %s", fr.GetSchemaDefinition()) + + cols := fr.SchemaReader.GetSchemaDefinition().RootColumn.Children + + require.Equal(t, len(cols), len(truthRows[0])) + require.Equal(t, int(fr.NumRows()), len(truthRows)) + + count := 0 + + for { + row, err := fr.NextRow() + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("reading record failed: %w", err) + } + + t.Logf("\n Record %v:", count) + for i := 0; i < len(cols); i++ { + var decodedV interface{} + v := row[cols[i].SchemaElement.Name] + switch vv := v.(type) { + case []byte: + // the parquet exporter encodes native go strings as []byte, so an extra + // step is required here + // TODO (MB): as we add more type support, this + // test will be insufficient: many go native types are encoded as + // []byte, so in the future, each column will have to call it's own + // custom decoder ( this is how IMPORT Parquet will work) + decodedV = string(vv) + case int64, float64, bool: + decodedV = vv + default: + t.Fatalf("unexepected type: %T", vv) + } + t.Logf("\t %v", decodedV) + require.Equal(t, truthRows[count][i], decodedV) + } + count++ + } + return nil +} + +// TestBasicParquetTypes exports a relation with bool, int, float and string +// values to a parquet file, and then asserts that the parquet exporter properly +// encoded the values of the crdb relation. +func TestBasicParquetTypes(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + dir, cleanupDir := testutils.TempDir(t) + defer cleanupDir() + + srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{ExternalIODir: dir}) + defer srv.Stopper().Stop(context.Background()) + sqlDB := sqlutils.MakeSQLRunner(db) + + sqlDB.Exec(t, `CREATE TABLE foo (i INT PRIMARY KEY, x STRING, y INT, z FLOAT, a BOOL, INDEX (y))`) + sqlDB.Exec(t, `INSERT INTO foo VALUES (1, 'Alice', 3, 14.3, true), (2, 'Bob', 2, 24.1, false), +(3, 'Carl', 1, 34.214,true)`) + + sqlDB.Exec(t, `EXPORT INTO PARQUET 'nodelocal://0/order_parquet' FROM SELECT * + FROM foo ORDER BY y ASC LIMIT 2`) + + paths, err := filepath.Glob(filepath.Join(dir, "order_parquet", + parquetExportFilePattern)) + require.NoError(t, err) + + require.Equal(t, 1, len(paths)) + + truth := [][]interface{}{ + {int64(3), "Carl", int64(1), 34.214, true}, + {int64(2), "Bob", int64(2), 24.1, false}} + + err = validateParquetFile(t, paths[0], truth) + require.NoError(t, err) +} + func TestExportUniqueness(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/ccl/importccl/exportparquet.go b/pkg/ccl/importccl/exportparquet.go new file mode 100644 index 000000000000..75a4be7df19e --- /dev/null +++ b/pkg/ccl/importccl/exportparquet.go @@ -0,0 +1,430 @@ +// Copyright 2021 The Cockroach Authors. +// +// Licensed as a CockroachDB Enterprise file under the Cockroach Community +// License (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt + +package importccl + +import ( + "bytes" + "context" + "fmt" + "strings" + + "github.com/cockroachdb/cockroach/pkg/cloud" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo" + "github.com/cockroachdb/cockroach/pkg/sql/execinfra" + "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" + "github.com/cockroachdb/cockroach/pkg/sql/rowenc" + "github.com/cockroachdb/cockroach/pkg/sql/rowexec" + "github.com/cockroachdb/cockroach/pkg/sql/sem/builtins" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util/tracing" + "github.com/cockroachdb/errors" + goparquet "github.com/fraugster/parquet-go" + "github.com/fraugster/parquet-go/parquet" + "github.com/fraugster/parquet-go/parquetschema" + "github.com/lib/pq/oid" +) + +const exportParquetFilePatternDefault = exportFilePatternPart + ".parquet" + +// parquetExporter is used to augment the parquetWriter, encapsulating the internals to make +// exporting oblivious for the consumers. +type parquetExporter struct { + buf *bytes.Buffer + parquetWriter *goparquet.FileWriter + schema *parquetschema.SchemaDefinition + parquetColumns []parquetColumn +} + +// Write appends a record to a parquet file. +func (c *parquetExporter) Write(record map[string]interface{}) error { + return c.parquetWriter.AddData(record) +} + +// Flush is merely a placeholder to mimic the CSV Exporter (we may add their methods +// to an interface in the near future). All flushing is done by parquetExporter.Close(). +func (c *parquetExporter) Flush() error { + return nil +} + +// Close flushes all records to parquetExporter.buf and closes the parquet writer. +func (c *parquetExporter) Close() error { + return c.parquetWriter.Close() +} + +// Bytes results in the slice of bytes. +func (c *parquetExporter) Bytes() []byte { + return c.buf.Bytes() +} + +func (c *parquetExporter) ResetBuffer() { + c.buf.Reset() + c.parquetWriter = buildFileWriter(c.buf, c.schema) +} + +// Len returns length of the buffer with content. +func (c *parquetExporter) Len() int { + return c.buf.Len() +} + +func (c *parquetExporter) FileName(spec execinfrapb.ParquetWriterSpec, part string) string { + pattern := exportParquetFilePatternDefault + if spec.NamePattern != "" { + pattern = spec.NamePattern + } + + fileName := strings.Replace(pattern, exportFilePatternPart, part, -1) + + return fileName +} + +// newParquetExporter creates a new parquet file writer, defines the parquet +// file schema, and initializes a new parquetExporter. +func newParquetExporter( + sp execinfrapb.ParquetWriterSpec, typs []*types.T, +) (*parquetExporter, error) { + + var exporter *parquetExporter + + buf := bytes.NewBuffer([]byte{}) + parquetColumns, err := newParquetColumns(typs) + if err != nil { + return nil, err + } + schema := newParquetSchema(parquetColumns) + + exporter = &parquetExporter{ + buf: buf, + schema: schema, + parquetColumns: parquetColumns, + } + return exporter, nil +} + +// parquetColumn contains the relevant data to map a crdb table column to a parquet table column. +type parquetColumn struct { + name string + crbdType *types.T + + //definition contains all relevant information around the parquet type for the table column + definition *parquetschema.ColumnDefinition + + //encodeFn converts crdb table column value to a native go type. + encodeFn func(datum tree.Datum) (interface{}, error) +} + +// newParquetColumns creates a list of parquet columns, given the input relation's column types +func newParquetColumns(typs []*types.T) ([]parquetColumn, error) { + parquetColumns := make([]parquetColumn, len(typs)) + for i := 0; i < len(typs); i++ { + // TODO(mbutler): figure out how to pass column names to export processor + name := fmt.Sprintf("Col%d", i) + parquetCol, err := newParquetColumn(typs[i], name) + if err != nil { + return nil, err + } + parquetColumns[i] = parquetCol + } + return parquetColumns, nil +} + +// newParquetColumn populates a parquetColumn by finding the right parquet type and defining the +// encodeFn +func newParquetColumn(typ *types.T, name string) (parquetColumn, error) { + col := parquetColumn{} + col.definition = new(parquetschema.ColumnDefinition) + col.definition.SchemaElement = parquet.NewSchemaElement() + col.name = name + col.crbdType = typ + + /* + The type of a parquet column is either a group (i.e. + an array in crdb) or a primitive type (e.g., int, float, boolean, + string) and the repetition can be one of the three following cases: + + - required: exactly one occurrence (i.e. the column value is a scalar, and cannot have null values) + - optional: 0 or 1 occurrence (i.e. same as above, but can have values) + - repeated: 0 or more occurrences (the column value can be an array of values, + so the value within the array will have its own repetition type) + + Right now, the parquet exporter hardcodes each crdb table column to have the `required` + repetition type. + + See this blog post for more on parquet type specification: + https://blog.twitter.com/engineering/en_us/a/2013/dremel-made-simple-with-parquet + */ + col.definition.SchemaElement.RepetitionType = parquet.FieldRepetitionTypePtr(parquet.FieldRepetitionType_REQUIRED) + col.definition.SchemaElement.Name = col.name + + // MB figured out the low level properties of the encoding by running the goland debugger on + // the following vendor example: + // https://github.com/fraugster/parquet-go/blob/master/examples/write-low-level/main.go + switch typ.Family() { + case types.BoolFamily: + col.definition.SchemaElement.Type = parquet.TypePtr(parquet.Type_BOOLEAN) + col.encodeFn = func(d tree.Datum) (interface{}, error) { + return bool(*d.(*tree.DBool)), nil + } + + case types.StringFamily: + col.definition.SchemaElement.Type = parquet.TypePtr(parquet.Type_BYTE_ARRAY) + col.definition.SchemaElement.LogicalType = parquet.NewLogicalType() + col.definition.SchemaElement.LogicalType.STRING = parquet.NewStringType() + col.definition.SchemaElement.ConvertedType = parquet.ConvertedTypePtr(parquet.ConvertedType_UTF8) + col.encodeFn = func(d tree.Datum) (interface{}, error) { + return []byte(*d.(*tree.DString)), nil + } + + case types.IntFamily: + if typ.Oid() == oid.T_int8 { + col.definition.SchemaElement.Type = parquet.TypePtr(parquet.Type_INT64) + col.encodeFn = func(d tree.Datum) (interface{}, error) { + return int64(*d.(*tree.DInt)), nil + } + } else { + col.definition.SchemaElement.Type = parquet.TypePtr(parquet.Type_INT32) + col.encodeFn = func(d tree.Datum) (interface{}, error) { + return int32(*d.(*tree.DInt)), nil + } + } + + case types.FloatFamily: + if typ.Oid() == oid.T_float8 { + col.definition.SchemaElement.Type = parquet.TypePtr(parquet.Type_DOUBLE) + col.encodeFn = func(d tree.Datum) (interface{}, error) { + return float64(*d.(*tree.DFloat)), nil + } + } else { + col.definition.SchemaElement.Type = parquet.TypePtr(parquet.Type_FLOAT) + col.encodeFn = func(d tree.Datum) (interface{}, error) { + return float32(*d.(*tree.DFloat)), nil + } + } + + case types.ArrayFamily: + // TODO(mb): Figure out how to modify the encodeFn for arrays. + // One possibility: recurse on type within array, define encodeFn elsewhere + // col.definition.Children[0] = newParquetColumn(typ.ArrayContents(), name) + return col, errors.Errorf("parquet export does not support array type yet") + + default: + return col, errors.Errorf("parquet export does not support the %v type yet", typ.Family()) + } + + return col, nil +} + +// newParquetSchema creates the schema for the parquet file, +// see example schema: +// https://github.com/fraugster/parquet-go/issues/18#issuecomment-946013210 +// see docs here: +// https://pkg.go.dev/github.com/fraugster/parquet-go/parquetschema#SchemaDefinition +func newParquetSchema(parquetFields []parquetColumn) *parquetschema.SchemaDefinition { + + schemaDefinition := new(parquetschema.SchemaDefinition) + schemaDefinition.RootColumn = new(parquetschema.ColumnDefinition) + schemaDefinition.RootColumn.SchemaElement = parquet.NewSchemaElement() + + for i := 0; i < len(parquetFields); i++ { + schemaDefinition.RootColumn.Children = append(schemaDefinition.RootColumn.Children, + parquetFields[i].definition) + schemaDefinition.RootColumn.SchemaElement.Name = "root" + } + return schemaDefinition +} + +func buildFileWriter( + buf *bytes.Buffer, schema *parquetschema.SchemaDefinition, +) *goparquet.FileWriter { + pw := goparquet.NewFileWriter(buf, + // TODO(MB): allow for user defined compression + goparquet.WithCompressionCodec(parquet.CompressionCodec_SNAPPY), + goparquet.WithSchemaDefinition(schema), + ) + return pw +} + +func newParquetWriterProcessor( + flowCtx *execinfra.FlowCtx, + processorID int32, + spec execinfrapb.ParquetWriterSpec, + input execinfra.RowSource, + output execinfra.RowReceiver, +) (execinfra.Processor, error) { + c := &parquetWriterProcessor{ + flowCtx: flowCtx, + processorID: processorID, + spec: spec, + input: input, + output: output, + } + semaCtx := tree.MakeSemaContext() + if err := c.out.Init(&execinfrapb.PostProcessSpec{}, c.OutputTypes(), &semaCtx, flowCtx.NewEvalCtx()); err != nil { + return nil, err + } + return c, nil +} + +type parquetWriterProcessor struct { + flowCtx *execinfra.FlowCtx + processorID int32 + spec execinfrapb.ParquetWriterSpec + input execinfra.RowSource + out execinfra.ProcOutputHelper + output execinfra.RowReceiver +} + +var _ execinfra.Processor = &parquetWriterProcessor{} + +func (sp *parquetWriterProcessor) OutputTypes() []*types.T { + res := make([]*types.T, len(colinfo.ExportColumns)) + for i := range res { + res[i] = colinfo.ExportColumns[i].Typ + } + return res +} + +func (sp *parquetWriterProcessor) MustBeStreaming() bool { + return false +} + +func (sp *parquetWriterProcessor) Run(ctx context.Context) { + ctx, span := tracing.ChildSpan(ctx, "parquetWriter") + defer span.Finish() + + instanceID := sp.flowCtx.EvalCtx.NodeID.SQLInstanceID() + uniqueID := builtins.GenerateUniqueInt(instanceID) + + err := func() error { + typs := sp.input.OutputTypes() + sp.input.Start(ctx) + input := execinfra.MakeNoMetadataRowSource(sp.input, sp.output) + alloc := &rowenc.DatumAlloc{} + + exporter, err := newParquetExporter(sp.spec, typs) + if err != nil { + return err + } + + parquetRow := make(map[string]interface{}, len(typs)) + chunk := 0 + done := false + for { + var rows int64 + exporter.ResetBuffer() + for { + // If the bytes.Buffer sink exceeds the target size of a Parquet file, we + // flush before exporting any additional rows. + if int64(exporter.buf.Len()) >= sp.spec.ChunkSize { + break + } + if sp.spec.ChunkRows > 0 && rows >= sp.spec.ChunkRows { + break + } + row, err := input.NextRow() + if err != nil { + return err + } + if row == nil { + done = true + break + } + rows++ + + for i, ed := range row { + // TODO(MB): Deal with NULL VALUES + if ed.IsNull() { + return errors.New("NULL value encountered during EXPORT PARQUET, " + + ". Tables with NULL values not supported yet :(") + } + if err := ed.EnsureDecoded(typs[i], alloc); err != nil { + return err + } + edNative, err := exporter.parquetColumns[i].encodeFn(ed.Datum) + if err != nil { + return err + } + parquetRow[exporter.parquetColumns[i].name] = edNative + } + if err := exporter.Write(parquetRow); err != nil { + return err + } + } + if rows < 1 { + break + } + if err := exporter.Flush(); err != nil { + return errors.Wrap(err, "failed to flush parquet exporter") + } + + // Close exporter to ensure buffer and any compression footer is flushed. + err = exporter.Close() + if err != nil { + return errors.Wrapf(err, "failed to close exporting exporter") + } + + conf, err := cloud.ExternalStorageConfFromURI(sp.spec.Destination, sp.spec.User()) + if err != nil { + return err + } + es, err := sp.flowCtx.Cfg.ExternalStorage(ctx, conf) + if err != nil { + return err + } + defer es.Close() + + part := fmt.Sprintf("n%d.%d", uniqueID, chunk) + chunk++ + filename := exporter.FileName(sp.spec, part) + + size := exporter.Len() + + if err := cloud.WriteFile(ctx, es, filename, bytes.NewReader(exporter.Bytes())); err != nil { + return err + } + res := rowenc.EncDatumRow{ + rowenc.DatumToEncDatum( + types.String, + tree.NewDString(filename), + ), + rowenc.DatumToEncDatum( + types.Int, + tree.NewDInt(tree.DInt(rows)), + ), + rowenc.DatumToEncDatum( + types.Int, + tree.NewDInt(tree.DInt(size)), + ), + } + + cs, err := sp.out.EmitRow(ctx, res, sp.output) + if err != nil { + return err + } + if cs != execinfra.NeedMoreRows { + // TODO(dt): presumably this is because our recv already closed due to + // another error... so do we really need another one? + return errors.New("unexpected closure of consumer") + } + if done { + break + } + } + + return nil + }() + + // TODO(dt): pick up tracing info in trailing meta + execinfra.DrainAndClose( + ctx, sp.output, err, func(context.Context) {} /* pushTrailingMeta */, sp.input) +} + +func init() { + rowexec.NewParquetWriterProcessor = newParquetWriterProcessor +} diff --git a/pkg/roachpb/io-formats.pb.go b/pkg/roachpb/io-formats.pb.go index 73c154f355ae..5aeff1ddccef 100644 --- a/pkg/roachpb/io-formats.pb.go +++ b/pkg/roachpb/io-formats.pb.go @@ -303,8 +303,6 @@ func (m *CSVOptions) XXX_DiscardUnknown() { var xxx_messageInfo_CSVOptions proto.InternalMessageInfo type ParquetOptions struct { - // null_encoding, if not nil, is the string which identifies a NULL. Can be the empty string. - NullEncoding *string `protobuf:"bytes,1,opt,name=null_encoding,json=nullEncoding" json:"null_encoding,omitempty"` } func (m *ParquetOptions) Reset() { *m = ParquetOptions{} } @@ -568,70 +566,70 @@ func init() { func init() { proto.RegisterFile("roachpb/io-formats.proto", fileDescriptor_16c5b508d081359f) } var fileDescriptor_16c5b508d081359f = []byte{ - // 1003 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x5f, 0x6f, 0xdb, 0x54, - 0x14, 0x8f, 0xf3, 0xcf, 0xf1, 0x49, 0xd2, 0x7a, 0x97, 0x09, 0x59, 0x13, 0x98, 0xe0, 0x31, 0xd4, - 0x01, 0x73, 0xa5, 0x42, 0x25, 0x04, 0x0f, 0x68, 0x4d, 0x5b, 0xd6, 0xa9, 0x4d, 0xb6, 0x44, 0xdb, - 0x03, 0x2f, 0x96, 0x71, 0x6e, 0x53, 0x53, 0xdb, 0xd7, 0xbd, 0xd7, 0x4e, 0x96, 0x7d, 0x0a, 0x3e, - 0x13, 0x4f, 0xe5, 0x6d, 0x6f, 0x4c, 0x42, 0x42, 0xd0, 0x7e, 0x0f, 0x84, 0xee, 0xb5, 0x9d, 0xd8, - 0x8d, 0x37, 0xfa, 0x76, 0x74, 0xfe, 0xfc, 0x7c, 0xce, 0xef, 0x77, 0xee, 0x31, 0x68, 0x94, 0xd8, - 0xce, 0x59, 0xf8, 0xf3, 0xb6, 0x4b, 0x1e, 0x9d, 0x12, 0xea, 0xdb, 0x11, 0x33, 0x43, 0x4a, 0x22, - 0x82, 0xee, 0x38, 0xc4, 0x39, 0x17, 0x51, 0x33, 0xcd, 0xb9, 0x77, 0x77, 0x4a, 0xa6, 0x44, 0x44, - 0xb7, 0xb9, 0x95, 0x24, 0x1a, 0xff, 0x36, 0xa0, 0x73, 0x34, 0x3c, 0x74, 0x3d, 0x7c, 0x28, 0x00, - 0xd0, 0x13, 0x68, 0x26, 0x50, 0x9a, 0xd4, 0x93, 0xb6, 0x36, 0x76, 0xbe, 0x30, 0xd7, 0xa0, 0xcc, - 0x7c, 0x81, 0xb9, 0x32, 0xf7, 0xea, 0x97, 0x7f, 0x7d, 0x52, 0x19, 0xa5, 0xf5, 0x68, 0x17, 0x6a, - 0x0e, 0x9b, 0x69, 0xd5, 0x9e, 0xb4, 0xd5, 0xde, 0xf9, 0xb8, 0x04, 0xa6, 0x3f, 0x7e, 0x39, 0x0c, - 0x23, 0x97, 0x04, 0x2c, 0xad, 0xe4, 0xf9, 0xe8, 0x08, 0x14, 0x7f, 0xc1, 0x2e, 0x3c, 0x8b, 0xc4, - 0x91, 0x56, 0x13, 0xc5, 0x9f, 0x97, 0x14, 0x9f, 0x2c, 0xc6, 0xcf, 0x8f, 0x87, 0x71, 0x74, 0xea, - 0x7a, 0xb8, 0x88, 0xd2, 0x12, 0xe5, 0xc3, 0x38, 0x42, 0x3f, 0x80, 0x1c, 0x4e, 0x2d, 0x87, 0x84, - 0x0b, 0xad, 0x2e, 0x80, 0x7a, 0x25, 0x40, 0xcf, 0xa6, 0x7d, 0x12, 0x2e, 0x8a, 0x10, 0xcd, 0x50, - 0x38, 0xd1, 0x13, 0x80, 0xa4, 0x97, 0x49, 0xec, 0x87, 0x9a, 0x22, 0x30, 0xee, 0x97, 0x36, 0xc3, - 0x2e, 0x3c, 0x9e, 0x53, 0x84, 0x49, 0x06, 0xd9, 0x8f, 0xfd, 0x30, 0x6d, 0x45, 0xc0, 0x34, 0xdf, - 0xd3, 0xca, 0xfe, 0x1a, 0x46, 0x33, 0x14, 0x4e, 0xf4, 0x2d, 0xd4, 0xed, 0x19, 0x25, 0x5a, 0x4b, - 0x54, 0xeb, 0x25, 0xd5, 0x8f, 0x67, 0x94, 0x14, 0x6b, 0x45, 0x05, 0x1a, 0x43, 0xdb, 0x21, 0x7e, - 0x48, 0x31, 0x63, 0x2e, 0x09, 0xb4, 0x86, 0x90, 0xf5, 0xcb, 0xff, 0x93, 0xb5, 0xbf, 0x2a, 0x49, - 0xd1, 0xf2, 0x28, 0xe8, 0x21, 0x74, 0x99, 0x3d, 0xc3, 0x16, 0xc5, 0xbf, 0x60, 0x27, 0xc2, 0x13, - 0x4d, 0xee, 0x49, 0x5b, 0xad, 0x34, 0xb3, 0xc3, 0x43, 0xa3, 0x34, 0x62, 0x60, 0x80, 0xdc, 0x7e, - 0xb5, 0x41, 0x7e, 0x11, 0x9c, 0x07, 0x64, 0x1e, 0xa8, 0x15, 0x24, 0x43, 0xad, 0x3f, 0x7e, 0xa9, - 0x4a, 0x48, 0x85, 0xce, 0x49, 0xaa, 0x1a, 0x17, 0x54, 0xad, 0xa2, 0x2e, 0x28, 0x4b, 0x56, 0xd5, - 0x1a, 0x02, 0x68, 0x26, 0x42, 0xa9, 0xf5, 0xc4, 0xe6, 0xa4, 0xa8, 0x0d, 0xd4, 0x82, 0x3a, 0x9f, - 0x5b, 0x6d, 0x1a, 0xbb, 0xd0, 0xce, 0xf5, 0x2c, 0x02, 0x71, 0x44, 0xd4, 0x0a, 0xb7, 0x06, 0x24, - 0xc0, 0xaa, 0xc4, 0xad, 0x1f, 0x5f, 0xbb, 0xa1, 0x5a, 0xe5, 0xd6, 0x1e, 0xb7, 0x6a, 0xc6, 0x9f, - 0x12, 0xc0, 0x6a, 0x11, 0xd1, 0x3d, 0x68, 0x38, 0xc4, 0xf7, 0x6d, 0xb1, 0xfd, 0x8d, 0x74, 0x9e, - 0xc4, 0x85, 0x74, 0x90, 0xb9, 0x81, 0x83, 0x48, 0x2c, 0x75, 0x16, 0xcd, 0x9c, 0x9c, 0x93, 0x20, - 0xf6, 0x3c, 0x0b, 0x07, 0x0e, 0x99, 0xb8, 0xc1, 0x54, 0x6c, 0xaf, 0x22, 0xb2, 0xa4, 0x51, 0x87, - 0x87, 0x0e, 0xd2, 0x08, 0xd2, 0xa0, 0xce, 0xce, 0xdd, 0x50, 0xac, 0x65, 0x37, 0x53, 0x8b, 0x7b, - 0x04, 0xb1, 0x11, 0x75, 0x9d, 0xc8, 0xba, 0x88, 0x49, 0x84, 0x99, 0xd0, 0x6b, 0x45, 0xac, 0x08, - 0x3d, 0x17, 0x11, 0xf4, 0x29, 0x28, 0x94, 0xcc, 0x2d, 0xcf, 0xf5, 0xdd, 0x48, 0x6c, 0x55, 0x2d, - 0x7b, 0x01, 0x94, 0xcc, 0x8f, 0xb9, 0xd7, 0xf8, 0x1e, 0x36, 0x9e, 0xd9, 0xf4, 0x22, 0xc6, 0x51, - 0x36, 0xe0, 0x5a, 0x93, 0xd2, 0xbb, 0x9a, 0x34, 0x7e, 0xab, 0xc1, 0x07, 0x25, 0xcf, 0x8c, 0x43, - 0xf0, 0xef, 0x32, 0x1c, 0xda, 0xd4, 0x8e, 0x08, 0x2d, 0x70, 0xd5, 0xa1, 0x64, 0x3e, 0xce, 0x22, - 0xe8, 0x11, 0x6c, 0x9e, 0xba, 0xd8, 0x9b, 0xe4, 0x92, 0xf3, 0xd4, 0x6d, 0x88, 0xe0, 0x2a, 0x7d, - 0x00, 0x32, 0x0e, 0x1c, 0x8f, 0x30, 0x2c, 0xb8, 0xdb, 0xd8, 0x31, 0x6f, 0xf7, 0xf2, 0xcd, 0x83, - 0xa4, 0x2a, 0x53, 0x24, 0x05, 0x41, 0x3d, 0x68, 0xa5, 0x26, 0x15, 0x54, 0x67, 0xdf, 0x5d, 0x7a, - 0xd1, 0x7d, 0x80, 0x33, 0x9b, 0x59, 0x98, 0x39, 0x76, 0x88, 0x0b, 0x5c, 0x2b, 0x67, 0x36, 0x3b, - 0x10, 0x6e, 0xf4, 0x11, 0x34, 0xd3, 0x84, 0x66, 0x0e, 0x24, 0xf5, 0x2d, 0xb5, 0x94, 0xcb, 0xb4, - 0x2c, 0x72, 0xdd, 0x7a, 0xe7, 0x42, 0x14, 0xb4, 0x84, 0x52, 0x2d, 0x4d, 0x90, 0xd3, 0x31, 0x91, - 0x02, 0x8d, 0x01, 0x9e, 0x61, 0xaa, 0x56, 0xf8, 0x63, 0x78, 0xec, 0xcd, 0xed, 0x05, 0x53, 0x25, - 0xd4, 0x81, 0x56, 0x42, 0x88, 0xed, 0xa9, 0xd5, 0xa7, 0xf5, 0x96, 0xa2, 0x82, 0xc1, 0xa0, 0x5b, - 0xb8, 0x70, 0xc8, 0x00, 0x65, 0x82, 0xc5, 0x77, 0x70, 0x51, 0xb9, 0x95, 0x9b, 0x8f, 0xc4, 0xbb, - 0x13, 0x5a, 0x29, 0xd9, 0x48, 0xdc, 0x83, 0x3e, 0x03, 0xf0, 0xed, 0x57, 0x23, 0x32, 0x1f, 0xbb, - 0xaf, 0x13, 0x91, 0xb2, 0xf2, 0x9c, 0xdf, 0xf8, 0x5d, 0xe2, 0x5f, 0xcd, 0x1d, 0xb3, 0x1b, 0x75, - 0x52, 0x79, 0x5d, 0x91, 0x85, 0x6a, 0x19, 0x0b, 0xe8, 0x6b, 0x40, 0xee, 0x34, 0x20, 0x14, 0x5b, - 0x71, 0xc0, 0xe2, 0x30, 0x24, 0x94, 0x5f, 0x9f, 0x5a, 0x4e, 0xb8, 0x3b, 0x49, 0xfc, 0xc5, 0x2a, - 0x8c, 0xbe, 0x83, 0x0f, 0xd7, 0x8b, 0x2c, 0x8f, 0x4c, 0xc5, 0x56, 0x64, 0x13, 0xde, 0x5d, 0x2b, - 0x3c, 0x26, 0x53, 0x63, 0x17, 0xd4, 0x9b, 0xe7, 0xbd, 0xd8, 0xa7, 0x54, 0xaa, 0xd6, 0x1f, 0x55, - 0x68, 0xe7, 0x2e, 0x32, 0xea, 0xdf, 0xf8, 0xaf, 0x3e, 0x78, 0xff, 0x05, 0x37, 0x4b, 0x7f, 0xa9, - 0x0f, 0xa0, 0x9d, 0x1e, 0x07, 0x9f, 0x4c, 0xb0, 0x60, 0x28, 0x9b, 0x1a, 0x92, 0xc0, 0x09, 0x99, - 0x60, 0x4e, 0x36, 0x73, 0xce, 0xb0, 0x6f, 0x3f, 0x1d, 0x0f, 0x07, 0xb9, 0x2b, 0xc4, 0xb3, 0x96, - 0x7e, 0xf4, 0x15, 0x6c, 0xfa, 0xf6, 0x2b, 0x8b, 0x62, 0x87, 0xd0, 0x89, 0xc5, 0xb8, 0x2e, 0xf9, - 0x37, 0xd2, 0xe5, 0xba, 0x88, 0x98, 0x90, 0x66, 0x1b, 0xd4, 0x2c, 0x73, 0xf9, 0x94, 0x1b, 0xb9, - 0xf4, 0xcd, 0x24, 0xba, 0x7a, 0xcb, 0xb7, 0xb8, 0x4e, 0xdf, 0x40, 0x33, 0xfd, 0x2b, 0xc8, 0x50, - 0x1b, 0xf6, 0x0f, 0xd5, 0x0a, 0xda, 0x84, 0xf6, 0xde, 0xd1, 0xc0, 0x1a, 0x1d, 0xf4, 0x87, 0xa3, - 0xfd, 0x71, 0xf2, 0x67, 0xe0, 0xdd, 0x2e, 0x3d, 0xd5, 0xbd, 0x87, 0x97, 0xff, 0xe8, 0x95, 0xcb, - 0x2b, 0x5d, 0x7a, 0x73, 0xa5, 0x4b, 0x6f, 0xaf, 0x74, 0xe9, 0xef, 0x2b, 0x5d, 0xfa, 0xf5, 0x5a, - 0xaf, 0xbc, 0xb9, 0xd6, 0x2b, 0x6f, 0xaf, 0xf5, 0xca, 0x4f, 0x72, 0x4a, 0xe8, 0x7f, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xbd, 0x9c, 0x7e, 0x1f, 0x21, 0x09, 0x00, 0x00, + // 996 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x4d, 0x6f, 0xdb, 0x46, + 0x10, 0x15, 0xf5, 0x45, 0x71, 0x24, 0xd9, 0xcc, 0x36, 0x28, 0x88, 0xa0, 0x65, 0x55, 0xa6, 0x29, + 0x9c, 0xb6, 0x91, 0x01, 0xb7, 0x06, 0x8a, 0x5e, 0x8a, 0x58, 0xb6, 0x1b, 0x07, 0xb6, 0x94, 0x48, + 0x48, 0x0e, 0xbd, 0x10, 0x2c, 0xb5, 0x96, 0x59, 0x93, 0x5c, 0x7a, 0x97, 0x94, 0xa2, 0xfc, 0x8a, + 0xfe, 0xa6, 0x9e, 0xdc, 0x5b, 0x6e, 0x0d, 0x50, 0xa0, 0x68, 0xed, 0xff, 0x51, 0x14, 0xbb, 0x5c, + 0x4a, 0xa4, 0xcd, 0xa6, 0xb9, 0x2d, 0x66, 0xe6, 0x3d, 0xee, 0xbc, 0x37, 0x3b, 0x04, 0x83, 0x12, + 0xc7, 0x3d, 0x8b, 0x7e, 0xda, 0xf6, 0xc8, 0xa3, 0x53, 0x42, 0x03, 0x27, 0x66, 0xfd, 0x88, 0x92, + 0x98, 0xa0, 0x3b, 0x2e, 0x71, 0xcf, 0x45, 0xb6, 0x2f, 0x6b, 0xee, 0xdd, 0x9d, 0x91, 0x19, 0x11, + 0xd9, 0x6d, 0x7e, 0x4a, 0x0b, 0xad, 0x7f, 0x1a, 0xd0, 0x39, 0x1a, 0x1d, 0x7a, 0x3e, 0x3e, 0x14, + 0x04, 0xe8, 0x09, 0x34, 0x53, 0x2a, 0x43, 0xe9, 0x29, 0x5b, 0x1b, 0x3b, 0x5f, 0xf4, 0x6f, 0x51, + 0xf5, 0xf3, 0x80, 0xfe, 0xfa, 0xb8, 0x57, 0xbf, 0xfc, 0xf3, 0x93, 0xca, 0x58, 0xe2, 0xd1, 0x2e, + 0xd4, 0x5c, 0x36, 0x37, 0xaa, 0x3d, 0x65, 0xab, 0xbd, 0xf3, 0x71, 0x09, 0xcd, 0x60, 0xf2, 0x72, + 0x14, 0xc5, 0x1e, 0x09, 0x99, 0x44, 0xf2, 0x7a, 0x74, 0x04, 0x5a, 0xb0, 0x64, 0x17, 0xbe, 0x4d, + 0x92, 0xd8, 0xa8, 0x09, 0xf0, 0xe7, 0x25, 0xe0, 0x93, 0xe5, 0xe4, 0xf9, 0xf1, 0x28, 0x89, 0x4f, + 0x3d, 0x1f, 0x17, 0x59, 0x5a, 0x02, 0x3e, 0x4a, 0x62, 0xf4, 0x3d, 0xa8, 0xd1, 0xcc, 0x76, 0x49, + 0xb4, 0x34, 0xea, 0x82, 0xa8, 0x57, 0x42, 0xf4, 0x6c, 0x36, 0x20, 0xd1, 0xb2, 0x48, 0xd1, 0x8c, + 0x44, 0x10, 0x3d, 0x01, 0x48, 0xef, 0x32, 0x4d, 0x82, 0xc8, 0xd0, 0x04, 0xc7, 0xfd, 0xd2, 0xcb, + 0xb0, 0x0b, 0x9f, 0xd7, 0x14, 0x69, 0xd2, 0x46, 0xf6, 0x93, 0x20, 0x92, 0x57, 0x11, 0x34, 0xcd, + 0x77, 0x5c, 0x65, 0xff, 0x16, 0x47, 0x33, 0x12, 0x41, 0xf4, 0x2d, 0xd4, 0x9d, 0x39, 0x25, 0x46, + 0x4b, 0xa0, 0xcd, 0x12, 0xf4, 0xe3, 0x39, 0x25, 0x45, 0xac, 0x40, 0xa0, 0x09, 0xb4, 0x5d, 0x12, + 0x44, 0x14, 0x33, 0xe6, 0x91, 0xd0, 0x68, 0x08, 0x5b, 0xbf, 0xfc, 0x3f, 0x5b, 0x07, 0x6b, 0x88, + 0x64, 0xcb, 0xb3, 0xa0, 0x87, 0xd0, 0x65, 0xce, 0x1c, 0xdb, 0x14, 0xff, 0x8c, 0xdd, 0x18, 0x4f, + 0x0d, 0xb5, 0xa7, 0x6c, 0xb5, 0x64, 0x65, 0x87, 0xa7, 0xc6, 0x32, 0x63, 0x61, 0x80, 0xdc, 0x7c, + 0xb5, 0x41, 0x7d, 0x11, 0x9e, 0x87, 0x64, 0x11, 0xea, 0x15, 0xa4, 0x42, 0x6d, 0x30, 0x79, 0xa9, + 0x2b, 0x48, 0x87, 0xce, 0x89, 0x74, 0x8d, 0x1b, 0xaa, 0x57, 0x51, 0x17, 0xb4, 0x95, 0xaa, 0x7a, + 0x0d, 0x01, 0x34, 0x53, 0xa3, 0xf4, 0x7a, 0x7a, 0xe6, 0xa2, 0xe8, 0x0d, 0xd4, 0x82, 0x3a, 0xef, + 0x5b, 0x6f, 0x5a, 0xbb, 0xd0, 0xce, 0xdd, 0x59, 0x24, 0x92, 0x98, 0xe8, 0x15, 0x7e, 0x1a, 0x92, + 0x10, 0xeb, 0x0a, 0x3f, 0xfd, 0xf0, 0xda, 0x8b, 0xf4, 0x2a, 0x3f, 0xed, 0xf1, 0x53, 0xcd, 0xfa, + 0x43, 0x01, 0x58, 0x0f, 0x22, 0xba, 0x07, 0x0d, 0x97, 0x04, 0x81, 0x23, 0xa6, 0xbf, 0x21, 0xfb, + 0x49, 0x43, 0xc8, 0x04, 0x95, 0x1f, 0x70, 0x18, 0x8b, 0xa1, 0xce, 0xb2, 0x59, 0x90, 0x6b, 0x12, + 0x26, 0xbe, 0x6f, 0xe3, 0xd0, 0x25, 0x53, 0x2f, 0x9c, 0x89, 0xe9, 0xd5, 0x44, 0x95, 0x32, 0xee, + 0xf0, 0xd4, 0x81, 0xcc, 0x20, 0x03, 0xea, 0xec, 0xdc, 0x8b, 0xc4, 0x58, 0x76, 0x33, 0xb7, 0x78, + 0x44, 0x08, 0x1b, 0x53, 0xcf, 0x8d, 0xed, 0x8b, 0x84, 0xc4, 0x98, 0x09, 0xbf, 0xd6, 0xc2, 0x8a, + 0xd4, 0x73, 0x91, 0x41, 0x9f, 0x82, 0x46, 0xc9, 0xc2, 0xf6, 0xbd, 0xc0, 0x8b, 0xc5, 0x54, 0xd5, + 0xb2, 0x17, 0x40, 0xc9, 0xe2, 0x98, 0x47, 0x2d, 0x1d, 0x36, 0x9e, 0x39, 0xf4, 0x22, 0xc1, 0xb1, + 0x6c, 0xd0, 0xfa, 0xb5, 0x06, 0x1f, 0x94, 0xbc, 0x1d, 0xfe, 0x5d, 0x4e, 0xc6, 0x70, 0xe4, 0x50, + 0x27, 0x26, 0xb4, 0x20, 0x40, 0x87, 0x92, 0xc5, 0x24, 0xcb, 0xa0, 0x47, 0xb0, 0x79, 0xea, 0x61, + 0x7f, 0x9a, 0x2b, 0xce, 0xeb, 0xb1, 0x21, 0x92, 0xeb, 0xf2, 0x21, 0xa8, 0x38, 0x74, 0x7d, 0xc2, + 0xb0, 0x10, 0x64, 0x63, 0xa7, 0xff, 0x7e, 0xcf, 0xb9, 0x7f, 0x90, 0xa2, 0x32, 0x99, 0x25, 0x09, + 0xea, 0x41, 0x4b, 0x1e, 0xa9, 0xd0, 0x2f, 0xfb, 0xee, 0x2a, 0x8a, 0xee, 0x03, 0x9c, 0x39, 0xcc, + 0xc6, 0xcc, 0x75, 0x22, 0x5c, 0x10, 0x50, 0x3b, 0x73, 0xd8, 0x81, 0x08, 0xa3, 0x8f, 0xa0, 0x29, + 0x0b, 0x9a, 0x39, 0x12, 0x19, 0x5b, 0x19, 0xa4, 0x96, 0x19, 0x54, 0x74, 0xb9, 0xf5, 0x9f, 0x2e, + 0x17, 0x0c, 0x82, 0x52, 0x83, 0xfa, 0xa0, 0xca, 0x36, 0x91, 0x06, 0x8d, 0x21, 0x9e, 0x63, 0xaa, + 0x57, 0xf8, 0x84, 0x3f, 0xf6, 0x17, 0xce, 0x92, 0xe9, 0x0a, 0xea, 0x40, 0x2b, 0x15, 0xc4, 0xf1, + 0xf5, 0xea, 0xd3, 0x7a, 0x4b, 0xd3, 0xc1, 0x62, 0xd0, 0x2d, 0xac, 0x2d, 0x64, 0x81, 0x36, 0xc5, + 0xe2, 0x3b, 0xb8, 0xe8, 0xdc, 0x3a, 0xcc, 0x5b, 0xe2, 0xb7, 0x13, 0x5e, 0x69, 0x59, 0x4b, 0x3c, + 0x82, 0x3e, 0x03, 0x08, 0x9c, 0x57, 0x63, 0xb2, 0x98, 0x78, 0xaf, 0x53, 0x93, 0x32, 0x78, 0x2e, + 0x6e, 0xfd, 0xa6, 0xf0, 0xaf, 0xe6, 0x36, 0xd4, 0x0d, 0x9c, 0x52, 0x8e, 0x2b, 0xaa, 0x50, 0x2d, + 0x53, 0x01, 0x7d, 0x0d, 0xc8, 0x9b, 0x85, 0x84, 0x62, 0x3b, 0x09, 0x59, 0x12, 0x45, 0x84, 0xf2, + 0x95, 0x52, 0xcb, 0x19, 0x77, 0x27, 0xcd, 0xbf, 0x58, 0xa7, 0xd1, 0x77, 0xf0, 0xe1, 0x6d, 0x90, + 0xed, 0x93, 0x99, 0x98, 0x8a, 0xac, 0xc3, 0xbb, 0xb7, 0x80, 0xc7, 0x64, 0x66, 0xed, 0x82, 0x7e, + 0x73, 0x67, 0x17, 0xef, 0xa9, 0x94, 0xba, 0xf5, 0x7b, 0x15, 0xda, 0xb9, 0x35, 0x8b, 0x06, 0x37, + 0x7e, 0x96, 0x0f, 0xde, 0xbd, 0x96, 0xfb, 0xa5, 0xff, 0xc9, 0x07, 0xd0, 0x96, 0x2f, 0x3e, 0x20, + 0x53, 0x2c, 0x14, 0xca, 0xba, 0x86, 0x34, 0x71, 0x42, 0xa6, 0x98, 0x8b, 0xcd, 0xdc, 0x33, 0x1c, + 0x38, 0x4f, 0x27, 0xa3, 0x61, 0x6e, 0xb5, 0xf0, 0xaa, 0x55, 0x1c, 0x7d, 0x05, 0x9b, 0x81, 0xf3, + 0xca, 0xa6, 0xd8, 0x25, 0x74, 0x6a, 0x33, 0xee, 0x4b, 0xfe, 0x8d, 0x74, 0xb9, 0x2f, 0x22, 0x27, + 0xac, 0xd9, 0x06, 0x3d, 0xab, 0x5c, 0x3d, 0xe5, 0x46, 0xae, 0x7c, 0x33, 0xcd, 0xae, 0xdf, 0xf2, + 0x7b, 0xac, 0x9c, 0x6f, 0xa0, 0x29, 0x57, 0xbd, 0x0a, 0xb5, 0xd1, 0xe0, 0x50, 0xaf, 0xa0, 0x4d, + 0x68, 0xef, 0x1d, 0x0d, 0xed, 0xf1, 0xc1, 0x60, 0x34, 0xde, 0x9f, 0xa4, 0xeb, 0x9e, 0xdf, 0x76, + 0x15, 0xa9, 0xee, 0x3d, 0xbc, 0xfc, 0xdb, 0xac, 0x5c, 0x5e, 0x99, 0xca, 0x9b, 0x2b, 0x53, 0x79, + 0x7b, 0x65, 0x2a, 0x7f, 0x5d, 0x99, 0xca, 0x2f, 0xd7, 0x66, 0xe5, 0xcd, 0xb5, 0x59, 0x79, 0x7b, + 0x6d, 0x56, 0x7e, 0x54, 0xa5, 0xa0, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x69, 0x03, 0x9f, + 0xf6, 0x08, 0x00, 0x00, } func (m *IOFileFormat) Marshal() (dAtA []byte, err error) { @@ -801,13 +799,6 @@ func (m *ParquetOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.NullEncoding != nil { - i -= len(*m.NullEncoding) - copy(dAtA[i:], *m.NullEncoding) - i = encodeVarintIoFormats(dAtA, i, uint64(len(*m.NullEncoding))) - i-- - dAtA[i] = 0xa - } return len(dAtA) - i, nil } @@ -1079,10 +1070,6 @@ func (m *ParquetOptions) Size() (n int) { } var l int _ = l - if m.NullEncoding != nil { - l = len(*m.NullEncoding) - n += 1 + l + sovIoFormats(uint64(l)) - } return n } @@ -1680,39 +1667,6 @@ func (m *ParquetOptions) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: ParquetOptions: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NullEncoding", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowIoFormats - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthIoFormats - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthIoFormats - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - s := string(dAtA[iNdEx:postIndex]) - m.NullEncoding = &s - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipIoFormats(dAtA[iNdEx:]) diff --git a/pkg/roachpb/io-formats.proto b/pkg/roachpb/io-formats.proto index 08d67076a980..c112794a1dbe 100644 --- a/pkg/roachpb/io-formats.proto +++ b/pkg/roachpb/io-formats.proto @@ -65,8 +65,6 @@ message CSVOptions { } message ParquetOptions { - // null_encoding, if not nil, is the string which identifies a NULL. Can be the empty string. - optional string null_encoding = 1 [(gogoproto.nullable) = true]; } // MySQLOutfileOptions describe the format of mysql's outfile. diff --git a/pkg/sql/colexec/colbuilder/execplan.go b/pkg/sql/colexec/colbuilder/execplan.go index ebfda889fc35..81cc03da9613 100644 --- a/pkg/sql/colexec/colbuilder/execplan.go +++ b/pkg/sql/colexec/colbuilder/execplan.go @@ -250,6 +250,7 @@ var ( errBackupDataWrap = errors.New("core.BackupData is not supported") errBackfillerWrap = errors.New("core.Backfiller is not supported (not an execinfra.RowSource)") errCSVWriterWrap = errors.New("core.CSVWriter is not supported (not an execinfra.RowSource)") + errParquetWriterWrap = errors.New("core.ParquetWriter is not supported (not an execinfra.RowSource)") errSamplerWrap = errors.New("core.Sampler is not supported (not an execinfra.RowSource)") errSampleAggregatorWrap = errors.New("core.SampleAggregator is not supported (not an execinfra.RowSource)") errExperimentalWrappingProhibited = errors.New("wrapping for non-JoinReader and non-LocalPlanNode cores is prohibited in vectorize=experimental_always") @@ -277,6 +278,8 @@ func canWrap(mode sessiondatapb.VectorizeExecMode, spec *execinfrapb.ProcessorSp return errReadImportWrap case spec.Core.CSVWriter != nil: return errCSVWriterWrap + case spec.Core.ParquetWriter != nil: + return errParquetWriterWrap case spec.Core.Sampler != nil: return errSamplerWrap case spec.Core.SampleAggregator != nil: diff --git a/pkg/sql/distsql_physical_planner.go b/pkg/sql/distsql_physical_planner.go index e764658a177e..1bc07657df7c 100644 --- a/pkg/sql/distsql_physical_planner.go +++ b/pkg/sql/distsql_physical_planner.go @@ -3881,7 +3881,7 @@ func (dsp *DistSQLPlanner) createPlanForWindow( } // createPlanForExport creates a physical plan for EXPORT. -// We add a new stage of [filetype]Writer processors to the input plan. +// We add a new stage of CSV/Parquet Writer processors to the input plan. func (dsp *DistSQLPlanner) createPlanForExport( planCtx *PlanningCtx, n *exportNode, ) (*PhysicalPlan, error) { @@ -3892,7 +3892,7 @@ func (dsp *DistSQLPlanner) createPlanForExport( var core execinfrapb.ProcessorCoreUnion - if n.csvOpts != nil{ + if n.csvOpts != nil { core.CSVWriter = &execinfrapb.CSVWriterSpec{ Destination: n.destination, NamePattern: n.fileNamePattern, @@ -3902,21 +3902,20 @@ func (dsp *DistSQLPlanner) createPlanForExport( CompressionCodec: n.fileCompression, UserProto: planCtx.planner.User().EncodeProto(), } - } else if n.parquetOpts != nil{ + } else if n.parquetOpts != nil { core.ParquetWriter = &execinfrapb.ParquetWriterSpec{ - Destination: n.destination, - NamePattern: n.fileNamePattern, - Options: *n.parquetOpts, - ChunkRows: int64(n.chunkRows), - ChunkSize: n.chunkSize, - CompressionCodec: n.fileCompression, - UserProto: planCtx.planner.User().EncodeProto(), + Destination: n.destination, + NamePattern: n.fileNamePattern, + Options: *n.parquetOpts, + ChunkRows: int64(n.chunkRows), + ChunkSize: n.chunkSize, + UserProto: planCtx.planner.User().EncodeProto(), } - } else{ - return nil, errors.Errorf("PARQUET AND CSV OPTS BOTH EMPTY?!?") + } else { + return nil, errors.AssertionFailedf("parquetOpts and csvOpts are both empty. " + + "One must be not nil") } - resTypes := make([]*types.T, len(colinfo.ExportColumns)) for i := range colinfo.ExportColumns { resTypes[i] = colinfo.ExportColumns[i].Typ diff --git a/pkg/sql/execinfrapb/processors_bulk_io.pb.go b/pkg/sql/execinfrapb/processors_bulk_io.pb.go index 99ed718c9ce9..280ca7ece6b3 100644 --- a/pkg/sql/execinfrapb/processors_bulk_io.pb.go +++ b/pkg/sql/execinfrapb/processors_bulk_io.pb.go @@ -10,11 +10,6 @@ package execinfrapb import ( encoding_binary "encoding/binary" fmt "fmt" - io "io" - math "math" - math_bits "math/bits" - time "time" - _ "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" github_com_cockroachdb_cockroach_pkg_jobs_jobspb "github.com/cockroachdb/cockroach/pkg/jobs/jobspb" roachpb "github.com/cockroachdb/cockroach/pkg/roachpb" @@ -25,6 +20,10 @@ import ( _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + io "io" + math "math" + math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. @@ -661,9 +660,7 @@ func (*SplitAndScatterSpec_RestoreEntryChunk) Descriptor() ([]byte, []int) { func (m *SplitAndScatterSpec_RestoreEntryChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *SplitAndScatterSpec_RestoreEntryChunk) XXX_Marshal( - b []byte, deterministic bool, -) ([]byte, error) { +func (m *SplitAndScatterSpec_RestoreEntryChunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) if err != nil { @@ -732,6 +729,55 @@ func (m *CSVWriterSpec) XXX_DiscardUnknown() { var xxx_messageInfo_CSVWriterSpec proto.InternalMessageInfo +// ParquetWriterSpec is the specification for a processor that consumes rows and +// writes them to Parquet files at uri. It outputs a row per file written with +// the file name, row count and byte size. +type ParquetWriterSpec struct { + // destination as a cloud.ExternalStorage URI pointing to an export store + // location (directory). + Destination string `protobuf:"bytes,1,opt,name=destination" json:"destination"` + NamePattern string `protobuf:"bytes,2,opt,name=name_pattern,json=namePattern" json:"name_pattern"` + Options roachpb.ParquetOptions `protobuf:"bytes,3,opt,name=options" json:"options"` + // chunk_rows is num rows to write per file. 0 = no limit. + ChunkRows int64 `protobuf:"varint,4,opt,name=chunk_rows,json=chunkRows" json:"chunk_rows"` + // chunk_size is the target byte size per file. + ChunkSize int64 `protobuf:"varint,7,opt,name=chunk_size,json=chunkSize" json:"chunk_size"` + // compression_codec specifies compression used for exported file. THIS IS ISN'T USED RIGHT NOW + CompressionCodec FileCompression `protobuf:"varint,5,opt,name=compression_codec,json=compressionCodec,enum=cockroach.sql.distsqlrun.FileCompression" json:"compression_codec"` + // User who initiated the export. This is used to check access privileges + // when using FileTable ExternalStorage. + UserProto github_com_cockroachdb_cockroach_pkg_security.SQLUsernameProto `protobuf:"bytes,6,opt,name=user_proto,json=userProto,casttype=github.com/cockroachdb/cockroach/pkg/security.SQLUsernameProto" json:"user_proto"` +} + +func (m *ParquetWriterSpec) Reset() { *m = ParquetWriterSpec{} } +func (m *ParquetWriterSpec) String() string { return proto.CompactTextString(m) } +func (*ParquetWriterSpec) ProtoMessage() {} +func (*ParquetWriterSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6d46d06b67eadaca, []int{12} +} +func (m *ParquetWriterSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ParquetWriterSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ParquetWriterSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ParquetWriterSpec.Merge(m, src) +} +func (m *ParquetWriterSpec) XXX_Size() int { + return m.Size() +} +func (m *ParquetWriterSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ParquetWriterSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_ParquetWriterSpec proto.InternalMessageInfo + // BulkRowWriterSpec is the specification for a processor that consumes rows and // writes them to a target table using AddSSTable. It outputs a BulkOpSummary. type BulkRowWriterSpec struct { @@ -742,7 +788,7 @@ func (m *BulkRowWriterSpec) Reset() { *m = BulkRowWriterSpec{} } func (m *BulkRowWriterSpec) String() string { return proto.CompactTextString(m) } func (*BulkRowWriterSpec) ProtoMessage() {} func (*BulkRowWriterSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_6d46d06b67eadaca, []int{12} + return fileDescriptor_6d46d06b67eadaca, []int{13} } func (m *BulkRowWriterSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -790,6 +836,7 @@ func init() { proto.RegisterType((*SplitAndScatterSpec)(nil), "cockroach.sql.distsqlrun.SplitAndScatterSpec") proto.RegisterType((*SplitAndScatterSpec_RestoreEntryChunk)(nil), "cockroach.sql.distsqlrun.SplitAndScatterSpec.RestoreEntryChunk") proto.RegisterType((*CSVWriterSpec)(nil), "cockroach.sql.distsqlrun.CSVWriterSpec") + proto.RegisterType((*ParquetWriterSpec)(nil), "cockroach.sql.distsqlrun.ParquetWriterSpec") proto.RegisterType((*BulkRowWriterSpec)(nil), "cockroach.sql.distsqlrun.BulkRowWriterSpec") } @@ -798,138 +845,140 @@ func init() { } var fileDescriptor_6d46d06b67eadaca = []byte{ - // 2086 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x5b, 0x6f, 0x1b, 0xc7, - 0x15, 0xd6, 0xf2, 0x26, 0xf2, 0x50, 0x17, 0x6a, 0x9c, 0xc4, 0x1b, 0x15, 0x95, 0x04, 0xfa, 0x12, - 0xd6, 0x85, 0x49, 0xc4, 0x6e, 0x03, 0x23, 0x69, 0xe2, 0x9a, 0x94, 0xe5, 0x50, 0x8e, 0x6d, 0x75, - 0x69, 0xd9, 0x40, 0xd0, 0x76, 0xb1, 0xdc, 0x1d, 0x51, 0x63, 0x2e, 0x77, 0xd6, 0x33, 0xb3, 0x96, - 0xe9, 0xc7, 0xf6, 0xad, 0x4f, 0x7d, 0xed, 0x9f, 0x28, 0x50, 0xa0, 0xff, 0xa0, 0x2f, 0x7e, 0xcc, - 0x63, 0x80, 0x00, 0x42, 0x2b, 0xf7, 0x4f, 0xd4, 0x4f, 0xc5, 0x5c, 0x96, 0x5c, 0xc9, 0x92, 0x2d, - 0x39, 0xc8, 0x8b, 0xbc, 0x9e, 0x73, 0xbe, 0x6f, 0xce, 0x99, 0x39, 0x37, 0x0e, 0x34, 0xf8, 0xd3, - 0xb0, 0x85, 0x9f, 0x63, 0x9f, 0x44, 0x3b, 0xcc, 0x8b, 0xfb, 0xad, 0x98, 0x51, 0x1f, 0x73, 0x4e, - 0x19, 0x77, 0xfb, 0x49, 0x38, 0x74, 0x09, 0x6d, 0xc6, 0x8c, 0x0a, 0x8a, 0x6c, 0x9f, 0xfa, 0x43, - 0x46, 0x3d, 0x7f, 0xb7, 0xc9, 0x9f, 0x86, 0xcd, 0x80, 0x70, 0xc1, 0x9f, 0x86, 0x2c, 0x89, 0x96, - 0x3f, 0x7a, 0x42, 0xfb, 0xbc, 0x25, 0xff, 0xc4, 0x7d, 0xf5, 0x8f, 0x46, 0x2c, 0xdb, 0x4a, 0x3b, - 0xee, 0xb7, 0x08, 0xbd, 0xba, 0x43, 0xd9, 0xc8, 0x13, 0xa9, 0xe4, 0x82, 0xdc, 0xd5, 0xf7, 0x84, - 0x17, 0xd2, 0x41, 0x2b, 0xc0, 0xdc, 0x8f, 0xfb, 0x2d, 0x2e, 0x58, 0xe2, 0x8b, 0x84, 0xe1, 0xc0, - 0x28, 0x5d, 0x7a, 0x9b, 0x69, 0x1e, 0xc7, 0xe9, 0x2e, 0x89, 0x20, 0x61, 0x6b, 0x37, 0xf4, 0x5b, - 0x82, 0x8c, 0x30, 0x17, 0xde, 0x28, 0x36, 0x92, 0x0f, 0x06, 0x74, 0x40, 0xd5, 0x67, 0x4b, 0x7e, - 0x99, 0x55, 0x94, 0x5a, 0x15, 0x78, 0xc2, 0x33, 0x6b, 0x4b, 0xe9, 0x9a, 0x17, 0x13, 0xbd, 0x54, - 0xff, 0xa1, 0x00, 0x0b, 0x6d, 0xcf, 0x1f, 0xee, 0x90, 0x30, 0xc4, 0xac, 0x17, 0x63, 0x1f, 0xdd, - 0x81, 0x82, 0x18, 0xc7, 0xd8, 0xb6, 0xd6, 0xac, 0xc6, 0xc2, 0xb5, 0xab, 0xcd, 0x93, 0x0e, 0xa4, - 0x79, 0x18, 0xd7, 0x7c, 0x38, 0x8e, 0x71, 0xbb, 0xf0, 0x72, 0x7f, 0x75, 0xc6, 0x51, 0x04, 0xa8, - 0x0d, 0x45, 0xe1, 0xf5, 0x43, 0x6c, 0xe7, 0xd6, 0xac, 0x46, 0xf5, 0xda, 0xe5, 0x23, 0x4c, 0xfc, - 0x69, 0xa8, 0xfc, 0x7b, 0x28, 0x75, 0xd6, 0x31, 0xf7, 0x19, 0x89, 0x05, 0x65, 0x86, 0x42, 0x43, - 0xd1, 0x75, 0x28, 0xf2, 0xd8, 0x8b, 0xb8, 0x0d, 0x6b, 0xf9, 0x46, 0xf5, 0xda, 0xf9, 0x0c, 0x87, - 0x71, 0xa6, 0xd9, 0x8b, 0xbd, 0x28, 0x05, 0x29, 0x5d, 0xf4, 0x29, 0x94, 0x83, 0x84, 0x79, 0x82, - 0xd0, 0xc8, 0x2e, 0xac, 0x59, 0x8d, 0x7c, 0xfb, 0x43, 0x29, 0x7e, 0xbd, 0xbf, 0x3a, 0x2f, 0x0f, - 0xaf, 0xb9, 0x6e, 0x84, 0xce, 0x44, 0x0d, 0x5d, 0x00, 0xf0, 0x77, 0x93, 0x68, 0xe8, 0x72, 0xf2, - 0x02, 0xdb, 0x45, 0x05, 0xd2, 0x9c, 0x15, 0xb5, 0xde, 0x23, 0x2f, 0x30, 0xba, 0x05, 0x95, 0x3d, - 0x46, 0x04, 0xbe, 0xc5, 0x1f, 0xec, 0xd8, 0xb3, 0xca, 0xa9, 0x9f, 0x67, 0x0c, 0x92, 0x37, 0xd4, - 0xdc, 0x0d, 0xfd, 0xe6, 0xc3, 0xf4, 0x86, 0x52, 0x8a, 0x09, 0x0a, 0xdd, 0x84, 0x32, 0xc3, 0x5e, - 0xa0, 0x18, 0x2a, 0xa7, 0x67, 0x98, 0x80, 0x10, 0x87, 0x73, 0x24, 0x0a, 0xf0, 0x73, 0xcc, 0x5d, - 0x41, 0xdd, 0xbe, 0xb9, 0x02, 0xbb, 0xbc, 0x96, 0x6f, 0xcc, 0xb7, 0x3b, 0xaf, 0xf7, 0x57, 0x6f, - 0x0e, 0x88, 0xd8, 0x4d, 0xfa, 0x4d, 0x9f, 0x8e, 0x5a, 0x13, 0xe6, 0xa0, 0x3f, 0xfd, 0x6e, 0xc5, - 0xc3, 0x41, 0xeb, 0xcd, 0xc8, 0x6c, 0x76, 0x25, 0x6d, 0x77, 0xdd, 0x59, 0x32, 0xfc, 0x0f, 0x69, - 0x7a, 0xc1, 0xf5, 0x2b, 0x50, 0x90, 0xb7, 0x8b, 0xaa, 0x30, 0xdb, 0x8d, 0x9e, 0x79, 0x21, 0x09, - 0x6a, 0x33, 0x08, 0xa0, 0xd4, 0xa1, 0x61, 0x32, 0x8a, 0x6a, 0x16, 0xaa, 0x40, 0x51, 0xc1, 0x6b, - 0xb9, 0xcd, 0x42, 0x39, 0x5f, 0x2b, 0x6c, 0x16, 0xca, 0xa5, 0xda, 0x6c, 0xfd, 0x1f, 0x16, 0x54, - 0x37, 0x69, 0x7f, 0x8b, 0xd1, 0x01, 0xc3, 0x9c, 0xa3, 0x3f, 0x42, 0xe9, 0x09, 0xed, 0xbb, 0x24, - 0x50, 0xc1, 0x95, 0x6f, 0xdf, 0x91, 0xce, 0x1d, 0xec, 0xaf, 0x16, 0x37, 0x69, 0xbf, 0xbb, 0xfe, - 0x7a, 0x7f, 0xf5, 0xb3, 0x53, 0x19, 0x9f, 0x49, 0xc4, 0xa6, 0x42, 0x3a, 0xc5, 0x27, 0xb4, 0xdf, - 0x0d, 0x50, 0x03, 0xe6, 0x7c, 0x1a, 0x09, 0x46, 0xfa, 0x89, 0xba, 0x7c, 0x19, 0x78, 0x39, 0x73, - 0x84, 0x87, 0x24, 0xc8, 0x86, 0x02, 0x0f, 0xa9, 0xb0, 0xf3, 0x6b, 0x56, 0xa3, 0x98, 0x46, 0xad, - 0x5c, 0xa9, 0xff, 0x05, 0x00, 0x39, 0xd8, 0x0b, 0xba, 0xa3, 0x98, 0x32, 0xb1, 0xee, 0x09, 0x4f, - 0x65, 0xc5, 0x97, 0x50, 0xd2, 0xc9, 0x6d, 0x97, 0xd5, 0xb5, 0xad, 0x1e, 0x13, 0x89, 0xdd, 0x07, - 0x1b, 0x24, 0xc4, 0x1b, 0x4a, 0xcd, 0x70, 0x1a, 0x10, 0xba, 0x04, 0x55, 0xee, 0x8d, 0xe2, 0x10, - 0xeb, 0x00, 0xcb, 0x65, 0xb6, 0x05, 0x2d, 0x50, 0x11, 0xf6, 0x08, 0x4a, 0x2a, 0xee, 0xb9, 0x5d, - 0x51, 0xf1, 0x7e, 0xe3, 0xe4, 0xec, 0x7b, 0xd3, 0x46, 0x9d, 0x49, 0xfc, 0x76, 0x24, 0xd8, 0x58, - 0x71, 0x5b, 0x8e, 0x61, 0x43, 0x77, 0x20, 0x9f, 0x30, 0x62, 0xcf, 0x2a, 0xd2, 0x5f, 0x9f, 0x89, - 0x74, 0x9b, 0x11, 0xc5, 0xe8, 0x48, 0x06, 0xf4, 0x2d, 0x00, 0xc3, 0x3c, 0x19, 0x61, 0x37, 0xa6, - 0xdc, 0x5e, 0x50, 0x7c, 0x5f, 0x9c, 0x89, 0xcf, 0x51, 0xf0, 0x2d, 0xaa, 0xed, 0x74, 0x2a, 0x2c, - 0xfd, 0x3f, 0xba, 0x03, 0xe5, 0xd8, 0x44, 0x8a, 0x5d, 0x52, 0x87, 0x7c, 0xe9, 0x64, 0xe6, 0x4c, - 0x58, 0xa5, 0x39, 0x92, 0x82, 0xd1, 0x4d, 0xf8, 0x98, 0x0f, 0x49, 0xec, 0x8e, 0x08, 0xe7, 0x24, - 0x1a, 0xb8, 0x3b, 0x94, 0x61, 0x32, 0x88, 0xdc, 0x21, 0x1e, 0xcb, 0x42, 0x62, 0x35, 0xca, 0x06, - 0xf2, 0x91, 0x54, 0xbb, 0xa7, 0xb5, 0x36, 0xb4, 0xd2, 0x5d, 0x3c, 0xe6, 0xe8, 0x0a, 0xcc, 0xef, - 0x79, 0x61, 0x28, 0x8b, 0xc5, 0x7d, 0x2f, 0xa2, 0xdc, 0xae, 0x66, 0x0a, 0xc2, 0x61, 0x11, 0xba, - 0x06, 0x4b, 0x32, 0x39, 0x31, 0xdb, 0xf2, 0x98, 0x17, 0x86, 0x38, 0x24, 0x7c, 0x64, 0xcf, 0x67, - 0xee, 0xf7, 0x4d, 0x31, 0xc2, 0x00, 0x09, 0xc7, 0xcc, 0x55, 0x35, 0xd8, 0x5e, 0x5c, 0xb3, 0x1a, - 0x95, 0xf6, 0x86, 0x29, 0x51, 0x5f, 0x9d, 0x2e, 0x7f, 0xb1, 0x9f, 0x30, 0x22, 0xc6, 0xcd, 0xde, - 0xef, 0xbe, 0xd9, 0xe6, 0x98, 0x45, 0xde, 0x08, 0x6f, 0x49, 0x36, 0xa7, 0x22, 0x99, 0xd5, 0x27, - 0xfa, 0x02, 0x8a, 0xb2, 0x10, 0x73, 0xbb, 0xa6, 0xee, 0xe9, 0xd2, 0x49, 0x05, 0x78, 0x1c, 0x67, - 0xea, 0xaf, 0xa3, 0x31, 0xe8, 0x4f, 0x16, 0x9c, 0x97, 0xbd, 0x43, 0xaa, 0xb8, 0x31, 0x23, 0x23, - 0x8f, 0x8d, 0x5d, 0x86, 0x07, 0x32, 0xaf, 0x96, 0x94, 0xc5, 0x9b, 0xc6, 0xe2, 0xf6, 0xfb, 0x56, - 0x1c, 0x47, 0xb1, 0xdd, 0xf7, 0x46, 0xd8, 0xf9, 0x30, 0xdd, 0x6a, 0x4b, 0xef, 0xa4, 0x45, 0xcb, - 0x09, 0x54, 0x75, 0xf8, 0xa8, 0xd0, 0x46, 0xbf, 0x85, 0x82, 0x84, 0xaa, 0xea, 0x71, 0xb6, 0x86, - 0x62, 0x39, 0x0a, 0x89, 0x2e, 0x02, 0x08, 0x8f, 0x0d, 0xb0, 0xe8, 0xd0, 0x90, 0xdb, 0xb9, 0xb5, - 0x7c, 0xa3, 0x62, 0xe4, 0x99, 0xf5, 0x65, 0x0e, 0xd5, 0x4c, 0x2e, 0xa1, 0x1a, 0xe4, 0x87, 0x78, - 0xac, 0x76, 0xad, 0x38, 0xf2, 0x13, 0xdd, 0x87, 0xe2, 0x33, 0x2f, 0x4c, 0xd2, 0xd6, 0x76, 0xb6, - 0x34, 0xcd, 0x78, 0xe4, 0x68, 0x9a, 0xcf, 0x73, 0x37, 0xac, 0xe5, 0xcf, 0xa0, 0x9c, 0xe6, 0x5a, - 0x76, 0xc7, 0xa2, 0xde, 0xf1, 0x83, 0xec, 0x8e, 0x95, 0x2c, 0xee, 0x37, 0xb0, 0x70, 0x38, 0xa7, - 0xde, 0x85, 0xce, 0x67, 0xd0, 0x9b, 0x85, 0xb2, 0x95, 0x29, 0xda, 0x85, 0x5a, 0x71, 0xb3, 0x50, - 0x2e, 0xd6, 0x4a, 0x9b, 0x85, 0xf2, 0x5c, 0x6d, 0xbe, 0xfe, 0xdf, 0x1c, 0x9c, 0xef, 0x09, 0x86, - 0xbd, 0x51, 0x37, 0x1a, 0x60, 0x2e, 0x4b, 0xe7, 0xa4, 0x22, 0x5e, 0x85, 0x0a, 0x57, 0x22, 0x59, - 0xcf, 0x65, 0xc7, 0x2c, 0xb4, 0x6b, 0xa6, 0x9e, 0x97, 0x0d, 0x66, 0xdd, 0x29, 0x6b, 0x95, 0x6e, - 0x80, 0x2e, 0xc0, 0x7c, 0xec, 0x31, 0x41, 0x24, 0x87, 0x4b, 0x02, 0x99, 0xe2, 0xf9, 0x46, 0xc5, - 0x99, 0x9b, 0x2c, 0x76, 0x03, 0x8e, 0x3e, 0x81, 0xc5, 0xa9, 0x12, 0x8f, 0xb1, 0xcf, 0x55, 0xcd, - 0xaa, 0x38, 0x0b, 0x93, 0x65, 0xb9, 0x37, 0x47, 0x2d, 0x38, 0x37, 0x55, 0xf4, 0x82, 0x40, 0xe6, - 0x3d, 0xe6, 0xaa, 0x0d, 0x56, 0x1c, 0x34, 0x11, 0xdd, 0x4a, 0x25, 0xa8, 0x0d, 0xc0, 0x85, 0xc7, - 0x84, 0x2b, 0x33, 0xd7, 0x5c, 0xdb, 0xe9, 0x9a, 0xb7, 0x82, 0xc9, 0x55, 0xf4, 0x4b, 0x58, 0x30, - 0x1e, 0x9b, 0x1d, 0x55, 0xfb, 0xa8, 0xa4, 0x75, 0x41, 0xcb, 0xcc, 0x96, 0xe8, 0xe2, 0xa4, 0xd7, - 0xe9, 0x11, 0x64, 0xfe, 0x50, 0xaf, 0x33, 0x1d, 0x4b, 0x1f, 0x7f, 0xfd, 0x07, 0x0b, 0x7e, 0x76, - 0xe4, 0x98, 0x37, 0x18, 0x8d, 0x04, 0x31, 0x23, 0x99, 0x03, 0xe7, 0x76, 0xc9, 0x60, 0xd7, 0xdd, - 0xf3, 0x04, 0x66, 0xae, 0x27, 0x5c, 0x65, 0x93, 0x49, 0x83, 0x53, 0x79, 0x51, 0x93, 0xf8, 0xc7, - 0x12, 0x7e, 0x4b, 0xf4, 0x24, 0x18, 0xb5, 0x61, 0x5e, 0x30, 0xcf, 0x1f, 0xe2, 0xc0, 0xd5, 0x13, - 0x56, 0xee, 0x34, 0x13, 0xd6, 0x9c, 0xc1, 0xf4, 0xd4, 0xa0, 0x35, 0xf5, 0x31, 0x7f, 0xb2, 0x8f, - 0xf5, 0x7f, 0xcd, 0xea, 0x19, 0x33, 0x89, 0x27, 0xb1, 0x33, 0x19, 0xeb, 0xac, 0x33, 0x8c, 0x75, - 0x5f, 0x43, 0x8d, 0x44, 0x82, 0xd1, 0x20, 0xf1, 0xcf, 0x66, 0xf4, 0xe2, 0x14, 0xa6, 0xed, 0xbe, - 0x0e, 0xd5, 0x00, 0xef, 0x78, 0x49, 0x28, 0x5c, 0xd9, 0x16, 0xf5, 0x2d, 0x22, 0x63, 0x3c, 0xac, - 0x6b, 0xd1, 0xb6, 0xd3, 0x75, 0xc0, 0xa8, 0x6d, 0x33, 0x82, 0xfe, 0x6c, 0xc1, 0xb9, 0x84, 0x11, - 0xee, 0xf6, 0xc7, 0x6e, 0x48, 0x7d, 0x2f, 0x24, 0x62, 0xec, 0x0e, 0x9f, 0xd9, 0x05, 0x65, 0xc2, - 0x57, 0x6f, 0x9f, 0x93, 0xa7, 0xbe, 0xcb, 0x86, 0xca, 0xdb, 0xe3, 0x6f, 0x0c, 0xc3, 0xdd, 0x67, - 0xba, 0x5f, 0x7f, 0x70, 0xb0, 0xbf, 0x5a, 0xdb, 0x76, 0xba, 0x59, 0xd1, 0x23, 0xa7, 0x96, 0x1c, - 0x51, 0x46, 0x0e, 0x54, 0x47, 0xcf, 0x7c, 0xdf, 0xdd, 0x21, 0xa1, 0xc0, 0x4c, 0xe5, 0xdd, 0xc2, - 0xa1, 0x10, 0x48, 0xfd, 0xbf, 0xf7, 0xa8, 0xd3, 0xd9, 0x50, 0x4a, 0x53, 0xcf, 0xa6, 0x6b, 0x0e, - 0x48, 0x16, 0xfd, 0x8d, 0xbe, 0x06, 0xc0, 0x91, 0xcf, 0xc6, 0xb1, 0x1a, 0x9a, 0x74, 0xeb, 0x6d, - 0x1c, 0x43, 0x29, 0xa7, 0x9b, 0xdb, 0x13, 0xc5, 0x07, 0xea, 0x2f, 0x77, 0x32, 0x58, 0xf4, 0x00, - 0x96, 0xfa, 0xca, 0x5b, 0x37, 0x93, 0x6c, 0x67, 0x98, 0x94, 0x17, 0x35, 0xba, 0x37, 0x49, 0xb9, - 0xbb, 0x60, 0x96, 0x5c, 0x1c, 0x05, 0x9a, 0xae, 0x7c, 0x7a, 0xba, 0x79, 0x8d, 0xbd, 0x1d, 0x05, - 0x8a, 0x6c, 0x1b, 0x4a, 0xf1, 0x50, 0xd5, 0x1e, 0x3d, 0x5d, 0x5d, 0x3f, 0xf5, 0x9d, 0x6d, 0x0d, - 0xbb, 0x81, 0x19, 0xac, 0x2a, 0x32, 0xbe, 0xb7, 0xee, 0x76, 0xd7, 0xb9, 0x53, 0x8c, 0xe5, 0xf2, - 0x91, 0x6e, 0x0e, 0x3f, 0x51, 0x37, 0x5f, 0xee, 0xc0, 0x87, 0xc7, 0x86, 0xce, 0x31, 0xed, 0xe9, - 0xe4, 0x66, 0x71, 0x03, 0x60, 0xea, 0x4b, 0x16, 0x59, 0x38, 0x06, 0x59, 0xce, 0x20, 0xeb, 0x1c, - 0x16, 0x1d, 0xcc, 0x05, 0x65, 0x58, 0x86, 0x81, 0xca, 0xe2, 0xcf, 0x21, 0x1f, 0x10, 0x66, 0xca, - 0x50, 0xfd, 0x98, 0x80, 0xb9, 0xfd, 0x5c, 0x48, 0x67, 0xc2, 0x9e, 0xa0, 0xcc, 0x1b, 0xa4, 0xbf, - 0x0e, 0x25, 0x48, 0x0e, 0xe0, 0xb1, 0x27, 0x76, 0xb5, 0x85, 0xe9, 0x00, 0x2e, 0x57, 0xb2, 0xbd, - 0xa8, 0x7e, 0x0f, 0x40, 0xf7, 0x49, 0x2c, 0x8d, 0xbb, 0x08, 0x25, 0x1a, 0x06, 0xe9, 0xcf, 0x87, - 0xf9, 0x69, 0xb9, 0x79, 0x10, 0x06, 0xb2, 0xdc, 0xd0, 0x30, 0xe8, 0x06, 0xe8, 0x63, 0x28, 0x47, - 0x78, 0xcf, 0x55, 0x83, 0x82, 0x64, 0x9f, 0x73, 0x66, 0x23, 0xbc, 0x27, 0xe7, 0x81, 0xfa, 0x3f, - 0x2d, 0xa8, 0x19, 0x27, 0x64, 0x21, 0xd0, 0x87, 0xf0, 0x29, 0x14, 0x64, 0x2d, 0x31, 0x6e, 0xbc, - 0xa3, 0x94, 0x28, 0x55, 0x74, 0x1b, 0x8a, 0x3b, 0x44, 0x4e, 0xe9, 0xba, 0xfc, 0xfc, 0xe2, 0x6d, - 0xed, 0xff, 0xd0, 0x91, 0xa5, 0x05, 0x4d, 0xa1, 0xd1, 0x65, 0xa8, 0xa6, 0x33, 0x6b, 0x37, 0x78, - 0x6e, 0x6a, 0xa8, 0xd6, 0xc8, 0x0a, 0xea, 0xff, 0xcb, 0x4d, 0xce, 0x7e, 0x52, 0x41, 0x37, 0x60, - 0x8e, 0xe9, 0x25, 0x9d, 0x15, 0x67, 0xe8, 0x05, 0x55, 0x03, 0x54, 0x39, 0x71, 0x38, 0xf7, 0x73, - 0x3f, 0x22, 0xf7, 0xdb, 0x50, 0x62, 0x58, 0x8d, 0xd8, 0x79, 0x75, 0x2a, 0x17, 0x4f, 0x3e, 0x95, - 0xe9, 0x9d, 0xa6, 0x3f, 0x93, 0x34, 0x52, 0xfe, 0xfe, 0x31, 0x19, 0xaa, 0xab, 0xea, 0xaf, 0xde, - 0x79, 0xb2, 0xa7, 0x4a, 0xd1, 0x1f, 0x11, 0xf6, 0x7f, 0xcb, 0xc1, 0xb9, 0x5e, 0x1c, 0x12, 0x71, - 0x2b, 0x0a, 0x7a, 0xbe, 0x27, 0x84, 0x69, 0xc9, 0x7f, 0x80, 0x92, 0x7a, 0x18, 0x48, 0x5b, 0xd8, - 0xcd, 0x93, 0x2d, 0x3d, 0x06, 0x9e, 0x5a, 0xaf, 0xec, 0xe9, 0x48, 0x9e, 0xf4, 0x20, 0x34, 0x69, - 0xe6, 0x30, 0x73, 0xef, 0x7b, 0x98, 0xcb, 0x2e, 0x2c, 0xbd, 0xb1, 0x0d, 0xda, 0x84, 0x59, 0x2c, - 0x7f, 0x07, 0xe3, 0xd4, 0xf0, 0x2b, 0xef, 0x3c, 0xe2, 0x49, 0xaa, 0x18, 0xfe, 0x94, 0xa0, 0xfe, - 0xf7, 0x3c, 0xcc, 0x77, 0x7a, 0x8f, 0x1e, 0x33, 0x92, 0x9e, 0xca, 0x65, 0xd9, 0x58, 0xb9, 0x20, - 0x91, 0x7e, 0x7c, 0xb1, 0x32, 0xc9, 0x9d, 0x15, 0xa0, 0x4f, 0x60, 0x4e, 0xd6, 0x38, 0x37, 0x56, - 0x27, 0x12, 0x1d, 0xaa, 0x02, 0x55, 0x55, 0xfd, 0xb4, 0x00, 0x7d, 0x09, 0xb3, 0x54, 0xc7, 0x9a, - 0x4a, 0x8f, 0xea, 0xb1, 0xad, 0xae, 0xd3, 0x7b, 0x64, 0x02, 0x32, 0xb5, 0xd0, 0x60, 0xa6, 0xcf, - 0x3a, 0x8c, 0xee, 0x71, 0x33, 0x88, 0x65, 0x9f, 0x75, 0x1c, 0xba, 0xc7, 0x8f, 0xbc, 0xfd, 0xcc, - 0x1e, 0xff, 0xf6, 0xf3, 0x7b, 0x58, 0xf2, 0xe9, 0x28, 0x96, 0x29, 0x29, 0x47, 0x4e, 0x9f, 0x06, - 0xd8, 0x37, 0xdd, 0xf7, 0x2d, 0xe9, 0x2f, 0xb3, 0xa6, 0x33, 0x85, 0xa5, 0xc3, 0x58, 0x86, 0xa9, - 0x23, 0x89, 0x8e, 0xb4, 0x90, 0xd2, 0x4f, 0xd4, 0x42, 0xea, 0x8f, 0x61, 0xa9, 0x9d, 0x84, 0xd2, - 0xeb, 0xcc, 0x9d, 0x4d, 0x9e, 0xe9, 0xac, 0xf7, 0x7e, 0xa6, 0xbb, 0x72, 0x09, 0x16, 0x8f, 0xb8, - 0x8a, 0xca, 0x50, 0xb8, 0x4f, 0x23, 0x5c, 0x9b, 0x91, 0x5f, 0x77, 0x5e, 0x90, 0xb8, 0x66, 0xb5, - 0xaf, 0xbe, 0xfc, 0xcf, 0xca, 0xcc, 0xcb, 0x83, 0x15, 0xeb, 0xbb, 0x83, 0x15, 0xeb, 0xfb, 0x83, - 0x15, 0xeb, 0xdf, 0x07, 0x2b, 0xd6, 0x5f, 0x5f, 0xad, 0xcc, 0x7c, 0xf7, 0x6a, 0x65, 0xe6, 0xfb, - 0x57, 0x2b, 0x33, 0xdf, 0x56, 0x33, 0x2f, 0xa1, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xf4, 0x86, - 0x91, 0xe7, 0xb6, 0x15, 0x00, 0x00, + // 2117 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0x4d, 0x6f, 0x1b, 0xc7, + 0xf9, 0xd7, 0xf2, 0x4d, 0xe4, 0x43, 0xbd, 0x50, 0xe3, 0x24, 0xde, 0xe8, 0x8f, 0xbf, 0xa4, 0xd2, + 0x2f, 0x61, 0x5d, 0x98, 0x44, 0xec, 0x36, 0x30, 0x92, 0x26, 0xae, 0x28, 0x59, 0x0e, 0xe5, 0xd8, + 0x56, 0x97, 0x96, 0x0d, 0x04, 0x6d, 0x17, 0xcb, 0xdd, 0x11, 0x35, 0xe6, 0x72, 0x67, 0x3d, 0x33, + 0x6b, 0x99, 0x3e, 0xb6, 0xb7, 0x9e, 0x7a, 0xed, 0x97, 0x28, 0x50, 0xa0, 0xe8, 0x17, 0xe8, 0xc5, + 0xc7, 0x1c, 0x03, 0x04, 0x10, 0x5a, 0xb9, 0x5f, 0xa2, 0x3e, 0x15, 0xf3, 0xb2, 0xe4, 0x4a, 0x96, + 0x6c, 0xc9, 0x81, 0x6f, 0xbd, 0xc8, 0xeb, 0x99, 0xe7, 0xf7, 0x9b, 0xe7, 0x99, 0xe7, 0x95, 0x03, + 0x0d, 0xfe, 0x24, 0x6c, 0xe1, 0x67, 0xd8, 0x27, 0xd1, 0x0e, 0xf3, 0xe2, 0x5e, 0x2b, 0x66, 0xd4, + 0xc7, 0x9c, 0x53, 0xc6, 0xdd, 0x5e, 0x12, 0x0e, 0x5c, 0x42, 0x9b, 0x31, 0xa3, 0x82, 0x22, 0xdb, + 0xa7, 0xfe, 0x80, 0x51, 0xcf, 0xdf, 0x6d, 0xf2, 0x27, 0x61, 0x33, 0x20, 0x5c, 0xf0, 0x27, 0x21, + 0x4b, 0xa2, 0xc5, 0x8f, 0x1e, 0xd3, 0x1e, 0x6f, 0xc9, 0x3f, 0x71, 0x4f, 0xfd, 0xa3, 0x11, 0x8b, + 0xb6, 0x92, 0x8e, 0x7b, 0x2d, 0x42, 0xaf, 0xee, 0x50, 0x36, 0xf4, 0x44, 0xba, 0x73, 0x41, 0x9e, + 0xea, 0x7b, 0xc2, 0x0b, 0x69, 0xbf, 0x15, 0x60, 0xee, 0xc7, 0xbd, 0x16, 0x17, 0x2c, 0xf1, 0x45, + 0xc2, 0x70, 0x60, 0x84, 0x2e, 0xbd, 0x49, 0x35, 0x8f, 0xe3, 0xf4, 0x94, 0x44, 0x90, 0xb0, 0xb5, + 0x1b, 0xfa, 0x2d, 0x41, 0x86, 0x98, 0x0b, 0x6f, 0x18, 0x9b, 0x9d, 0x0f, 0xfa, 0xb4, 0x4f, 0xd5, + 0x67, 0x4b, 0x7e, 0x99, 0x55, 0x94, 0x6a, 0x15, 0x78, 0xc2, 0x33, 0x6b, 0x0b, 0xe9, 0x9a, 0x17, + 0x13, 0xbd, 0x54, 0xff, 0xa1, 0x00, 0x73, 0x6d, 0xcf, 0x1f, 0xec, 0x90, 0x30, 0xc4, 0xac, 0x1b, + 0x63, 0x1f, 0xdd, 0x86, 0x82, 0x18, 0xc5, 0xd8, 0xb6, 0x56, 0xac, 0xc6, 0xdc, 0xb5, 0xab, 0xcd, + 0x93, 0x2e, 0xa4, 0x79, 0x18, 0xd7, 0x7c, 0x30, 0x8a, 0x71, 0xbb, 0xf0, 0x62, 0x7f, 0x79, 0xca, + 0x51, 0x04, 0xa8, 0x0d, 0x45, 0xe1, 0xf5, 0x42, 0x6c, 0xe7, 0x56, 0xac, 0x46, 0xf5, 0xda, 0xe5, + 0x23, 0x4c, 0xfc, 0x49, 0xa8, 0xec, 0x7b, 0x20, 0x65, 0xd6, 0x31, 0xf7, 0x19, 0x89, 0x05, 0x65, + 0x86, 0x42, 0x43, 0xd1, 0x75, 0x28, 0xf2, 0xd8, 0x8b, 0xb8, 0x0d, 0x2b, 0xf9, 0x46, 0xf5, 0xda, + 0xf9, 0x0c, 0x87, 0x31, 0xa6, 0xd9, 0x8d, 0xbd, 0x28, 0x05, 0x29, 0x59, 0xf4, 0x29, 0x94, 0x83, + 0x84, 0x79, 0x82, 0xd0, 0xc8, 0x2e, 0xac, 0x58, 0x8d, 0x7c, 0xfb, 0x43, 0xb9, 0xfd, 0x6a, 0x7f, + 0x79, 0x56, 0x5e, 0x5e, 0x73, 0xdd, 0x6c, 0x3a, 0x63, 0x31, 0x74, 0x01, 0xc0, 0xdf, 0x4d, 0xa2, + 0x81, 0xcb, 0xc9, 0x73, 0x6c, 0x17, 0x15, 0x48, 0x73, 0x56, 0xd4, 0x7a, 0x97, 0x3c, 0xc7, 0x68, + 0x15, 0x2a, 0x7b, 0x8c, 0x08, 0xbc, 0xca, 0xef, 0xef, 0xd8, 0xd3, 0xca, 0xa8, 0xff, 0xcf, 0x28, + 0x24, 0x3d, 0xd4, 0xdc, 0x0d, 0xfd, 0xe6, 0x83, 0xd4, 0x43, 0x29, 0xc5, 0x18, 0x85, 0x6e, 0x42, + 0x99, 0x61, 0x2f, 0x50, 0x0c, 0x95, 0xd3, 0x33, 0x8c, 0x41, 0x88, 0xc3, 0x39, 0x12, 0x05, 0xf8, + 0x19, 0xe6, 0xae, 0xa0, 0x6e, 0xcf, 0xb8, 0xc0, 0x2e, 0xaf, 0xe4, 0x1b, 0xb3, 0xed, 0xb5, 0x57, + 0xfb, 0xcb, 0x37, 0xfb, 0x44, 0xec, 0x26, 0xbd, 0xa6, 0x4f, 0x87, 0xad, 0x31, 0x73, 0xd0, 0x9b, + 0x7c, 0xb7, 0xe2, 0x41, 0xbf, 0xf5, 0x7a, 0x64, 0x36, 0x3b, 0x92, 0xb6, 0xb3, 0xee, 0x2c, 0x18, + 0xfe, 0x07, 0x34, 0x75, 0x70, 0xfd, 0x0a, 0x14, 0xa4, 0x77, 0x51, 0x15, 0xa6, 0x3b, 0xd1, 0x53, + 0x2f, 0x24, 0x41, 0x6d, 0x0a, 0x01, 0x94, 0xd6, 0x68, 0x98, 0x0c, 0xa3, 0x9a, 0x85, 0x2a, 0x50, + 0x54, 0xf0, 0x5a, 0x6e, 0xb3, 0x50, 0xce, 0xd7, 0x0a, 0x9b, 0x85, 0x72, 0xa9, 0x36, 0x5d, 0xff, + 0xab, 0x05, 0xd5, 0x4d, 0xda, 0xdb, 0x62, 0xb4, 0xcf, 0x30, 0xe7, 0xe8, 0x77, 0x50, 0x7a, 0x4c, + 0x7b, 0x2e, 0x09, 0x54, 0x70, 0xe5, 0xdb, 0xb7, 0xa5, 0x71, 0x07, 0xfb, 0xcb, 0xc5, 0x4d, 0xda, + 0xeb, 0xac, 0xbf, 0xda, 0x5f, 0xfe, 0xec, 0x54, 0xca, 0x67, 0x12, 0xb1, 0xa9, 0x90, 0x4e, 0xf1, + 0x31, 0xed, 0x75, 0x02, 0xd4, 0x80, 0x19, 0x9f, 0x46, 0x82, 0x91, 0x5e, 0xa2, 0x9c, 0x2f, 0x03, + 0x2f, 0x67, 0xae, 0xf0, 0xd0, 0x0e, 0xb2, 0xa1, 0xc0, 0x43, 0x2a, 0xec, 0xfc, 0x8a, 0xd5, 0x28, + 0xa6, 0x51, 0x2b, 0x57, 0xea, 0x7f, 0x04, 0x40, 0x0e, 0xf6, 0x82, 0xce, 0x30, 0xa6, 0x4c, 0xac, + 0x7b, 0xc2, 0x53, 0x59, 0xf1, 0x25, 0x94, 0x74, 0x72, 0xdb, 0x65, 0xe5, 0xb6, 0xe5, 0x63, 0x22, + 0xb1, 0x73, 0x7f, 0x83, 0x84, 0x78, 0x43, 0x89, 0x19, 0x4e, 0x03, 0x42, 0x97, 0xa0, 0xca, 0xbd, + 0x61, 0x1c, 0x62, 0x1d, 0x60, 0xb9, 0xcc, 0xb1, 0xa0, 0x37, 0x54, 0x84, 0x3d, 0x84, 0x92, 0x8a, + 0x7b, 0x6e, 0x57, 0x54, 0xbc, 0xdf, 0x38, 0x39, 0xfb, 0x5e, 0xd7, 0x51, 0x67, 0x12, 0xbf, 0x15, + 0x09, 0x36, 0x52, 0xdc, 0x96, 0x63, 0xd8, 0xd0, 0x6d, 0xc8, 0x27, 0x8c, 0xd8, 0xd3, 0x8a, 0xf4, + 0x17, 0x67, 0x22, 0xdd, 0x66, 0x44, 0x31, 0x3a, 0x92, 0x01, 0x7d, 0x0b, 0xc0, 0x30, 0x4f, 0x86, + 0xd8, 0x8d, 0x29, 0xb7, 0xe7, 0x14, 0xdf, 0x17, 0x67, 0xe2, 0x73, 0x14, 0x7c, 0x8b, 0x6a, 0x3d, + 0x9d, 0x0a, 0x4b, 0xff, 0x8f, 0x6e, 0x43, 0x39, 0x36, 0x91, 0x62, 0x97, 0xd4, 0x25, 0x5f, 0x3a, + 0x99, 0x39, 0x13, 0x56, 0x69, 0x8e, 0xa4, 0x60, 0x74, 0x13, 0x3e, 0xe6, 0x03, 0x12, 0xbb, 0x43, + 0xc2, 0x39, 0x89, 0xfa, 0xee, 0x0e, 0x65, 0x98, 0xf4, 0x23, 0x77, 0x80, 0x47, 0xb2, 0x90, 0x58, + 0x8d, 0xb2, 0x81, 0x7c, 0x24, 0xc5, 0xee, 0x6a, 0xa9, 0x0d, 0x2d, 0x74, 0x07, 0x8f, 0x38, 0xba, + 0x02, 0xb3, 0x7b, 0x5e, 0x18, 0xca, 0x62, 0x71, 0xcf, 0x8b, 0x28, 0xb7, 0xab, 0x99, 0x82, 0x70, + 0x78, 0x0b, 0x5d, 0x83, 0x05, 0x99, 0x9c, 0x98, 0x6d, 0x79, 0xcc, 0x0b, 0x43, 0x1c, 0x12, 0x3e, + 0xb4, 0x67, 0x33, 0xfe, 0x7d, 0x7d, 0x1b, 0x61, 0x80, 0x84, 0x63, 0xe6, 0xaa, 0x1a, 0x6c, 0xcf, + 0xaf, 0x58, 0x8d, 0x4a, 0x7b, 0xc3, 0x94, 0xa8, 0xaf, 0x4e, 0x97, 0xbf, 0xd8, 0x4f, 0x18, 0x11, + 0xa3, 0x66, 0xf7, 0xd7, 0xdf, 0x6c, 0x73, 0xcc, 0x22, 0x6f, 0x88, 0xb7, 0x24, 0x9b, 0x53, 0x91, + 0xcc, 0xea, 0x13, 0x7d, 0x01, 0x45, 0x59, 0x88, 0xb9, 0x5d, 0x53, 0x7e, 0xba, 0x74, 0x52, 0x01, + 0x1e, 0xc5, 0x99, 0xfa, 0xeb, 0x68, 0x0c, 0xfa, 0xbd, 0x05, 0xe7, 0x65, 0xef, 0x90, 0x22, 0x6e, + 0xcc, 0xc8, 0xd0, 0x63, 0x23, 0x97, 0xe1, 0xbe, 0xcc, 0xab, 0x05, 0xa5, 0xf1, 0xa6, 0xd1, 0xb8, + 0xfd, 0xae, 0x15, 0xc7, 0x51, 0x6c, 0xf7, 0xbc, 0x21, 0x76, 0x3e, 0x4c, 0x8f, 0xda, 0xd2, 0x27, + 0xe9, 0xad, 0xc5, 0x04, 0xaa, 0x3a, 0x7c, 0x54, 0x68, 0xa3, 0x5f, 0x41, 0x41, 0x42, 0x55, 0xf5, + 0x38, 0x5b, 0x43, 0xb1, 0x1c, 0x85, 0x44, 0x17, 0x01, 0x84, 0xc7, 0xfa, 0x58, 0xac, 0xd1, 0x90, + 0xdb, 0xb9, 0x95, 0x7c, 0xa3, 0x62, 0xf6, 0x33, 0xeb, 0x8b, 0x1c, 0xaa, 0x99, 0x5c, 0x42, 0x35, + 0xc8, 0x0f, 0xf0, 0x48, 0x9d, 0x5a, 0x71, 0xe4, 0x27, 0xba, 0x07, 0xc5, 0xa7, 0x5e, 0x98, 0xa4, + 0xad, 0xed, 0x6c, 0x69, 0x9a, 0xb1, 0xc8, 0xd1, 0x34, 0x9f, 0xe7, 0x6e, 0x58, 0x8b, 0x9f, 0x41, + 0x39, 0xcd, 0xb5, 0xec, 0x89, 0x45, 0x7d, 0xe2, 0x07, 0xd9, 0x13, 0x2b, 0x59, 0xdc, 0x2f, 0x61, + 0xee, 0x70, 0x4e, 0xbd, 0x0d, 0x9d, 0xcf, 0xa0, 0x37, 0x0b, 0x65, 0x2b, 0x53, 0xb4, 0x0b, 0xb5, + 0xe2, 0x66, 0xa1, 0x5c, 0xac, 0x95, 0x36, 0x0b, 0xe5, 0x99, 0xda, 0x6c, 0xfd, 0xdf, 0x39, 0x38, + 0xdf, 0x15, 0x0c, 0x7b, 0xc3, 0x4e, 0xd4, 0xc7, 0x5c, 0x96, 0xce, 0x71, 0x45, 0xbc, 0x0a, 0x15, + 0xae, 0xb6, 0x64, 0x3d, 0x97, 0x1d, 0xb3, 0xd0, 0xae, 0x99, 0x7a, 0x5e, 0x36, 0x98, 0x75, 0xa7, + 0xac, 0x45, 0x3a, 0x01, 0xba, 0x00, 0xb3, 0xb1, 0xc7, 0x04, 0x91, 0x1c, 0x2e, 0x09, 0x64, 0x8a, + 0xe7, 0x1b, 0x15, 0x67, 0x66, 0xbc, 0xd8, 0x09, 0x38, 0xfa, 0x04, 0xe6, 0x27, 0x42, 0x3c, 0xc6, + 0x3e, 0x57, 0x35, 0xab, 0xe2, 0xcc, 0x8d, 0x97, 0xe5, 0xd9, 0x1c, 0xb5, 0xe0, 0xdc, 0x44, 0xd0, + 0x0b, 0x02, 0x99, 0xf7, 0x98, 0xab, 0x36, 0x58, 0x71, 0xd0, 0x78, 0x6b, 0x35, 0xdd, 0x41, 0x6d, + 0x00, 0x2e, 0x3c, 0x26, 0x5c, 0x99, 0xb9, 0xc6, 0x6d, 0xa7, 0x6b, 0xde, 0x0a, 0x26, 0x57, 0xd1, + 0xcf, 0x60, 0xce, 0x58, 0x6c, 0x4e, 0x54, 0xed, 0xa3, 0x92, 0xd6, 0x05, 0xbd, 0x67, 0x8e, 0x44, + 0x17, 0xc7, 0xbd, 0x4e, 0x8f, 0x20, 0xb3, 0x87, 0x7a, 0x9d, 0xe9, 0x58, 0xfa, 0xfa, 0xeb, 0x3f, + 0x58, 0xf0, 0x7f, 0x47, 0xae, 0x79, 0x83, 0xd1, 0x48, 0x10, 0x33, 0x92, 0x39, 0x70, 0x6e, 0x97, + 0xf4, 0x77, 0xdd, 0x3d, 0x4f, 0x60, 0xe6, 0x7a, 0xc2, 0x55, 0x3a, 0x99, 0x34, 0x38, 0x95, 0x15, + 0x35, 0x89, 0x7f, 0x24, 0xe1, 0xab, 0xa2, 0x2b, 0xc1, 0xa8, 0x0d, 0xb3, 0x82, 0x79, 0xfe, 0x00, + 0x07, 0xae, 0x9e, 0xb0, 0x72, 0xa7, 0x99, 0xb0, 0x66, 0x0c, 0xa6, 0xab, 0x06, 0xad, 0x89, 0x8d, + 0xf9, 0x93, 0x6d, 0xac, 0xff, 0x63, 0x5a, 0xcf, 0x98, 0x49, 0x3c, 0x8e, 0x9d, 0xf1, 0x58, 0x67, + 0x9d, 0x61, 0xac, 0xfb, 0x1a, 0x6a, 0x24, 0x12, 0x8c, 0x06, 0x89, 0x7f, 0x36, 0xa5, 0xe7, 0x27, + 0x30, 0xad, 0xf7, 0x75, 0xa8, 0x06, 0x78, 0xc7, 0x4b, 0x42, 0xe1, 0xca, 0xb6, 0xa8, 0xbd, 0x88, + 0x8c, 0xf2, 0xb0, 0xae, 0xb7, 0xb6, 0x9d, 0x8e, 0x03, 0x46, 0x6c, 0x9b, 0x11, 0xf4, 0x07, 0x0b, + 0xce, 0x25, 0x8c, 0x70, 0xb7, 0x37, 0x72, 0x43, 0xea, 0x7b, 0x21, 0x11, 0x23, 0x77, 0xf0, 0xd4, + 0x2e, 0x28, 0x15, 0xbe, 0x7a, 0xf3, 0x9c, 0x3c, 0xb1, 0x5d, 0x36, 0x54, 0xde, 0x1e, 0x7d, 0x63, + 0x18, 0xee, 0x3c, 0xd5, 0xfd, 0xfa, 0x83, 0x83, 0xfd, 0xe5, 0xda, 0xb6, 0xd3, 0xc9, 0x6e, 0x3d, + 0x74, 0x6a, 0xc9, 0x11, 0x61, 0xe4, 0x40, 0x75, 0xf8, 0xd4, 0xf7, 0xdd, 0x1d, 0x12, 0x0a, 0xcc, + 0x54, 0xde, 0xcd, 0x1d, 0x0a, 0x81, 0xd4, 0xfe, 0xbb, 0x0f, 0xd7, 0xd6, 0x36, 0x94, 0xd0, 0xc4, + 0xb2, 0xc9, 0x9a, 0x03, 0x92, 0x45, 0x7f, 0xa3, 0xaf, 0x01, 0x70, 0xe4, 0xb3, 0x51, 0xac, 0x86, + 0x26, 0xdd, 0x7a, 0x1b, 0xc7, 0x50, 0xca, 0xe9, 0xe6, 0xd6, 0x58, 0xf0, 0xbe, 0xfa, 0xcb, 0x9d, + 0x0c, 0x16, 0xdd, 0x87, 0x85, 0x9e, 0xb2, 0xd6, 0xcd, 0x24, 0xdb, 0x19, 0x26, 0xe5, 0x79, 0x8d, + 0xee, 0x8e, 0x53, 0xee, 0x0e, 0x98, 0x25, 0x17, 0x47, 0x81, 0xa6, 0x2b, 0x9f, 0x9e, 0x6e, 0x56, + 0x63, 0x6f, 0x45, 0x81, 0x22, 0xdb, 0x86, 0x52, 0x3c, 0x50, 0xb5, 0x47, 0x4f, 0x57, 0xd7, 0x4f, + 0xed, 0xb3, 0xad, 0x41, 0x27, 0x30, 0x83, 0x55, 0x45, 0xc6, 0xf7, 0xd6, 0x9d, 0xce, 0x3a, 0x77, + 0x8a, 0xb1, 0x5c, 0x3e, 0xd2, 0xcd, 0xe1, 0x3d, 0x75, 0xf3, 0xc5, 0x35, 0xf8, 0xf0, 0xd8, 0xd0, + 0x39, 0xa6, 0x3d, 0x9d, 0xdc, 0x2c, 0x6e, 0x00, 0x4c, 0x6c, 0xc9, 0x22, 0x0b, 0xc7, 0x20, 0xcb, + 0x19, 0x64, 0x9d, 0xc3, 0xbc, 0x83, 0xb9, 0xa0, 0x0c, 0xcb, 0x30, 0x50, 0x59, 0xfc, 0x39, 0xe4, + 0x03, 0xc2, 0x4c, 0x19, 0xaa, 0x1f, 0x13, 0x30, 0xb7, 0x9e, 0x09, 0x69, 0x4c, 0xd8, 0x15, 0x94, + 0x79, 0xfd, 0xf4, 0xd7, 0xa1, 0x04, 0xc9, 0x01, 0x3c, 0xf6, 0xc4, 0xae, 0xd6, 0x30, 0x1d, 0xc0, + 0xe5, 0x4a, 0xb6, 0x17, 0xd5, 0xef, 0x02, 0xe8, 0x3e, 0x89, 0xa5, 0x72, 0x17, 0xa1, 0x44, 0xc3, + 0x20, 0xfd, 0xf9, 0x30, 0x3b, 0x29, 0x37, 0xf7, 0xc3, 0x40, 0x96, 0x1b, 0x1a, 0x06, 0x9d, 0x00, + 0x7d, 0x0c, 0xe5, 0x08, 0xef, 0xb9, 0x6a, 0x50, 0x90, 0xec, 0x33, 0xce, 0x74, 0x84, 0xf7, 0xe4, + 0x3c, 0x50, 0xff, 0x9b, 0x05, 0x35, 0x63, 0x84, 0x2c, 0x04, 0xfa, 0x12, 0x3e, 0x85, 0x82, 0xac, + 0x25, 0xc6, 0x8c, 0xb7, 0x94, 0x12, 0x25, 0x8a, 0x6e, 0x41, 0x71, 0x87, 0xc8, 0x29, 0x5d, 0x97, + 0x9f, 0x9f, 0xbe, 0xa9, 0xfd, 0x1f, 0xba, 0xb2, 0xb4, 0xa0, 0x29, 0x34, 0xba, 0x0c, 0xd5, 0x74, + 0x66, 0xed, 0x04, 0xcf, 0x4c, 0x0d, 0xd5, 0x12, 0xd9, 0x8d, 0xfa, 0x7f, 0x72, 0xe3, 0xbb, 0x1f, + 0x57, 0xd0, 0x0d, 0x98, 0x61, 0x7a, 0x49, 0x67, 0xc5, 0x19, 0x7a, 0x41, 0xd5, 0x00, 0x55, 0x4e, + 0x1c, 0xce, 0xfd, 0xdc, 0x8f, 0xc8, 0xfd, 0x36, 0x94, 0x18, 0x56, 0x23, 0x76, 0x5e, 0xdd, 0xca, + 0xc5, 0x93, 0x6f, 0x65, 0xe2, 0xd3, 0xf4, 0x67, 0x92, 0x46, 0xca, 0xdf, 0x3f, 0x26, 0x43, 0x75, + 0x55, 0xfd, 0xf9, 0x5b, 0x6f, 0xf6, 0x54, 0x29, 0xfa, 0x23, 0xc2, 0xfe, 0xcf, 0x39, 0x38, 0xd7, + 0x8d, 0x43, 0x22, 0x56, 0xa3, 0xa0, 0xeb, 0x7b, 0x42, 0x98, 0x96, 0xfc, 0x5b, 0x28, 0xa9, 0x87, + 0x81, 0xb4, 0x85, 0xdd, 0x3c, 0x59, 0xd3, 0x63, 0xe0, 0xa9, 0xf6, 0x4a, 0x9f, 0x35, 0xc9, 0x93, + 0x5e, 0x84, 0x26, 0xcd, 0x5c, 0x66, 0xee, 0x5d, 0x2f, 0x73, 0xd1, 0x85, 0x85, 0xd7, 0x8e, 0x41, + 0x9b, 0x30, 0x8d, 0xe5, 0xef, 0x60, 0x9c, 0x2a, 0x7e, 0xe5, 0xad, 0x57, 0x3c, 0x4e, 0x15, 0xc3, + 0x9f, 0x12, 0xd4, 0xff, 0x92, 0x87, 0xd9, 0xb5, 0xee, 0xc3, 0x47, 0x8c, 0xa4, 0xb7, 0x72, 0x59, + 0x36, 0x56, 0x2e, 0x48, 0xa4, 0x1f, 0x5f, 0xac, 0x4c, 0x72, 0x67, 0x37, 0xd0, 0x27, 0x30, 0x23, + 0x6b, 0x9c, 0x1b, 0xab, 0x1b, 0x89, 0x0e, 0x55, 0x81, 0xaa, 0xaa, 0x7e, 0x7a, 0x03, 0x7d, 0x09, + 0xd3, 0x54, 0xc7, 0x9a, 0x4a, 0x8f, 0xea, 0xb1, 0xad, 0x6e, 0xad, 0xfb, 0xd0, 0x04, 0x64, 0xaa, + 0xa1, 0xc1, 0x4c, 0x9e, 0x75, 0x18, 0xdd, 0xe3, 0x66, 0x10, 0xcb, 0x3e, 0xeb, 0x38, 0x74, 0x8f, + 0x1f, 0x79, 0xfb, 0x99, 0x3e, 0xfe, 0xed, 0xe7, 0x37, 0xb0, 0xe0, 0xd3, 0x61, 0x2c, 0x53, 0x52, + 0x8e, 0x9c, 0x3e, 0x0d, 0xb0, 0x6f, 0xba, 0xef, 0x1b, 0xd2, 0x5f, 0x66, 0xcd, 0xda, 0x04, 0x96, + 0x0e, 0x63, 0x19, 0xa6, 0x35, 0x49, 0x74, 0xa4, 0x85, 0x94, 0xde, 0x53, 0x0b, 0xa9, 0xff, 0x3d, + 0x0f, 0x0b, 0x5b, 0x1e, 0x7b, 0x92, 0x60, 0xf1, 0x3e, 0x9d, 0xb6, 0x7a, 0xd4, 0x69, 0x3f, 0x39, + 0xc6, 0x69, 0x46, 0x8f, 0xff, 0x39, 0xee, 0x34, 0x8e, 0x7b, 0x04, 0x0b, 0xed, 0x24, 0x94, 0x56, + 0x67, 0xfc, 0x36, 0x7e, 0x5f, 0xb5, 0xde, 0xf9, 0x7d, 0xf5, 0xca, 0x25, 0x98, 0x3f, 0x62, 0x2a, + 0x2a, 0x43, 0xe1, 0x1e, 0x8d, 0x70, 0x6d, 0x4a, 0x7e, 0xdd, 0x7e, 0x4e, 0xe2, 0x9a, 0xd5, 0xbe, + 0xfa, 0xe2, 0x5f, 0x4b, 0x53, 0x2f, 0x0e, 0x96, 0xac, 0xef, 0x0e, 0x96, 0xac, 0xef, 0x0f, 0x96, + 0xac, 0x7f, 0x1e, 0x2c, 0x59, 0x7f, 0x7a, 0xb9, 0x34, 0xf5, 0xdd, 0xcb, 0xa5, 0xa9, 0xef, 0x5f, + 0x2e, 0x4d, 0x7d, 0x5b, 0xcd, 0x3c, 0x61, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe4, 0x9d, 0xb2, + 0x3b, 0x6f, 0x17, 0x00, 0x00, } func (m *BackfillerSpec) Marshal() (dAtA []byte, err error) { @@ -1869,6 +1918,63 @@ func (m *CSVWriterSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ParquetWriterSpec) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ParquetWriterSpec) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ParquetWriterSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(m.ChunkSize)) + i-- + dAtA[i] = 0x38 + i -= len(m.UserProto) + copy(dAtA[i:], m.UserProto) + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(len(m.UserProto))) + i-- + dAtA[i] = 0x32 + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(m.CompressionCodec)) + i-- + dAtA[i] = 0x28 + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(m.ChunkRows)) + i-- + dAtA[i] = 0x20 + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + i -= len(m.NamePattern) + copy(dAtA[i:], m.NamePattern) + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(len(m.NamePattern))) + i-- + dAtA[i] = 0x12 + i -= len(m.Destination) + copy(dAtA[i:], m.Destination) + i = encodeVarintProcessorsBulkIo(dAtA, i, uint64(len(m.Destination))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *BulkRowWriterSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2260,6 +2366,26 @@ func (m *CSVWriterSpec) Size() (n int) { return n } +func (m *ParquetWriterSpec) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Destination) + n += 1 + l + sovProcessorsBulkIo(uint64(l)) + l = len(m.NamePattern) + n += 1 + l + sovProcessorsBulkIo(uint64(l)) + l = m.Options.Size() + n += 1 + l + sovProcessorsBulkIo(uint64(l)) + n += 1 + sovProcessorsBulkIo(uint64(m.ChunkRows)) + n += 1 + sovProcessorsBulkIo(uint64(m.CompressionCodec)) + l = len(m.UserProto) + n += 1 + l + sovProcessorsBulkIo(uint64(l)) + n += 1 + sovProcessorsBulkIo(uint64(m.ChunkSize)) + return n +} + func (m *BulkRowWriterSpec) Size() (n int) { if m == nil { return 0 @@ -5404,6 +5530,242 @@ func (m *CSVWriterSpec) Unmarshal(dAtA []byte) error { } return nil } +func (m *ParquetWriterSpec) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ParquetWriterSpec: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ParquetWriterSpec: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Destination = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePattern", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamePattern = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChunkRows", wireType) + } + m.ChunkRows = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChunkRows |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CompressionCodec", wireType) + } + m.CompressionCodec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CompressionCodec |= FileCompression(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserProto", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserProto = github_com_cockroachdb_cockroach_pkg_security.SQLUsernameProto(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChunkSize", wireType) + } + m.ChunkSize = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowProcessorsBulkIo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChunkSize |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipProcessorsBulkIo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthProcessorsBulkIo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *BulkRowWriterSpec) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/sql/execinfrapb/processors_bulk_io.proto b/pkg/sql/execinfrapb/processors_bulk_io.proto index a00c469994d1..6d550026a65e 100644 --- a/pkg/sql/execinfrapb/processors_bulk_io.proto +++ b/pkg/sql/execinfrapb/processors_bulk_io.proto @@ -285,7 +285,7 @@ message CSVWriterSpec { } // ParquetWriterSpec is the specification for a processor that consumes rows and -// writes them to CSV files at uri. It outputs a row per file written with +// writes them to Parquet files at uri. It outputs a row per file written with // the file name, row count and byte size. message ParquetWriterSpec { // destination as a cloud.ExternalStorage URI pointing to an export store @@ -293,13 +293,13 @@ message ParquetWriterSpec { optional string destination = 1 [(gogoproto.nullable) = false]; optional string name_pattern = 2 [(gogoproto.nullable) = false]; optional roachpb.ParquetOptions options = 3 [(gogoproto.nullable) = false]; - //optional roachpb.CSVOptions options = 3 [(gogoproto.nullable) = false]; + // chunk_rows is num rows to write per file. 0 = no limit. optional int64 chunk_rows = 4 [(gogoproto.nullable) = false]; // chunk_size is the target byte size per file. optional int64 chunk_size = 7 [(gogoproto.nullable) = false]; - // compression_codec specifies compression used for exported file. + // compression_codec specifies compression used for exported file. THIS IS ISN'T USED RIGHT NOW optional FileCompression compression_codec = 5 [(gogoproto.nullable) = false]; // User who initiated the export. This is used to check access privileges diff --git a/pkg/sql/export.go b/pkg/sql/export.go index 800315a18bd4..927552925011 100644 --- a/pkg/sql/export.go +++ b/pkg/sql/export.go @@ -13,7 +13,6 @@ package sql import ( "context" "fmt" - "github.com/cockroachdb/cockroach/pkg/util" "strconv" "strings" @@ -26,6 +25,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" "github.com/cockroachdb/errors" ) @@ -100,6 +100,8 @@ var featureExportEnabled = settings.RegisterBoolSetting( func (ef *execFactory) ConstructExport( input exec.Node, fileName tree.TypedExpr, fileFormat string, options []exec.KVOption, ) (exec.Node, error) { + fileFormat = strings.ToLower(fileFormat) + if !featureExportEnabled.Get(&ef.planner.ExecCfg().Settings.SV) { return nil, pgerror.Newf( pgcode.OperatorIntervention, @@ -120,7 +122,7 @@ func (ef *execFactory) ConstructExport( return nil, errors.Errorf("EXPORT cannot be used inside a transaction") } - if fileFormat != "CSV" && fileFormat != "PARQUET" { + if fileFormat != csvSuffix && fileFormat != parquetSuffix { return nil, errors.Errorf("unsupported export format: %q", fileFormat) } @@ -154,49 +156,28 @@ func (ef *execFactory) ConstructExport( return nil, err } - var exportFilePattern string - - // evaluate csvOpts - var csvOpts *roachpb.CSVOptions - if err := func () error { - if fileFormat == "CSV" { - csvOpts = &roachpb.CSVOptions{} - } else{ - return nil - } + var ( + exportFilePattern string + csvOpts *roachpb.CSVOptions + parquetOpts *roachpb.ParquetOptions + ) + switch fileFormat { + case csvSuffix: + csvOpts = &roachpb.CSVOptions{} if override, ok := optVals[exportOptionDelimiter]; ok { csvOpts.Comma, err = util.GetSingleRune(override) if err != nil { - return pgerror.New(pgcode.InvalidParameterValue, "invalid delimiter") + return nil, pgerror.New(pgcode.InvalidParameterValue, "invalid delimiter") } } - if override, ok := optVals[exportOptionNullAs]; ok { csvOpts.NullEncoding = &override } - exportFilePattern = exportFilePatternPart+"."+csvSuffix - - return nil - }(); err != nil { - return nil, err - } - - // evaluate parquetOpts - var parquetOpts *roachpb.ParquetOptions - if err := func () error { - if fileFormat == "PARQUET" { - parquetOpts = &roachpb.ParquetOptions{} - } else { - return nil - } - if override, ok := optVals[exportOptionNullAs]; ok { - parquetOpts.NullEncoding = &override - } + exportFilePattern = exportFilePatternPart + "." + csvSuffix + case parquetSuffix: + parquetOpts = &roachpb.ParquetOptions{} exportFilePattern = exportFilePatternPart + "." + parquetSuffix - return nil - }(); err != nil { - return nil, err } chunkRows := exportChunkRowsDefault diff --git a/pkg/sql/rowexec/processors.go b/pkg/sql/rowexec/processors.go index 94802e0a433d..0c045c6dbe14 100644 --- a/pkg/sql/rowexec/processors.go +++ b/pkg/sql/rowexec/processors.go @@ -286,6 +286,16 @@ func NewProcessor( } return NewCSVWriterProcessor(flowCtx, processorID, *core.CSVWriter, inputs[0], outputs[0]) } + if core.ParquetWriter != nil { + if err := checkNumInOut(inputs, outputs, 1, 1); err != nil { + return nil, err + } + if NewParquetWriterProcessor == nil { + return nil, errors.New("ParquetWriter processor unimplemented") + } + return NewParquetWriterProcessor(flowCtx, processorID, *core.ParquetWriter, inputs[0], + outputs[0]) + } if core.BulkRowWriter != nil { if err := checkNumInOut(inputs, outputs, 1, 1); err != nil { return nil, err @@ -387,6 +397,9 @@ var NewStreamIngestionDataProcessor func(*execinfra.FlowCtx, int32, execinfrapb. // NewCSVWriterProcessor is implemented in the non-free (CCL) codebase and then injected here via runtime initialization. var NewCSVWriterProcessor func(*execinfra.FlowCtx, int32, execinfrapb.CSVWriterSpec, execinfra.RowSource, execinfra.RowReceiver) (execinfra.Processor, error) +// NewParquetWriterProcessor is implemented in the non-free (CCL) codebase and then injected here via runtime initialization. +var NewParquetWriterProcessor func(*execinfra.FlowCtx, int32, execinfrapb.ParquetWriterSpec, execinfra.RowSource, execinfra.RowReceiver) (execinfra.Processor, error) + // NewChangeAggregatorProcessor is implemented in the non-free (CCL) codebase and then injected here via runtime initialization. var NewChangeAggregatorProcessor func(*execinfra.FlowCtx, int32, execinfrapb.ChangeAggregatorSpec, *execinfrapb.PostProcessSpec, execinfra.RowReceiver) (execinfra.Processor, error) diff --git a/pkg/sql/sem/tree/format.go b/pkg/sql/sem/tree/format.go index 3be89bb769c3..36b521540f49 100644 --- a/pkg/sql/sem/tree/format.go +++ b/pkg/sql/sem/tree/format.go @@ -231,9 +231,6 @@ const ( // because the behavior of array_to_string() is fixed for compatibility // with PostgreSQL, whereas EXPORT may evolve over time to support // other things (eg. fixing #33429). - // - // TODO(mjibson): Note that this is currently not suitable for - // emitting arrays or tuples. See: #33429 FmtExport FmtFlags = FmtBareStrings | fmtRawStrings ) diff --git a/vendor b/vendor index cd95d58038ce..831e7b059d54 160000 --- a/vendor +++ b/vendor @@ -1 +1 @@ -Subproject commit cd95d58038ce94c8a855b9ad4ded813751f84cd9 +Subproject commit 831e7b059d54bfad94f8cae88c29adcbdfc8efd3 From ada65584420142d09573a6a6c0addbbcc27ccf29 Mon Sep 17 00:00:00 2001 From: Ahmad Abedalqader Date: Mon, 18 Oct 2021 07:21:02 -0700 Subject: [PATCH 164/205] roachprod: making roachprod subcommands point to a new library Previously, roachprod binary interfaced directly with roachorod's functionality and there was no way for another tool to make use of that functionality. This needed to change to create a library that can be used by roachprod binary and also other tools. This patch migrates the subcommands functionality to a new library and makes the binary point to the new library. Release note: None --- .github/CODEOWNERS | 1 + BUILD.bazel | 2 +- Makefile | 2 +- build/bazelutil/check.sh | 10 +- pkg/BUILD.bazel | 8 +- pkg/cmd/roachprod/BUILD.bazel | 34 +- pkg/cmd/roachprod/main.go | 1216 +++-------------- pkg/cmd/roachprod/vm/azure/flags.go | 71 - pkg/roachprod/BUILD.bazel | 32 + pkg/{cmd => }/roachprod/cloud/BUILD.bazel | 7 +- pkg/{cmd => }/roachprod/cloud/cloud_test.go | 0 .../roachprod/cloud/cluster_cloud.go | 4 +- pkg/{cmd => }/roachprod/cloud/gc.go | 17 +- .../roachprod/cloud/gc_aws_keypairs.go | 14 +- pkg/{cmd => }/roachprod/config/BUILD.bazel | 3 +- pkg/{cmd => }/roachprod/config/config.go | 6 +- pkg/{cmd => }/roachprod/errors/BUILD.bazel | 2 +- pkg/{cmd => }/roachprod/errors/errors.go | 0 pkg/{cmd => }/roachprod/hosts.go | 19 +- pkg/{cmd => }/roachprod/install/BUILD.bazel | 11 +- .../roachprod/install/cluster_synced.go | 37 +- pkg/{cmd => }/roachprod/install/cockroach.go | 20 +- pkg/{cmd => }/roachprod/install/download.go | 0 pkg/{cmd => }/roachprod/install/expander.go | 0 pkg/{cmd => }/roachprod/install/install.go | 0 pkg/{cmd => }/roachprod/install/iterm2.go | 0 pkg/{cmd => }/roachprod/install/nodes.go | 0 .../roachprod/install/scripts/download.sh | 0 .../roachprod/install/scripts/start.sh | 0 .../roachprod/install/scripts/start_test.sh | 0 pkg/{cmd => }/roachprod/install/session.go | 2 +- pkg/{cmd => }/roachprod/install/staging.go | 0 .../roachprod/install/start_template_test.go | 0 .../install/testdata/start/start.txt | 0 pkg/{cmd => }/roachprod/k8s/roachprod-gc.yaml | 0 pkg/roachprod/roachprod.go | 1208 ++++++++++++++++ pkg/{cmd => }/roachprod/ssh/BUILD.bazel | 2 +- pkg/{cmd => }/roachprod/ssh/io.go | 0 pkg/{cmd => }/roachprod/ssh/io_test.go | 0 pkg/{cmd => }/roachprod/ssh/shell.go | 0 pkg/{cmd => }/roachprod/ui/BUILD.bazel | 2 +- pkg/{cmd => }/roachprod/ui/collate_errors.go | 0 pkg/{cmd => }/roachprod/ui/writer.go | 0 pkg/{cmd => }/roachprod/vm/BUILD.bazel | 5 +- pkg/{cmd => }/roachprod/vm/aws/.gitattributes | 0 pkg/{cmd => }/roachprod/vm/aws/BUILD.bazel | 11 +- pkg/{cmd => }/roachprod/vm/aws/README.md | 0 pkg/{cmd => }/roachprod/vm/aws/aws.go | 80 +- pkg/{cmd => }/roachprod/vm/aws/config.go | 0 pkg/{cmd => }/roachprod/vm/aws/config.json | 0 pkg/{cmd => }/roachprod/vm/aws/embedded.go | 0 pkg/{cmd => }/roachprod/vm/aws/keys.go | 0 pkg/{cmd => }/roachprod/vm/aws/old.json | 0 pkg/{cmd => }/roachprod/vm/aws/support.go | 7 +- .../roachprod/vm/aws/terraform/.gitignore | 0 .../vm/aws/terraform/aws-region/ami.tf | 0 .../vm/aws/terraform/aws-region/main.tf | 0 .../vm/aws/terraform/aws-region/network.tf | 0 .../vm/aws/terraform/aws-vpc-peer/main.tf | 0 .../roachprod/vm/aws/terraform/main.tf | 0 .../roachprod/vm/aws/terraformgen/BUILD.bazel | 7 +- .../vm/aws/terraformgen/terraformgen.go | 3 +- pkg/{cmd => }/roachprod/vm/azure/BUILD.bazel | 7 +- pkg/{cmd => }/roachprod/vm/azure/auth.go | 0 pkg/{cmd => }/roachprod/vm/azure/azure.go | 84 +- pkg/{cmd => }/roachprod/vm/azure/doc.go | 0 pkg/roachprod/vm/azure/flags.go | 96 ++ pkg/{cmd => }/roachprod/vm/azure/ids.go | 0 pkg/{cmd => }/roachprod/vm/azure/utils.go | 0 .../roachprod/vm/flagstub/BUILD.bazel | 4 +- .../roachprod/vm/flagstub/flagstub.go | 2 +- pkg/{cmd => }/roachprod/vm/gce/BUILD.bazel | 8 +- pkg/{cmd => }/roachprod/vm/gce/gcloud.go | 93 +- pkg/{cmd => }/roachprod/vm/gce/utils.go | 2 +- pkg/{cmd => }/roachprod/vm/local/BUILD.bazel | 8 +- pkg/{cmd => }/roachprod/vm/local/local.go | 10 +- pkg/{cmd => }/roachprod/vm/vm.go | 26 +- pkg/{cmd => }/roachprod/vm/vm_test.go | 0 pkg/testutils/lint/lint_test.go | 3 + 79 files changed, 1858 insertions(+), 1328 deletions(-) delete mode 100644 pkg/cmd/roachprod/vm/azure/flags.go create mode 100644 pkg/roachprod/BUILD.bazel rename pkg/{cmd => }/roachprod/cloud/BUILD.bazel (85%) rename pkg/{cmd => }/roachprod/cloud/cloud_test.go (100%) rename pkg/{cmd => }/roachprod/cloud/cluster_cloud.go (98%) rename pkg/{cmd => }/roachprod/cloud/gc.go (94%) rename pkg/{cmd => }/roachprod/cloud/gc_aws_keypairs.go (93%) rename pkg/{cmd => }/roachprod/config/BUILD.bazel (59%) rename pkg/{cmd => }/roachprod/config/config.go (90%) rename pkg/{cmd => }/roachprod/errors/BUILD.bazel (73%) rename pkg/{cmd => }/roachprod/errors/errors.go (100%) rename pkg/{cmd => }/roachprod/hosts.go (91%) rename pkg/{cmd => }/roachprod/install/BUILD.bazel (82%) rename pkg/{cmd => }/roachprod/install/cluster_synced.go (97%) rename pkg/{cmd => }/roachprod/install/cockroach.go (97%) rename pkg/{cmd => }/roachprod/install/download.go (100%) rename pkg/{cmd => }/roachprod/install/expander.go (100%) rename pkg/{cmd => }/roachprod/install/install.go (100%) rename pkg/{cmd => }/roachprod/install/iterm2.go (100%) rename pkg/{cmd => }/roachprod/install/nodes.go (100%) rename pkg/{cmd => }/roachprod/install/scripts/download.sh (100%) rename pkg/{cmd => }/roachprod/install/scripts/start.sh (100%) rename pkg/{cmd => }/roachprod/install/scripts/start_test.sh (100%) rename pkg/{cmd => }/roachprod/install/session.go (98%) rename pkg/{cmd => }/roachprod/install/staging.go (100%) rename pkg/{cmd => }/roachprod/install/start_template_test.go (100%) rename pkg/{cmd => }/roachprod/install/testdata/start/start.txt (100%) rename pkg/{cmd => }/roachprod/k8s/roachprod-gc.yaml (100%) create mode 100644 pkg/roachprod/roachprod.go rename pkg/{cmd => }/roachprod/ssh/BUILD.bazel (78%) rename pkg/{cmd => }/roachprod/ssh/io.go (100%) rename pkg/{cmd => }/roachprod/ssh/io_test.go (100%) rename pkg/{cmd => }/roachprod/ssh/shell.go (100%) rename pkg/{cmd => }/roachprod/ui/BUILD.bazel (72%) rename pkg/{cmd => }/roachprod/ui/collate_errors.go (100%) rename pkg/{cmd => }/roachprod/ui/writer.go (100%) rename pkg/{cmd => }/roachprod/vm/BUILD.bazel (78%) rename pkg/{cmd => }/roachprod/vm/aws/.gitattributes (100%) rename pkg/{cmd => }/roachprod/vm/aws/BUILD.bazel (77%) rename pkg/{cmd => }/roachprod/vm/aws/README.md (100%) rename pkg/{cmd => }/roachprod/vm/aws/aws.go (90%) rename pkg/{cmd => }/roachprod/vm/aws/config.go (100%) rename pkg/{cmd => }/roachprod/vm/aws/config.json (100%) rename pkg/{cmd => }/roachprod/vm/aws/embedded.go (100%) rename pkg/{cmd => }/roachprod/vm/aws/keys.go (100%) rename pkg/{cmd => }/roachprod/vm/aws/old.json (100%) rename pkg/{cmd => }/roachprod/vm/aws/support.go (97%) rename pkg/{cmd => }/roachprod/vm/aws/terraform/.gitignore (100%) rename pkg/{cmd => }/roachprod/vm/aws/terraform/aws-region/ami.tf (100%) rename pkg/{cmd => }/roachprod/vm/aws/terraform/aws-region/main.tf (100%) rename pkg/{cmd => }/roachprod/vm/aws/terraform/aws-region/network.tf (100%) rename pkg/{cmd => }/roachprod/vm/aws/terraform/aws-vpc-peer/main.tf (100%) rename pkg/{cmd => }/roachprod/vm/aws/terraform/main.tf (100%) rename pkg/{cmd => }/roachprod/vm/aws/terraformgen/BUILD.bazel (63%) rename pkg/{cmd => }/roachprod/vm/aws/terraformgen/terraformgen.go (98%) rename pkg/{cmd => }/roachprod/vm/azure/BUILD.bazel (86%) rename pkg/{cmd => }/roachprod/vm/azure/auth.go (100%) rename pkg/{cmd => }/roachprod/vm/azure/azure.go (94%) rename pkg/{cmd => }/roachprod/vm/azure/doc.go (100%) create mode 100644 pkg/roachprod/vm/azure/flags.go rename pkg/{cmd => }/roachprod/vm/azure/ids.go (100%) rename pkg/{cmd => }/roachprod/vm/azure/utils.go (100%) rename pkg/{cmd => }/roachprod/vm/flagstub/BUILD.bazel (66%) rename pkg/{cmd => }/roachprod/vm/flagstub/flagstub.go (97%) rename pkg/{cmd => }/roachprod/vm/gce/BUILD.bazel (63%) rename pkg/{cmd => }/roachprod/vm/gce/gcloud.go (90%) rename pkg/{cmd => }/roachprod/vm/gce/utils.go (99%) rename pkg/{cmd => }/roachprod/vm/local/BUILD.bazel (61%) rename pkg/{cmd => }/roachprod/vm/local/local.go (92%) rename pkg/{cmd => }/roachprod/vm/vm.go (93%) rename pkg/{cmd => }/roachprod/vm/vm_test.go (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fad4a3191cbb..2924c36672ad 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -236,6 +236,7 @@ /pkg/roachpb/string_test.go @cockroachdb/kv-prs /pkg/roachpb/tenant* @cockroachdb/kv-prs /pkg/roachpb/version* @cockroachdb/server +/pkg/roachprod/ @cockroachdb/dev-inf /pkg/rpc/ @cockroachdb/server-prs /pkg/scheduledjobs/ @cockroachdb/bulk-prs /pkg/security/ @cockroachdb/server-prs @cockroachdb/prodsec diff --git a/BUILD.bazel b/BUILD.bazel index da1edd0c12c9..0a6c5893a08b 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -116,7 +116,7 @@ load("@bazel_gazelle//:def.bzl", "gazelle") # gazelle:exclude pkg/cmd/prereqs/testdata # gazelle:exclude pkg/testutils/**/testdata/** # gazelle:exclude pkg/security/securitytest/embedded.go -# gazelle:exclude pkg/cmd/roachprod/vm/aws/embedded.go +# gazelle:exclude pkg/roachprod/vm/aws/embedded.go # gazelle:exclude pkg/**/*_string.go # gazelle:exclude pkg/ui/distccl/distccl_no_bazel.go # gazelle:exclude pkg/ui/distoss/distoss_no_bazel.go diff --git a/Makefile b/Makefile index e91828bc7ce8..ceb6b152d8da 100644 --- a/Makefile +++ b/Makefile @@ -1758,7 +1758,7 @@ optgen-package = ./pkg/sql/opt/optgen/cmd/optgen logictest-package = ./pkg/sql/logictest logictestccl-package = ./pkg/ccl/logictestccl logictestopt-package = ./pkg/sql/opt/exec/execbuilder -terraformgen-package = ./pkg/cmd/roachprod/vm/aws/terraformgen +terraformgen-package = ./pkg/roachprod/vm/aws/terraformgen logictest-bins := bin/logictest bin/logictestopt bin/logictestccl # Additional dependencies for binaries that depend on generated code. diff --git a/build/bazelutil/check.sh b/build/bazelutil/check.sh index 45252e875af2..b48c48554415 100755 --- a/build/bazelutil/check.sh +++ b/build/bazelutil/check.sh @@ -3,10 +3,10 @@ set -euo pipefail EXISTING_GO_GENERATE_COMMENTS=" -pkg/cmd/roachprod/vm/aws/config.go://go:generate go-bindata -mode 0600 -modtime 1400000000 -pkg aws -o embedded.go config.json old.json -pkg/cmd/roachprod/vm/aws/config.go://go:generate gofmt -s -w embedded.go -pkg/cmd/roachprod/vm/aws/config.go://go:generate goimports -w embedded.go -pkg/cmd/roachprod/vm/aws/config.go://go:generate terraformgen -o terraform/main.tf +pkg/roachprod/vm/aws/config.go://go:generate go-bindata -mode 0600 -modtime 1400000000 -pkg aws -o embedded.go config.json old.json +pkg/roachprod/vm/aws/config.go://go:generate gofmt -s -w embedded.go +pkg/roachprod/vm/aws/config.go://go:generate goimports -w embedded.go +pkg/roachprod/vm/aws/config.go://go:generate terraformgen -o terraform/main.tf pkg/cmd/roachtest/prometheus/prometheus.go://go:generate mockgen -package=prometheus -destination=mock_generated.go -source=prometheus.go . Cluster pkg/cmd/roachtest/tests/drt.go://go:generate mockgen -source drt.go -package tests -destination drt_generated.go pkg/kv/kvclient/kvcoord/transport.go://go:generate mockgen -package=kvcoord -destination=mocks_generated.go . Transport @@ -84,4 +84,4 @@ git grep 'broken_in_bazel' pkg | grep BUILD.bazel: | grep -v pkg/BUILD.bazel | g echo "A new broken test in Bazel was added in $LINE" echo 'Ensure the test runs with Bazel, then remove the broken_in_bazel tag.' exit 1 -done +done \ No newline at end of file diff --git a/pkg/BUILD.bazel b/pkg/BUILD.bazel index 38d9147867af..26728b28f236 100644 --- a/pkg/BUILD.bazel +++ b/pkg/BUILD.bazel @@ -80,10 +80,6 @@ ALL_TESTS = [ "//pkg/cmd/prereqs:prereqs_test", "//pkg/cmd/publish-artifacts:publish-artifacts_test", "//pkg/cmd/publish-provisional-artifacts:publish-provisional-artifacts_test", - "//pkg/cmd/roachprod/cloud:cloud_test", - "//pkg/cmd/roachprod/install:install_test", - "//pkg/cmd/roachprod/ssh:ssh_test", - "//pkg/cmd/roachprod/vm:vm_test", "//pkg/cmd/roachtest/prometheus:prometheus_test", "//pkg/cmd/roachtest/spec:spec_test", "//pkg/cmd/roachtest/tests:tests_test", @@ -161,6 +157,10 @@ ALL_TESTS = [ "//pkg/migration/migrations:migrations_test", "//pkg/roachpb:roachpb_test", "//pkg/roachpb:string_test", + "//pkg/roachprod/cloud:cloud_test", + "//pkg/roachprod/install:install_test", + "//pkg/roachprod/ssh:ssh_test", + "//pkg/roachprod/vm:vm_test", "//pkg/rpc/nodedialer:nodedialer_test", "//pkg/rpc:rpc_test", "//pkg/security/certmgr:certmgr_test", diff --git a/pkg/cmd/roachprod/BUILD.bazel b/pkg/cmd/roachprod/BUILD.bazel index 7aaea4846a81..bfee26e9291f 100644 --- a/pkg/cmd/roachprod/BUILD.bazel +++ b/pkg/cmd/roachprod/BUILD.bazel @@ -2,35 +2,25 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "roachprod_lib", - srcs = [ - "hosts.go", - "main.go", - ], + srcs = ["main.go"], importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod", visibility = ["//visibility:private"], deps = [ "//pkg/build", - "//pkg/cmd/roachprod/cloud", - "//pkg/cmd/roachprod/config", - "//pkg/cmd/roachprod/errors", - "//pkg/cmd/roachprod/install", - "//pkg/cmd/roachprod/ssh", - "//pkg/cmd/roachprod/ui", - "//pkg/cmd/roachprod/vm", - "//pkg/cmd/roachprod/vm/aws", - "//pkg/cmd/roachprod/vm/azure", - "//pkg/cmd/roachprod/vm/gce", - "//pkg/cmd/roachprod/vm/local", - "//pkg/util/ctxgroup", + "//pkg/roachprod", + "//pkg/roachprod/config", + "//pkg/roachprod/errors", + "//pkg/roachprod/install", + "//pkg/roachprod/ssh", + "//pkg/roachprod/ui", + "//pkg/roachprod/vm", + "//pkg/roachprod/vm/aws", + "//pkg/roachprod/vm/azure", + "//pkg/roachprod/vm/gce", + "//pkg/roachprod/vm/local", "//pkg/util/flagutil", - "//pkg/util/httputil", - "//pkg/util/syncutil", - "//pkg/util/timeutil", "@com_github_cockroachdb_errors//:errors", - "@com_github_cockroachdb_errors//oserror", "@com_github_spf13_cobra//:cobra", - "@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 a0bc1d0a70f3..6c5c2b4711d3 100644 --- a/pkg/cmd/roachprod/main.go +++ b/pkg/cmd/roachprod/main.go @@ -11,48 +11,32 @@ package main import ( - "context" "encoding/json" "fmt" - "io" - "io/ioutil" - "log" - "net" - "net/http" "os" - "os/exec" "os/user" "path" - "path/filepath" - "regexp" - "runtime" "sort" "strings" "text/tabwriter" "time" "github.com/cockroachdb/cockroach/pkg/build" - cld "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/cloud" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - rperrors "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/errors" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/install" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ssh" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ui" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/aws" - _ "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/azure" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/gce" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/local" - "github.com/cockroachdb/cockroach/pkg/util/ctxgroup" + "github.com/cockroachdb/cockroach/pkg/roachprod" + // cld "github.com/cockroachdb/cockroach/pkg/roachprod/cloud" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + rperrors "github.com/cockroachdb/cockroach/pkg/roachprod/errors" + "github.com/cockroachdb/cockroach/pkg/roachprod/install" + "github.com/cockroachdb/cockroach/pkg/roachprod/ssh" + "github.com/cockroachdb/cockroach/pkg/roachprod/ui" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + _ "github.com/cockroachdb/cockroach/pkg/roachprod/vm/aws" + _ "github.com/cockroachdb/cockroach/pkg/roachprod/vm/azure" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/gce" + _ "github.com/cockroachdb/cockroach/pkg/roachprod/vm/local" "github.com/cockroachdb/cockroach/pkg/util/flagutil" - "github.com/cockroachdb/cockroach/pkg/util/httputil" - "github.com/cockroachdb/cockroach/pkg/util/syncutil" - "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" - "github.com/cockroachdb/errors/oserror" "github.com/spf13/cobra" - "golang.org/x/sys/unix" - "golang.org/x/term" ) var rootCmd = &cobra.Command{ @@ -79,19 +63,21 @@ destroy the cluster. } var ( - numNodes int - numRacks int - username string - dryrun bool - destroyAllMine bool - extendLifetime time.Duration - wipePreserveCerts bool - listDetails bool - listJSON bool - listMine bool - secure = false - extraSSHOptions = "" - nodeEnv = []string{ + numNodes int + numRacks int + username string + dryrun bool + destroyAllMine bool + extendLifetime time.Duration + wipePreserveCerts bool + listDetails bool + listJSON bool + listMine bool + listPattern string + sqlCockroachBinary = "cockroach" + secure = false + extraSSHOptions = "" + nodeEnv = []string{ "COCKROACH_ENABLE_RPC_COMPRESSION=false", "COCKROACH_UI_RELEASE_NOTES_SIGNUP_DISMISSED=true", } @@ -108,6 +94,7 @@ var ( quiet = false sig = 9 waitFlag = false + startOpts = install.StartOptsType{} stageOS string stageDir string logsDir string @@ -124,152 +111,6 @@ var ( cachedHostsCluster string ) -func sortedClusters() []string { - var r []string - for n := range install.Clusters { - r = append(r, n) - } - sort.Strings(r) - return r -} - -func newCluster(name string) (*install.SyncedCluster, error) { - nodeNames := "all" - { - parts := strings.Split(name, ":") - switch len(parts) { - case 2: - nodeNames = parts[1] - fallthrough - case 1: - name = parts[0] - case 0: - return nil, fmt.Errorf("no cluster specified") - default: - return nil, fmt.Errorf("invalid cluster name: %s", name) - } - } - - c, ok := install.Clusters[name] - if !ok { - err := errors.Newf(`unknown cluster: %s`, name) - err = errors.WithHintf(err, ` -Available clusters: - %s -`, strings.Join(sortedClusters(), "\n ")) - err = errors.WithHint(err, `Use "roachprod sync" to update the list of available clusters.`) - return nil, err - } - - c.Impl = install.Cockroach{} - if numRacks > 0 { - for i := range c.Localities { - rack := fmt.Sprintf("rack=%d", i%numRacks) - if c.Localities[i] != "" { - rack = "," + rack - } - c.Localities[i] += rack - } - } - - nodes, err := install.ListNodes(nodeNames, len(c.VMs)) - if err != nil { - return nil, err - } - for _, n := range nodes { - if n > len(c.VMs) { - return nil, fmt.Errorf("invalid node spec %s, cluster contains %d nodes", - nodeNames, len(c.VMs)) - } - } - c.Nodes = nodes - c.Secure = secure - c.CertsDir = certsDir - c.Env = nodeEnv - c.Args = nodeArgs - if tag != "" { - c.Tag = "/" + tag - } - c.UseTreeDist = useTreeDist - c.Quiet = quiet || !term.IsTerminal(int(os.Stdout.Fd())) - c.MaxConcurrency = maxConcurrency - return c, nil -} - -// verifyClusterName ensures that the given name conforms to -// our naming pattern of "-". The -// username must match one of the vm.Provider account names -// or the --username override. -func verifyClusterName(clusterName string) (string, error) { - if len(clusterName) == 0 { - return "", fmt.Errorf("cluster name cannot be blank") - } - if clusterName == config.Local { - return clusterName, nil - } - - alphaNum, err := regexp.Compile(`^[a-zA-Z0-9\-]+$`) - if err != nil { - return "", err - } - if !alphaNum.MatchString(clusterName) { - return "", errors.Errorf("cluster name must match %s", alphaNum.String()) - } - - // Use the vm.Provider account names, or --username. - var accounts []string - if len(username) > 0 { - accounts = []string{username} - } else { - seenAccounts := map[string]bool{} - active, err := vm.FindActiveAccounts() - if err != nil { - return "", err - } - for _, account := range active { - if !seenAccounts[account] { - seenAccounts[account] = true - cleanAccount := vm.DNSSafeAccount(account) - if cleanAccount != account { - log.Printf("WARN: using `%s' as username instead of `%s'", cleanAccount, account) - } - accounts = append(accounts, cleanAccount) - } - } - } - - // If we see -, accept it. - for _, account := range accounts { - if strings.HasPrefix(clusterName, account+"-") && len(clusterName) > len(account)+1 { - return clusterName, nil - } - } - - // Try to pick out a reasonable cluster name from the input. - i := strings.Index(clusterName, "-") - suffix := clusterName - if i != -1 { - // The user specified a username prefix, but it didn't match an active - // account name. For example, assuming the account is "peter", `roachprod - // create joe-perf` should be specified as `roachprod create joe-perf -u - // joe`. - suffix = clusterName[i+1:] - } else { - // The user didn't specify a username prefix. For example, assuming the - // account is "peter", `roachprod create perf` should be specified as - // `roachprod create peter-perf`. - _ = 0 - } - - // Suggest acceptable cluster names. - var suggestions []string - for _, account := range accounts { - suggestions = append(suggestions, fmt.Sprintf("%s-%s", account, suffix)) - } - return "", fmt.Errorf("malformed cluster name %s, did you mean one of %s", - clusterName, suggestions) -} - // Provide `cobra.Command` functions with a standard return code handler. // Exit codes come from rperrors.Error.ExitCode(). // @@ -295,18 +136,6 @@ func wrap(f func(cmd *cobra.Command, args []string) error) func(cmd *cobra.Comma var createVMOpts vm.CreateOpts -type clusterAlreadyExistsError struct { - name string -} - -func (e *clusterAlreadyExistsError) Error() string { - return fmt.Sprintf("cluster %s already exists", e.name) -} - -func newClusterAlreadyExistsError(name string) error { - return &clusterAlreadyExistsError{name: name} -} - var createCmd = &cobra.Command{ Use: "create ", Short: "create a cluster", @@ -353,75 +182,12 @@ Local Clusters `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) (retErr error) { - if numNodes <= 0 || numNodes >= 1000 { - // Upper limit is just for safety. - return fmt.Errorf("number of nodes must be in [1..999]") - } - - clusterName, err := verifyClusterName(args[0]) - if err != nil { - return err - } - createVMOpts.ClusterName = clusterName - - defer func() { - if retErr == nil || clusterName == config.Local { - return - } - if errors.HasType(retErr, (*clusterAlreadyExistsError)(nil)) { - return - } - fmt.Fprintf(os.Stderr, "Cleaning up partially-created cluster (prev err: %s)\n", retErr) - if err := cleanupFailedCreate(clusterName); err != nil { - fmt.Fprintf(os.Stderr, "Error while cleaning up partially-created cluster: %s\n", err) - } else { - fmt.Fprintf(os.Stderr, "Cleaning up OK\n") - } - }() - - if clusterName != config.Local { - cloud, err := cld.ListCloud() - if err != nil { - return err - } - if _, ok := cloud.Clusters[clusterName]; ok { - return newClusterAlreadyExistsError(clusterName) - } - } else { - if _, ok := install.Clusters[clusterName]; ok { - return newClusterAlreadyExistsError(clusterName) - } - - // If the local cluster is being created, force the local Provider to be used - createVMOpts.VMProviders = []string{local.ProviderName} - } - - if createVMOpts.SSDOpts.FileSystem == vm.Zfs { - for _, provider := range createVMOpts.VMProviders { - if provider != gce.ProviderName { - return fmt.Errorf( - "creating a node with --filesystem=zfs is currently only supported on gce", - ) - } - } - } - - fmt.Printf("Creating cluster %s with %d nodes\n", clusterName, numNodes) - if createErr := cld.CreateCluster(numNodes, createVMOpts); createErr != nil { - return createErr - } - - // Just create directories for the local cluster as there's no need for ssh. - if clusterName == config.Local { - for i := 0; i < numNodes; i++ { - err := os.MkdirAll(fmt.Sprintf(os.ExpandEnv("${HOME}/local/%d"), i+1), 0755) - if err != nil { - return err - } - } - return nil + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return setupSSH(clusterName) + return roachprod.Create(numNodes, username, createVMOpts, clusterOpts) }), } @@ -440,76 +206,15 @@ if the user would like to update the keys on the remote hosts. Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) (retErr error) { - clusterName, err := verifyClusterName(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return setupSSH(clusterName) + return roachprod.SetupSSH(clusterOpts, username) }), } -func setupSSH(clusterName string) error { - cloud, err := syncCloud(quiet) - if err != nil { - return err - } - cloudCluster, ok := cloud.Clusters[clusterName] - if !ok { - return fmt.Errorf("could not find %s in list of cluster", clusterName) - } - cloudCluster.PrintDetails() - // Run ssh-keygen -R serially on each new VM in case an IP address has been recycled - for _, v := range cloudCluster.VMs { - cmd := exec.Command("ssh-keygen", "-R", v.PublicIP) - out, err := cmd.CombinedOutput() - if err != nil { - log.Printf("could not clear ssh key for hostname %s:\n%s", v.PublicIP, string(out)) - } - - } - - // Wait for the nodes in the cluster to start. - install.Clusters = map[string]*install.SyncedCluster{} - if err := loadClusters(); err != nil { - return err - } - installCluster, err := newCluster(clusterName) - if err != nil { - return err - } - // For GCP clusters we need to use the config.OSUser even if the client - // requested the shared user. - for i := range installCluster.VMs { - if cloudCluster.VMs[i].Provider == gce.ProviderName { - installCluster.Users[i] = config.OSUser.Username - } - } - if err := installCluster.Wait(); err != nil { - return err - } - // Fetch public keys from gcloud to set up ssh access for all users into the - // shared ubuntu user. - installCluster.AuthorizedKeys, err = gce.GetUserAuthorizedKeys() - if err != nil { - return errors.Wrap(err, "failed to retrieve authorized keys from gcloud") - } - return installCluster.SetupSSH() -} - -func cleanupFailedCreate(clusterName string) error { - cloud, err := cld.ListCloud() - if err != nil { - return err - } - c, ok := cloud.Clusters[clusterName] - if !ok { - // If the cluster doesn't exist, we didn't manage to create any VMs - // before failing. Not an error. - return nil - } - return cld.DestroyCluster(c) -} - var destroyCmd = &cobra.Command{ Use: "destroy [ --all-mine | [ ...] ]", Short: "destroy clusters", @@ -526,139 +231,44 @@ directory is removed. `, Args: cobra.ArbitraryArgs, Run: wrap(func(cmd *cobra.Command, args []string) error { - type cloudAndName struct { - name string - cloud *cld.Cloud - } - var cns []cloudAndName - switch len(args) { - case 0: - if !destroyAllMine { - return errors.New("no cluster name provided") - } - - destroyPattern, err := userClusterNameRegexp() - if err != nil { - return err - } - - cloud, err := cld.ListCloud() - if err != nil { - return err - } - - for name := range cloud.Clusters { - if destroyPattern.MatchString(name) { - cns = append(cns, cloudAndName{name: name, cloud: cloud}) - } - } - - default: - if destroyAllMine { - return errors.New("--all-mine cannot be combined with cluster names") - } - - var cloud *cld.Cloud - for _, arg := range args { - clusterName, err := verifyClusterName(arg) - if err != nil { - return err - } - - if clusterName != config.Local { - if cloud == nil { - cloud, err = cld.ListCloud() - if err != nil { - return err - } - } - - cns = append(cns, cloudAndName{name: clusterName, cloud: cloud}) - } else { - if err := destroyLocalCluster(); err != nil { - return err - } - } + var clusters []install.SyncedCluster + for _, clusterName := range args { + cluster := install.SyncedCluster{ + Name: clusterName, Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } + clusters = append(clusters, cluster) } - - if err := ctxgroup.GroupWorkers(cmd.Context(), len(cns), func(ctx context.Context, idx int) error { - return destroyCluster(cns[idx].cloud, cns[idx].name) - }); err != nil { - return err - } - fmt.Println("OK") - return nil + return roachprod.Destroy(clusters, destroyAllMine, username) }), } -func destroyCluster(cloud *cld.Cloud, clusterName string) error { - c, ok := cloud.Clusters[clusterName] - if !ok { - return fmt.Errorf("cluster %s does not exist", clusterName) - } - fmt.Printf("Destroying cluster %s with %d nodes\n", clusterName, len(c.VMs)) - return cld.DestroyCluster(c) -} - -func destroyLocalCluster() error { - if _, ok := install.Clusters[config.Local]; !ok { - return fmt.Errorf("cluster %s does not exist", config.Local) - } - c, err := newCluster(config.Local) - if err != nil { - return err - } - c.Wipe(false) - for _, i := range c.Nodes { - err := os.RemoveAll(fmt.Sprintf(os.ExpandEnv("${HOME}/local/%d"), i)) - if err != nil { - return err - } - } - return os.Remove(filepath.Join(os.ExpandEnv(config.DefaultHostDir), c.Name)) -} - var cachedHostsCmd = &cobra.Command{ Use: "cached-hosts", Short: "list all clusters (and optionally their host numbers) from local cache", Args: cobra.NoArgs, Run: wrap(func(cmd *cobra.Command, args []string) error { - if err := loadClusters(); err != nil { + cachedHosts, err := roachprod.CachedHosts(cachedHostsCluster) + if err != nil { return err } - - names := make([]string, 0, len(install.Clusters)) - for name := range install.Clusters { - names = append(names, name) - } - sort.Strings(names) - - for _, name := range names { - c := install.Clusters[name] - if strings.HasPrefix(c.Name, "teamcity") { + for _, host := range cachedHosts { + if strings.HasPrefix(host, "teamcity") { continue } - fmt.Print(c.Name) - // when invoked by bash-completion, cachedHostsCluster is what the user - // has currently typed -- if this cluster matches that, expand its hosts. - if strings.HasPrefix(cachedHostsCluster, c.Name) { - for i := range c.VMs { - fmt.Printf(" %s:%d", c.Name, i+1) - } - } - fmt.Println() + fmt.Println(host) } return nil }), } var listCmd = &cobra.Command{ - Use: "list [--details] [ --mine | ]", + Use: "list [--details | --json] [ --mine | --pattern ]", Short: "list all clusters", Long: `List all clusters. -The list command accepts an optional positional argument, which is a regular +The list command accepts a flag --pattern which is a regular expression that will be matched against the cluster name pattern. Alternatively, the --mine flag can be provided to list the clusters that are owned by the current user. @@ -698,53 +308,26 @@ The --json flag sets the format of the command output to json. Listing clusters has the side-effect of syncing ssh keys/configs and the local hosts file. `, - Args: cobra.RangeArgs(0, 1), + Args: cobra.NoArgs, Run: wrap(func(cmd *cobra.Command, args []string) error { - listPattern := regexp.MustCompile(".*") - switch len(args) { - case 0: - if listMine { - var err error - listPattern, err = userClusterNameRegexp() - if err != nil { - return err - } - } - case 1: - if listMine { - return errors.New("--mine cannot be combined with a pattern") - } - var err error - listPattern, err = regexp.Compile(args[0]) - if err != nil { - return errors.Wrapf(err, "could not compile regex pattern: %s", args[0]) - } - default: - return errors.New("only a single pattern may be listed") + if listJSON && listDetails { + return errors.New("'json' option cannot be combined with 'details' option") } - - cloud, err := syncCloud(quiet) + filteredCloud, err := roachprod.List(quiet, listMine, listPattern) if err != nil { return err } - // Filter and sort by cluster names for stable output. - var names []string - filteredCloud := cloud.Clone() - for name := range cloud.Clusters { - if listPattern.MatchString(name) { - names = append(names, name) - } else { - delete(filteredCloud.Clusters, name) - } + // sort by cluster names for stable output. + names := make([]string, len(filteredCloud.Clusters)) + i := 0 + for name := range filteredCloud.Clusters { + names[i] = name + i++ } sort.Strings(names) if listJSON { - if listDetails { - return errors.New("--json cannot be combined with --detail") - } - enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") if err := enc.Encode(filteredCloud); err != nil { @@ -791,30 +374,7 @@ hosts file. }), } -// userClusterNameRegexp returns a regexp that matches all clusters owned by the -// current user. -func userClusterNameRegexp() (*regexp.Regexp, error) { - // In general, we expect that users will have the same - // account name across the services they're using, - // but we still want to function even if this is not - // the case. - seenAccounts := map[string]bool{} - accounts, err := vm.FindActiveAccounts() - if err != nil { - return nil, err - } - pattern := "" - for _, account := range accounts { - if !seenAccounts[account] { - seenAccounts[account] = true - if len(pattern) > 0 { - pattern += "|" - } - pattern += fmt.Sprintf("(^%s-)", regexp.QuoteMeta(account)) - } - } - return regexp.Compile(pattern) -} +var bashCompletion = os.ExpandEnv("$HOME/.roachprod/bash-completion.sh") // TODO(peter): Do we need this command given that the "list" command syncs as // a side-effect. If you don't care about the list output, just "roachprod list @@ -825,102 +385,12 @@ var syncCmd = &cobra.Command{ Long: ``, Args: cobra.NoArgs, Run: wrap(func(cmd *cobra.Command, args []string) error { - _, err := syncCloud(quiet) + _, err := roachprod.Sync(quiet) + _ = rootCmd.GenBashCompletionFile(bashCompletion) return err }), } -var lockFile = os.ExpandEnv("$HOME/.roachprod/LOCK") - -var bashCompletion = os.ExpandEnv("$HOME/.roachprod/bash-completion.sh") - -// syncCloud grabs an exclusive lock on the roachprod state and then proceeds to -// read the current state from the cloud and write it out to disk. The locking -// protects both the reading and the writing in order to prevent the hazard -// caused by concurrent goroutines reading cloud state in a different order -// than writing it to disk. -func syncCloud(quiet bool) (*cld.Cloud, error) { - if !quiet { - fmt.Println("Syncing...") - } - // Acquire a filesystem lock so that two concurrent synchronizations of - // roachprod state don't clobber each other. - f, err := os.Create(lockFile) - if err != nil { - return nil, errors.Wrapf(err, "creating lock file %q", lockFile) - } - if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { - return nil, errors.Wrap(err, "acquiring lock on %q") - } - defer f.Close() - cloud, err := cld.ListCloud() - if err != nil { - return nil, err - } - if err := syncHosts(cloud); err != nil { - return nil, err - } - - var vms vm.List - for _, c := range cloud.Clusters { - vms = append(vms, c.VMs...) - } - - // Figure out if we're going to overwrite the DNS entries. We don't want to - // overwrite if we don't have all the VMs of interest, so we only do it if we - // have a list of all VMs from both AWS and GCE (so if both providers have - // been used to get the VMs and for GCP also if we listed the VMs in the - // default project). - refreshDNS := true - - if p := vm.Providers[gce.ProviderName]; !p.Active() { - refreshDNS = false - } else { - var defaultProjectFound bool - for _, prj := range p.(*gce.Provider).GetProjects() { - if prj == gce.DefaultProject() { - defaultProjectFound = true - break - } - } - if !defaultProjectFound { - refreshDNS = false - } - } - if !vm.Providers[aws.ProviderName].Active() { - refreshDNS = false - } - // DNS entries are maintained in the GCE DNS registry for all vms, from all - // clouds. - if refreshDNS { - if !quiet { - fmt.Println("Refreshing DNS entries...") - } - if err := gce.SyncDNS(vms); err != nil { - fmt.Fprintf(os.Stderr, "failed to update %s DNS: %v", gce.Subdomain, err) - } - } else { - if !quiet { - fmt.Println("Not refreshing DNS entries. We did not have all the VMs.") - } - } - - if err := vm.ProvidersSequential(vm.AllProviderNames(), func(p vm.Provider) error { - return p.CleanSSH() - }); err != nil { - return nil, err - } - - _ = rootCmd.GenBashCompletionFile(bashCompletion) - - if err := vm.ProvidersSequential(vm.AllProviderNames(), func(p vm.Provider) error { - return p.ConfigSSH() - }); err != nil { - return nil, err - } - return cloud, nil -} - var gcCmd = &cobra.Command{ Use: "gc", Short: "GC expired clusters and unused AWS keypairs\n", @@ -931,13 +401,7 @@ hourly by a cronjob so it is not necessary to run manually. `, Args: cobra.NoArgs, Run: wrap(func(cmd *cobra.Command, args []string) error { - cloud, err := cld.ListCloud() - if err == nil { - // GCClusters depends on ListCloud so only call it if ListCloud runs without errors - err = cld.GCClusters(cloud, dryrun) - } - otherErr := cld.GCAWSKeyPairs(dryrun) - return errors.CombineErrors(err, otherErr) + return roachprod.GC(dryrun, config.SlackToken) }), } @@ -951,38 +415,12 @@ destroyed: `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - clusterName, err := verifyClusterName(args[0]) - if err != nil { - return err - } - - cloud, err := cld.ListCloud() - if err != nil { - return err - } - - c, ok := cloud.Clusters[clusterName] - if !ok { - return fmt.Errorf("cluster %s does not exist", clusterName) + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - - if err := cld.ExtendCluster(c, extendLifetime); err != nil { - return err - } - - // Reload the clusters and print details. - cloud, err = cld.ListCloud() - if err != nil { - return err - } - - c, ok = cloud.Clusters[clusterName] - if !ok { - return fmt.Errorf("cluster %s does not exist", clusterName) - } - - c.PrintDetails() - return nil + return roachprod.Extend(clusterOpts, extendLifetime) }), } @@ -1026,12 +464,12 @@ cluster setting will be set to its value. `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.Start() - return nil + return roachprod.Start(clusterOpts, startOpts) }), } @@ -1057,16 +495,16 @@ other signals. `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } wait := waitFlag if sig == 9 /* SIGKILL */ && !cmd.Flags().Changed("wait") { wait = true } - c.Stop(sig, wait) - return nil + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, + } + return roachprod.Stop(clusterOpts, sig, wait) }), } @@ -1081,17 +519,12 @@ default cluster settings. It's intended to be used in conjunction with `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - clusterName, err := verifyClusterName(args[0]) - if err != nil { - return err - } - - c, err := newCluster(clusterName) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.Init() - return nil + return roachprod.Init(clusterOpts, username) }), } @@ -1111,12 +544,12 @@ The "status" command outputs the binary and PID for the specified nodes: `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.Status() - return nil + return roachprod.Status(clusterOpts) }), } @@ -1131,17 +564,22 @@ into a single stream. `, Args: cobra.RangeArgs(1, 2), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, + } + logsOpts := roachprod.LogsOpts{ + Dir: logsDir, Filter: logsFilter, ProgramFilter: logsProgramFilter, + Interval: logsInterval, From: logsFrom, To: logsTo, Out: cmd.OutOrStdout(), } var dest string if len(args) == 2 { dest = args[1] } else { - dest = c.Name + ".logs" + dest = clusterOpts.Name + ".logs" } - return c.Logs(logsDir, dest, username, logsFilter, logsProgramFilter, logsInterval, logsFrom, logsTo, cmd.OutOrStdout()) + return roachprod.Logs(logsOpts, clusterOpts, dest, username) }), } @@ -1164,21 +602,12 @@ of nodes, outputting a line whenever a change is detected: `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - for msg := range c.Monitor(monitorIgnoreEmptyNodes, monitorOneShot) { - if msg.Err != nil { - msg.Msg += "error: " + msg.Err.Error() - } - thisError := errors.Newf("%d: %s", msg.Index, msg.Msg) - if msg.Err != nil || strings.Contains(msg.Msg, "dead") { - err = errors.CombineErrors(err, thisError) - } - fmt.Println(thisError.Error()) + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return err + return roachprod.Monitor(clusterOpts, monitorIgnoreEmptyNodes, monitorOneShot) }), } @@ -1193,12 +622,12 @@ nodes. `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.Wipe(wipePreserveCerts) - return nil + return roachprod.Wipe(clusterOpts, wipePreserveCerts) }), } @@ -1228,39 +657,12 @@ the 'zfs rollback' command: Args: cobra.ExactArgs(2), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - - var fsCmd string - switch fs := args[1]; fs { - case vm.Zfs: - if err := install.Install(c, []string{vm.Zfs}); err != nil { - return err - } - fsCmd = `sudo zpool create -f data1 -m /mnt/data1 /dev/sdb` - case vm.Ext4: - fsCmd = `sudo mkfs.ext4 -F /dev/sdb && sudo mount -o defaults /dev/sdb /mnt/data1` - default: - return fmt.Errorf("unknown filesystem %q", fs) - } - - err = c.Run(os.Stdout, os.Stderr, c.Nodes, "reformatting", fmt.Sprintf(` -set -euo pipefail -if sudo zpool list -Ho name 2>/dev/null | grep ^data1$; then - sudo zpool destroy -f data1 -fi -if mountpoint -q /mnt/data1; then - sudo umount -f /mnt/data1 -fi -%s -sudo chmod 777 /mnt/data1 -`, fsCmd)) - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return nil + return roachprod.Reformat(clusterOpts, args[1]) }), } @@ -1272,23 +674,12 @@ var runCmd = &cobra.Command{ `, Args: cobra.MinimumNArgs(1), Run: wrap(func(_ *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - - // Use "ssh" if an interactive session was requested (i.e. there is no - // remote command to run). - if len(args) == 1 { - return c.SSH(strings.Split(extraSSHOptions, " "), args[1:]) - } - - cmd := strings.TrimSpace(strings.Join(args[1:], " ")) - title := cmd - if len(title) > 30 { - title = title[:27] + "..." + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return c.Run(os.Stdout, os.Stderr, c.Nodes, title, cmd) + return roachprod.Run(clusterOpts, extraSSHOptions, args[1:]) }), } @@ -1299,32 +690,12 @@ var resetCmd = &cobra.Command{ environments and will fall back to a no-op.`, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) (retErr error) { - if numNodes <= 0 || numNodes >= 1000 { - // Upper limit is just for safety. - return fmt.Errorf("number of nodes must be in [1..999]") + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - - clusterName, err := verifyClusterName(args[0]) - if err != nil { - return err - } - - if clusterName == config.Local { - return nil - } - - cloud, err := cld.ListCloud() - if err != nil { - return err - } - c, ok := cloud.Clusters[clusterName] - if !ok { - return errors.New("cluster not found") - } - - return vm.FanOut(c.VMs, func(p vm.Provider, vms vm.List) error { - return p.Reset(vms) - }) + return roachprod.Reset(clusterOpts, numNodes, username) }), } @@ -1337,11 +708,12 @@ var installCmd = &cobra.Command{ `, Args: cobra.MinimumNArgs(2), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return install.Install(c, args[1:]) + return roachprod.Install(clusterOpts, args[1:]) }), } @@ -1351,16 +723,17 @@ var downloadCmd = &cobra.Command{ Long: "Downloads 3rd party tools, using a GCS cache if possible.", Args: cobra.RangeArgs(3, 4), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } src, sha := args[1], args[2] var dest string if len(args) == 4 { dest = args[3] } - return install.Download(c, src, sha, dest) + return roachprod.Download(clusterOpts, src, sha, dest) }), } @@ -1378,18 +751,11 @@ Currently available application options are: `, Args: cobra.RangeArgs(1, 2), Run: wrap(func(cmd *cobra.Command, args []string) error { - applicationName := args[0] versionArg := "" if len(args) == 2 { versionArg = args[1] } - - os := runtime.GOOS - if stageOS != "" { - os = stageOS - } - - urls, err := install.URLsForApplication(applicationName, versionArg, os) + urls, err := roachprod.StageURL(args[0], versionArg, stageOS) if err != nil { return err } @@ -1424,29 +790,16 @@ Some examples of usage: `, Args: cobra.RangeArgs(2, 3), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - - os := "linux" - if stageOS != "" { - os = stageOS - } else if c.IsLocal() { - os = runtime.GOOS + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - - dir := "." - if stageDir != "" { - dir = stageDir - } - - applicationName := args[1] versionArg := "" if len(args) == 3 { versionArg = args[2] } - return install.StageApplication(c, applicationName, versionArg, os, dir) + return roachprod.Stage(clusterOpts, stageOS, stageDir, args[1], versionArg) }), } @@ -1460,12 +813,12 @@ start." `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.DistributeCerts() - return nil + return roachprod.DistributeCerts(clusterOpts) }), } @@ -1481,12 +834,12 @@ var putCmd = &cobra.Command{ if len(args) == 3 { dest = args[2] } - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.Put(src, dest) - return nil + return roachprod.Put(clusterOpts, src, dest) }), } @@ -1503,12 +856,12 @@ multiple nodes the destination file name will be prefixed with the node number. if len(args) == 3 { dest = args[2] } - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - c.Get(src, dest) - return nil + return roachprod.Get(clusterOpts, src, dest) }), } @@ -1518,15 +871,12 @@ var sqlCmd = &cobra.Command{ Long: "Run `cockroach sql` on a remote cluster.\n", Args: cobra.MinimumNArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - cockroach, ok := c.Impl.(install.Cockroach) - if !ok { - return errors.New("sql is only valid on cockroach clusters") + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - return cockroach.SQL(c, args[1:]) + return roachprod.SQL(clusterOpts, sqlCockroachBinary, args[1:]) }), } @@ -1537,37 +887,12 @@ var pgurlCmd = &cobra.Command{ `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - nodes := c.ServerNodes() - ips := make([]string, len(nodes)) - - if external { - for i := 0; i < len(nodes); i++ { - ips[i] = c.VMs[nodes[i]-1] - } - } else { - c.Parallel("", len(nodes), 0, func(i int) ([]byte, error) { - var err error - ips[i], err = c.GetInternalIP(nodes[i]) - return nil, err - }) + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - - var urls []string - for i, ip := range ips { - if ip == "" { - return errors.Errorf("empty ip: %v", ips) - } - urls = append(urls, c.Impl.NodeURL(c, ip, c.Impl.NodePort(c, nodes[i]))) - } - fmt.Println(strings.Join(urls, " ")) - if len(urls) != len(nodes) { - return errors.Errorf("have nodes %v, but urls %v from ips %v", nodes, urls, ips) - } - return nil + return roachprod.PgURL(clusterOpts, external) }), } @@ -1597,121 +922,15 @@ Examples: roachprod pprof-heap CLUSTERNAME:1 `, Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err - } - - var profType string - var description string - if cmd.CalledAs() == "pprof-heap" || pprofOptions.heap { - description = "capturing heap profile" - profType = "heap" - } else { - description = "capturing CPU profile" - profType = "profile" + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - - outputFiles := []string{} - mu := &syncutil.Mutex{} - pprofPath := fmt.Sprintf("debug/pprof/%s?seconds=%d", profType, int(pprofOptions.duration.Seconds())) - - minTimeout := 30 * time.Second - timeout := 2 * pprofOptions.duration - if timeout < minTimeout { - timeout = minTimeout + if cmd.CalledAs() == "pprof-heap" { + pprofOptions.heap = true } - - httpClient := httputil.NewClientWithTimeout(timeout) - startTime := timeutil.Now().Unix() - failed, err := c.ParallelE(description, len(c.ServerNodes()), 0, func(i int) ([]byte, error) { - host := c.VMs[i] - port := install.GetAdminUIPort(c.Impl.NodePort(c, i)) - scheme := "http" - if c.Secure { - scheme = "https" - } - outputFile := fmt.Sprintf("pprof-%s-%d-%s-%04d.out", profType, startTime, c.Name, i+1) - outputDir := filepath.Dir(outputFile) - file, err := ioutil.TempFile(outputDir, ".pprof") - if err != nil { - return nil, errors.Wrap(err, "create tmpfile for pprof download") - } - - defer func() { - err := file.Close() - if err != nil && !errors.Is(err, oserror.ErrClosed) { - fmt.Fprintf(os.Stderr, "warning: could not close temporary file") - } - err = os.Remove(file.Name()) - if err != nil && !oserror.IsNotExist(err) { - fmt.Fprintf(os.Stderr, "warning: could not remove temporary file") - } - }() - - pprofURL := fmt.Sprintf("%s://%s:%d/%s", scheme, host, port, pprofPath) - resp, err := httpClient.Get(context.Background(), pprofURL) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, errors.Newf("unexpected status from pprof endpoint: %s", resp.Status) - } - - if _, err := io.Copy(file, resp.Body); err != nil { - return nil, err - } - if err := file.Sync(); err != nil { - return nil, err - } - if err := file.Close(); err != nil { - return nil, err - } - if err := os.Rename(file.Name(), outputFile); err != nil { - return nil, err - } - - mu.Lock() - outputFiles = append(outputFiles, outputFile) - mu.Unlock() - return nil, nil - }) - - for _, s := range outputFiles { - fmt.Printf("Created %s\n", s) - } - - if err != nil { - sort.Slice(failed, func(i, j int) bool { return failed[i].Index < failed[j].Index }) - for _, f := range failed { - fmt.Fprintf(os.Stderr, "%d: %+v: %s\n", f.Index, f.Err, f.Out) - } - os.Exit(1) - } - - if pprofOptions.open { - waitCommands := []*exec.Cmd{} - for i, file := range outputFiles { - port := pprofOptions.startingPort + i - cmd := exec.Command("go", "tool", "pprof", - "-http", fmt.Sprintf(":%d", port), - file) - waitCommands = append(waitCommands, cmd) - if err := cmd.Start(); err != nil { - return err - } - } - - for _, cmd := range waitCommands { - err := cmd.Wait() - if err != nil { - return err - } - } - } - return nil + return roachprod.Pprof(clusterOpts, pprofOptions.duration, pprofOptions.heap, pprofOptions.open, pprofOptions.startingPort) }), } @@ -1723,43 +942,12 @@ var adminurlCmd = &cobra.Command{ `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) - if err != nil { - return err + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, } - - for i, node := range c.ServerNodes() { - host := vm.Name(c.Name, node) + "." + gce.Subdomain - - // verify DNS is working / fallback to IPs if not. - if i == 0 && !adminurlIPs { - if _, err := net.LookupHost(host); err != nil { - fmt.Fprintf(os.Stderr, "no valid DNS (yet?). might need to re-run `sync`?\n") - adminurlIPs = true - } - } - - if adminurlIPs { - host = c.VMs[node-1] - } - port := install.GetAdminUIPort(c.Impl.NodePort(c, node)) - scheme := "http" - if c.Secure { - scheme = "https" - } - if !strings.HasPrefix(adminurlPath, "/") { - adminurlPath = "/" + adminurlPath - } - url := fmt.Sprintf("%s://%s:%d%s", scheme, host, port, adminurlPath) - if adminurlOpen { - if err := exec.Command("python", "-m", "webbrowser", url).Run(); err != nil { - return err - } - } else { - fmt.Println(url) - } - } - return nil + return roachprod.AdminURL(clusterOpts, adminurlIPs, adminurlOpen, adminurlPath) }), } @@ -1770,26 +958,15 @@ var ipCmd = &cobra.Command{ `, Args: cobra.ExactArgs(1), Run: wrap(func(cmd *cobra.Command, args []string) error { - c, err := newCluster(args[0]) + clusterOpts := install.SyncedCluster{ + Name: args[0], Tag: tag, CertsDir: certsDir, Secure: secure, + Quiet: quiet, UseTreeDist: useTreeDist, Args: nodeArgs, + Env: nodeEnv, NumRacks: numRacks, MaxConcurrency: maxConcurrency, + } + ips, err := roachprod.IP(clusterOpts, external) if err != nil { return err } - - nodes := c.ServerNodes() - ips := make([]string, len(nodes)) - - if external { - for i := 0; i < len(nodes); i++ { - ips[i] = c.VMs[nodes[i]-1] - } - } else { - c.Parallel("", len(nodes), 0, func(i int) ([]byte, error) { - var err error - ips[i], err = c.GetInternalIP(nodes[i]) - return nil, err - }) - } - for _, ip := range ips { fmt.Println(ip) } @@ -1801,8 +978,7 @@ var versionCmd = &cobra.Command{ Use: `version`, Short: `print version information`, RunE: func(cmd *cobra.Command, args []string) error { - info := build.GetInfo() - fmt.Println(info.Long()) + fmt.Println(roachprod.Version()) return nil }, } @@ -1939,6 +1115,8 @@ func main() { "json", false, "Show cluster specs in a json format") listCmd.Flags().BoolVarP(&listMine, "mine", "m", false, "Show only clusters belonging to the current user") + listCmd.Flags().StringVar(&listPattern, + "pattern", "", "Show only clusters matching the regex pattern. Empty string matches everything.") adminurlCmd.Flags().BoolVar( &adminurlOpen, `open`, false, `Open the url in a browser`) @@ -2038,22 +1216,22 @@ func main() { switch cmd { case startCmd: cmd.Flags().BoolVar( - &install.StartOpts.Sequential, "sequential", true, + &startOpts.Sequential, "sequential", true, "start nodes sequentially so node IDs match hostnames") cmd.Flags().StringArrayVarP( &nodeArgs, "args", "a", nil, "node arguments") cmd.Flags().StringArrayVarP( &nodeEnv, "env", "e", nodeEnv, "node environment variables") cmd.Flags().BoolVar( - &install.StartOpts.Encrypt, "encrypt", encrypt, "start nodes with encryption at rest turned on") + &startOpts.Encrypt, "encrypt", encrypt, "start nodes with encryption at rest turned on") cmd.Flags().BoolVar( - &install.StartOpts.SkipInit, "skip-init", skipInit, "skip initializing the cluster") + &startOpts.SkipInit, "skip-init", skipInit, "skip initializing the cluster") cmd.Flags().IntVar( - &install.StartOpts.StoreCount, "store-count", 1, "number of stores to start each node with") + &startOpts.StoreCount, "store-count", 1, "number of stores to start each node with") fallthrough case sqlCmd: cmd.Flags().StringVarP( - &config.Binary, "binary", "b", config.Binary, + &sqlCockroachBinary, "binary", "b", "cockroach", "the remote cockroach binary to use") fallthrough case pgurlCmd, adminurlCmd: @@ -2091,12 +1269,12 @@ Node specification os.Exit(1) } - if err := initDirs(); err != nil { + if err := roachprod.InitDirs(); err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(1) } - if err := loadClusters(); err != nil { + if err := roachprod.LoadClusters(); err != nil { // We don't want to exit as we may be looking at the help message. fmt.Printf("problem loading clusters: %s\n", err) } diff --git a/pkg/cmd/roachprod/vm/azure/flags.go b/pkg/cmd/roachprod/vm/azure/flags.go deleted file mode 100644 index f55ddf0d38da..000000000000 --- a/pkg/cmd/roachprod/vm/azure/flags.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package azure - -import ( - "fmt" - "strings" - "time" - - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" - "github.com/spf13/pflag" -) - -type providerOpts struct { - locations []string - machineType string - operationTimeout time.Duration - syncDelete bool - vnetName string - zone string - networkDiskType string - networkDiskSize int32 - ultraDiskIOPS int64 - diskCaching string -} - -var defaultLocations = []string{ - "eastus2", - "westus", - "westeurope", -} - -var defaultZone = "1" - -// ConfigureCreateFlags implements vm.ProviderFlags. -func (o *providerOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { - flags.DurationVar(&o.operationTimeout, ProviderName+"-timeout", 10*time.Minute, - "The maximum amount of time for an Azure API operation to take") - flags.BoolVar(&o.syncDelete, ProviderName+"-sync-delete", false, - "Wait for deletions to finish before returning") - flags.StringVar(&o.machineType, ProviderName+"-machine-type", - string(compute.VirtualMachineSizeTypesStandardD4V3), - "Machine type (see https://azure.microsoft.com/en-us/pricing/details/virtual-machines/linux/)") - flags.StringSliceVar(&o.locations, ProviderName+"-locations", nil, - fmt.Sprintf("Locations for cluster (see `az account list-locations`) (default\n[%s])", - strings.Join(defaultLocations, ","))) - flags.StringVar(&o.vnetName, ProviderName+"-vnet-name", "common", - "The name of the VNet to use") - flags.StringVar(&o.zone, ProviderName+"-availability-zone", "", "Availability Zone to create VMs in") - flags.StringVar(&o.networkDiskType, ProviderName+"-network-disk-type", "premium-disk", - "type of network disk [premium-disk, ultra-disk]. only used if local-ssd is false") - flags.Int32Var(&o.networkDiskSize, ProviderName+"-volume-size", 500, - "Size in GB of network disk volume, only used if local-ssd=false") - flags.Int64Var(&o.ultraDiskIOPS, ProviderName+"-ultra-disk-iops", 5000, - "Number of IOPS provisioned for ultra disk, only used if network-disk-type=ultra-disk") - flags.StringVar(&o.diskCaching, ProviderName+"-disk-caching", "none", - "Disk caching behavior for attached storage. Valid values are: none, read-only, read-write. Not applicable to Ultra disks.") -} - -// ConfigureClusterFlags implements vm.ProviderFlags and is a no-op. -func (o *providerOpts) ConfigureClusterFlags(*pflag.FlagSet, vm.MultipleProjectsOption) { -} diff --git a/pkg/roachprod/BUILD.bazel b/pkg/roachprod/BUILD.bazel new file mode 100644 index 000000000000..14f493c58dfe --- /dev/null +++ b/pkg/roachprod/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "roachprod", + srcs = [ + "hosts.go", + "roachprod.go", + ], + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod", + visibility = ["//visibility:public"], + deps = [ + "//pkg/build", + "//pkg/cli/exit", + "//pkg/roachprod/cloud", + "//pkg/roachprod/config", + "//pkg/roachprod/install", + "//pkg/roachprod/vm", + "//pkg/roachprod/vm/aws", + "//pkg/roachprod/vm/azure", + "//pkg/roachprod/vm/gce", + "//pkg/roachprod/vm/local", + "//pkg/util/ctxgroup", + "//pkg/util/httputil", + "//pkg/util/log", + "//pkg/util/syncutil", + "//pkg/util/timeutil", + "@com_github_cockroachdb_errors//:errors", + "@com_github_cockroachdb_errors//oserror", + "@org_golang_x_sys//unix", + "@org_golang_x_term//:term", + ], +) diff --git a/pkg/cmd/roachprod/cloud/BUILD.bazel b/pkg/roachprod/cloud/BUILD.bazel similarity index 85% rename from pkg/cmd/roachprod/cloud/BUILD.bazel rename to pkg/roachprod/cloud/BUILD.bazel index ef723d09f023..f4223ad2e518 100644 --- a/pkg/cmd/roachprod/cloud/BUILD.bazel +++ b/pkg/roachprod/cloud/BUILD.bazel @@ -7,11 +7,12 @@ go_library( "gc.go", "gc_aws_keypairs.go", ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/cloud", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/cloud", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/config", - "//pkg/cmd/roachprod/vm", + "//pkg/roachprod/config", + "//pkg/roachprod/vm", + "//pkg/util/log", "//pkg/util/timeutil", "@com_github_aws_aws_sdk_go_v2_config//:config", "@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2", diff --git a/pkg/cmd/roachprod/cloud/cloud_test.go b/pkg/roachprod/cloud/cloud_test.go similarity index 100% rename from pkg/cmd/roachprod/cloud/cloud_test.go rename to pkg/roachprod/cloud/cloud_test.go diff --git a/pkg/cmd/roachprod/cloud/cluster_cloud.go b/pkg/roachprod/cloud/cluster_cloud.go similarity index 98% rename from pkg/cmd/roachprod/cloud/cluster_cloud.go rename to pkg/roachprod/cloud/cluster_cloud.go index cbbf6a11b91c..527fdc0f45fc 100644 --- a/pkg/cmd/roachprod/cloud/cluster_cloud.go +++ b/pkg/roachprod/cloud/cluster_cloud.go @@ -17,8 +17,8 @@ import ( "strings" "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" "github.com/cockroachdb/errors" ) diff --git a/pkg/cmd/roachprod/cloud/gc.go b/pkg/roachprod/cloud/gc.go similarity index 94% rename from pkg/cmd/roachprod/cloud/gc.go rename to pkg/roachprod/cloud/gc.go index dc7357e90c80..a4e2d453a821 100644 --- a/pkg/cmd/roachprod/cloud/gc.go +++ b/pkg/roachprod/cloud/gc.go @@ -11,11 +11,11 @@ package cloud import ( + "context" "encoding/base64" "fmt" "hash/fnv" "io/ioutil" - "log" "os" "path/filepath" "sort" @@ -23,8 +23,9 @@ import ( "text/tabwriter" "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" "github.com/cockroachdb/errors/oserror" @@ -145,7 +146,7 @@ func postStatus(client *slack.Client, channel string, dryrun bool, s *status, ba if len(badVMs) == 0 { send, err := shouldSend(channel, s) if err != nil { - log.Printf("unable to deduplicate notification: %s", err) + log.Infof(context.Background(), "unable to deduplicate notification: %s", err) } if !send { return @@ -224,12 +225,12 @@ func postStatus(client *slack.Client, channel string, dryrun bool, s *status, ba _, _, err := client.PostMessage(channel, "", params) if err != nil { - log.Println(err) + log.Infof(context.Background(), "%v", err) } } func postError(client *slack.Client, channel string, err error) { - log.Println(err) + log.Infof(context.Background(), "%v", err) if client == nil || channel == "" { return } @@ -241,7 +242,7 @@ func postError(client *slack.Client, channel string, err error) { } _, _, err = client.PostMessage(channel, fmt.Sprintf("`%s`", err), params) if err != nil { - log.Println(err) + log.Infof(context.Background(), "%v", err) } } @@ -317,7 +318,7 @@ func GCClusters(cloud *Cloud, dryrun bool) error { if err == nil { postStatus(client, userChannel, dryrun, status, nil) } else if !errors.Is(err, errNoSlackClient) { - log.Printf("could not deliver Slack DM to %s: %v", user+config.EmailDomain, err) + log.Infof(context.Background(), "could not deliver Slack DM to %s: %v", user+config.EmailDomain, err) } } } diff --git a/pkg/cmd/roachprod/cloud/gc_aws_keypairs.go b/pkg/roachprod/cloud/gc_aws_keypairs.go similarity index 93% rename from pkg/cmd/roachprod/cloud/gc_aws_keypairs.go rename to pkg/roachprod/cloud/gc_aws_keypairs.go index 28ee67b9001a..6bdeeee44d4e 100644 --- a/pkg/cmd/roachprod/cloud/gc_aws_keypairs.go +++ b/pkg/roachprod/cloud/gc_aws_keypairs.go @@ -12,7 +12,6 @@ package cloud import ( "context" - "log" "time" "github.com/aws/aws-sdk-go-v2/config" @@ -20,6 +19,7 @@ import ( ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/iam" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" ) @@ -188,13 +188,13 @@ func tagKeyPairIfUntagged( if IAMUserNameTag == "" { IAMUserNameKey := "IAMUserName" tags = append(tags, ec2types.Tag{Key: &IAMUserNameKey, Value: &IAMUserName}) - log.Printf("Tagging %s with IAMUserName: %s\n", *keyPair.KeyName, IAMUserName) + log.Infof(context.Background(), "Tagging %s with IAMUserName: %s\n", *keyPair.KeyName, IAMUserName) } createdAtValue := timestamp.Format(time.RFC3339) if createdAtTag == "" { createdAtKey := "CreatedAt" tags = append(tags, ec2types.Tag{Key: &createdAtKey, Value: &createdAtValue}) - log.Printf("Tagging %s with CreatedAt: %s\n", *keyPair.KeyName, createdAtValue) + log.Infof(context.Background(), "Tagging %s with CreatedAt: %s\n", *keyPair.KeyName, createdAtValue) } else { createdAtValue = createdAtTag } @@ -250,7 +250,7 @@ func GCAWSKeyPairs(dryrun bool) error { return err } for _, region := range regions { - log.Println(*region.RegionName) + log.Infof(context.Background(), "%s", *region.RegionName) EC2Client, err := getEC2Client(*region.RegionName) if err != nil { return err @@ -279,7 +279,7 @@ func GCAWSKeyPairs(dryrun bool) error { } // 10 days = 240 hours if timestamp.Sub(createdAtTimestamp).Hours() >= 240 { - log.Printf("Deleting %s because it is a teamcity-runner key created at %s.\n", + log.Infof(context.Background(), "Deleting %s because it is a teamcity-runner key created at %s.\n", *keyPair.KeyName, createdAtTimestamp) if !dryrun { err := deleteKeyPair(EC2Client, *keyPair.KeyName) @@ -293,7 +293,7 @@ func GCAWSKeyPairs(dryrun bool) error { // Delete key if user has console access without MFA". if usersWithConsoleAccess[IAMUserName] && !usersWithMFAEnabled[IAMUserName] { - log.Printf("Deleting %s because %s has console access but MFA disabled.\n", *keyPair.KeyName, IAMUserName) + log.Infof(context.Background(), "Deleting %s because %s has console access but MFA disabled.\n", *keyPair.KeyName, IAMUserName) if !dryrun { err := deleteKeyPair(EC2Client, *keyPair.KeyName) if err != nil { @@ -302,7 +302,7 @@ func GCAWSKeyPairs(dryrun bool) error { } // Delete key if user doesn't have an active access key. } else if !usersWithActiveAccessKey[IAMUserName] { - log.Printf("Deleting %s because %s does not have an active access key.\n", + log.Infof(context.Background(), "Deleting %s because %s does not have an active access key.\n", *keyPair.KeyName, IAMUserName) if !dryrun { err := deleteKeyPair(EC2Client, *keyPair.KeyName) diff --git a/pkg/cmd/roachprod/config/BUILD.bazel b/pkg/roachprod/config/BUILD.bazel similarity index 59% rename from pkg/cmd/roachprod/config/BUILD.bazel rename to pkg/roachprod/config/BUILD.bazel index f45cd09d24c7..b1103d103fda 100644 --- a/pkg/cmd/roachprod/config/BUILD.bazel +++ b/pkg/roachprod/config/BUILD.bazel @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "config", srcs = ["config.go"], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/config", visibility = ["//visibility:public"], + deps = ["//pkg/util/log"], ) diff --git a/pkg/cmd/roachprod/config/config.go b/pkg/roachprod/config/config.go similarity index 90% rename from pkg/cmd/roachprod/config/config.go rename to pkg/roachprod/config/config.go index f2b698614aa2..cbb24f60f5f0 100644 --- a/pkg/cmd/roachprod/config/config.go +++ b/pkg/roachprod/config/config.go @@ -11,8 +11,10 @@ package config import ( - "log" + "context" "os/user" + + "github.com/cockroachdb/cockroach/pkg/util/log" ) var ( @@ -28,7 +30,7 @@ func init() { var err error OSUser, err = user.Current() if err != nil { - log.Panic("Unable to determine OS user", err) + log.Fatalf(context.Background(), "Unable to determine OS user: %v", err) } } diff --git a/pkg/cmd/roachprod/errors/BUILD.bazel b/pkg/roachprod/errors/BUILD.bazel similarity index 73% rename from pkg/cmd/roachprod/errors/BUILD.bazel rename to pkg/roachprod/errors/BUILD.bazel index 5f3a2f004a63..9f65cb3d247e 100644 --- a/pkg/cmd/roachprod/errors/BUILD.bazel +++ b/pkg/roachprod/errors/BUILD.bazel @@ -3,7 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "errors", srcs = ["errors.go"], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/errors", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/errors", visibility = ["//visibility:public"], deps = ["@com_github_cockroachdb_errors//:errors"], ) diff --git a/pkg/cmd/roachprod/errors/errors.go b/pkg/roachprod/errors/errors.go similarity index 100% rename from pkg/cmd/roachprod/errors/errors.go rename to pkg/roachprod/errors/errors.go diff --git a/pkg/cmd/roachprod/hosts.go b/pkg/roachprod/hosts.go similarity index 91% rename from pkg/cmd/roachprod/hosts.go rename to pkg/roachprod/hosts.go index b0cf73323611..f5774dc5c9d1 100644 --- a/pkg/cmd/roachprod/hosts.go +++ b/pkg/roachprod/hosts.go @@ -8,25 +8,27 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -package main +package roachprod import ( + "context" "fmt" "io/ioutil" - "log" "os" "path" "path/filepath" "strings" "text/tabwriter" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/cloud" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/install" + "github.com/cockroachdb/cockroach/pkg/roachprod/cloud" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/install" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" ) -func initDirs() error { +// InitDirs TODO +func InitDirs() error { hd := os.ExpandEnv(config.DefaultHostDir) if err := os.MkdirAll(hd, 0755); err != nil { return err @@ -97,7 +99,7 @@ func gcHostsFiles(cloud *cloud.Cloud) error { filename := filepath.Join(hd, file.Name()) if err = os.Remove(filename); err != nil { - log.Printf("failed to remove file %s", filename) + log.Infof(context.Background(), "failed to remove file %s", filename) } } return nil @@ -107,7 +109,8 @@ func newInvalidHostsLineErr(line string) error { return fmt.Errorf("invalid hosts line, expected @ [locality] [vpcId], got %q", line) } -func loadClusters() error { +// LoadClusters TODO +func LoadClusters() error { hd := os.ExpandEnv(config.DefaultHostDir) files, err := ioutil.ReadDir(hd) if err != nil { diff --git a/pkg/cmd/roachprod/install/BUILD.bazel b/pkg/roachprod/install/BUILD.bazel similarity index 82% rename from pkg/cmd/roachprod/install/BUILD.bazel rename to pkg/roachprod/install/BUILD.bazel index e96f843b99ca..111cf2835d5a 100644 --- a/pkg/cmd/roachprod/install/BUILD.bazel +++ b/pkg/roachprod/install/BUILD.bazel @@ -17,13 +17,14 @@ go_library( "scripts/download.sh", "scripts/start.sh", ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/install", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/install", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/config", - "//pkg/cmd/roachprod/errors", - "//pkg/cmd/roachprod/ssh", - "//pkg/cmd/roachprod/ui", + "//pkg/cli/exit", + "//pkg/roachprod/config", + "//pkg/roachprod/errors", + "//pkg/roachprod/ssh", + "//pkg/roachprod/ui", "//pkg/util/envutil", "//pkg/util/httputil", "//pkg/util/log", diff --git a/pkg/cmd/roachprod/install/cluster_synced.go b/pkg/roachprod/install/cluster_synced.go similarity index 97% rename from pkg/cmd/roachprod/install/cluster_synced.go rename to pkg/roachprod/install/cluster_synced.go index a260097a927e..fe9a77017183 100644 --- a/pkg/cmd/roachprod/install/cluster_synced.go +++ b/pkg/roachprod/install/cluster_synced.go @@ -17,7 +17,6 @@ import ( "fmt" "io" "io/ioutil" - "log" "math" "os" "os/exec" @@ -30,11 +29,12 @@ import ( "text/template" "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - rperrors "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/errors" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ssh" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ui" - clog "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/cli/exit" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + rperrors "github.com/cockroachdb/cockroach/pkg/roachprod/errors" + "github.com/cockroachdb/cockroach/pkg/roachprod/ssh" + "github.com/cockroachdb/cockroach/pkg/roachprod/ui" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/syncutil" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/cockroach/pkg/util/version" @@ -63,6 +63,7 @@ type SyncedCluster struct { Name string VMs []string Users []string + NumRacks int Localities []string VPCs []string // all other fields are populated in newCluster. @@ -907,7 +908,7 @@ tar cvf certs.tar certs if msg != "" { fmt.Fprintln(os.Stderr, msg) - os.Exit(1) + exit.WithCode(exit.UnspecifiedError()) } var tmpfileName string @@ -918,7 +919,7 @@ tar cvf certs.tar certs tmpfile, err := ioutil.TempFile("", "certs") if err != nil { fmt.Fprintln(os.Stderr, err) - os.Exit(1) + exit.WithCode(exit.UnspecifiedError()) } _ = tmpfile.Close() defer func() { @@ -929,7 +930,7 @@ tar cvf certs.tar certs return c.scp(fmt.Sprintf("%s@%s:certs.tar", c.user(1), c.host(1)), tmpfile.Name()) }(); err != nil { fmt.Fprintln(os.Stderr, err) - os.Exit(1) + exit.WithCode(exit.UnspecifiedError()) } tmpfileName = tmpfile.Name() @@ -940,7 +941,7 @@ tar cvf certs.tar certs certsTar, err := ioutil.ReadFile(tmpfileName) if err != nil { fmt.Fprintln(os.Stderr, err) - os.Exit(1) + exit.WithCode(exit.UnspecifiedError()) } // Skip the first node which is where we generated the certs. @@ -1193,7 +1194,7 @@ func (c *SyncedCluster) Put(src, dest string) { } if haveErr { - log.Fatalf("put %s failed", src) + log.Fatalf(context.Background(), "put %s failed", src) } } @@ -1293,7 +1294,7 @@ func (c *SyncedCluster) Logs( // host information is useless. if c.IsLocal() { cmd.Args = append(cmd.Args, - "--file-pattern", "^(?:.*/)?(?P[0-9]+).*/"+clog.FileNamePattern+"$", + "--file-pattern", "^(?:.*/)?(?P[0-9]+).*/"+log.FileNamePattern+"$", "--prefix", "${id}> ") } cmd.Stdout = out @@ -1549,7 +1550,7 @@ func (c *SyncedCluster) Get(src, dest string) { } if haveErr { - log.Fatalf("get %s failed", src) + log.Fatalf(context.Background(), "get %s failed", src) } } @@ -1674,7 +1675,7 @@ func (c *SyncedCluster) Parallel( for _, f := range failed { fmt.Fprintf(os.Stderr, "%d: %+v: %s\n", f.Index, f.Err, f.Out) } - log.Fatal("command failed") + log.Fatal(context.Background(), "command failed") } } @@ -1806,17 +1807,17 @@ func (c *SyncedCluster) Init() { vers, err := getCockroachVersion(c, c.ServerNodes()[firstNodeIdx]) if err != nil { - log.Fatalf("unable to retrieve cockroach version: %v", err) + log.Fatalf(context.Background(), "unable to retrieve cockroach version: %v", err) } if !vers.AtLeast(version.MustParse("v20.1.0")) { - log.Fatal("`roachprod init` only supported for v20.1 and beyond") + log.Fatal(context.Background(), "`roachprod init` only supported for v20.1 and beyond") } fmt.Printf("%s: initializing cluster\n", h.c.Name) initOut, err := h.initializeCluster(firstNodeIdx) if err != nil { - log.Fatalf("unable to initialize cluster: %v", err) + log.Fatalf(context.Background(), "unable to initialize cluster: %v", err) } if initOut != "" { fmt.Println(initOut) @@ -1825,7 +1826,7 @@ func (c *SyncedCluster) Init() { fmt.Printf("%s: setting cluster settings\n", h.c.Name) clusterSettingsOut, err := h.setClusterSettings(firstNodeIdx) if err != nil { - log.Fatalf("unable to set cluster settings: %v", err) + log.Fatalf(context.Background(), "unable to set cluster settings: %v", err) } if clusterSettingsOut != "" { fmt.Println(clusterSettingsOut) diff --git a/pkg/cmd/roachprod/install/cockroach.go b/pkg/roachprod/install/cockroach.go similarity index 97% rename from pkg/cmd/roachprod/install/cockroach.go rename to pkg/roachprod/install/cockroach.go index 8e3ab1aa0ab9..ffca7ff61003 100644 --- a/pkg/cmd/roachprod/install/cockroach.go +++ b/pkg/roachprod/install/cockroach.go @@ -11,9 +11,9 @@ package install import ( + "context" _ "embed" // required for go:embed "fmt" - "log" "net/url" "os" "os/exec" @@ -24,9 +24,10 @@ import ( "text/template" "github.com/alessio/shellescape" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ssh" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/ssh" "github.com/cockroachdb/cockroach/pkg/util/envutil" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/version" "github.com/cockroachdb/errors" ) @@ -34,14 +35,17 @@ import ( //go:embed scripts/start.sh var startScript string -// StartOpts TODO(peter): document -var StartOpts struct { +// StartOptsType houses the options needed by Start() +type StartOptsType struct { Encrypt bool Sequential bool SkipInit bool StoreCount int } +// StartOpts is exported to be updated before calling Start() +var StartOpts StartOptsType + // Cockroach TODO(peter): document type Cockroach struct{} @@ -192,7 +196,7 @@ func (r Cockroach) Start(c *SyncedCluster, extraArgs []string) { fmt.Printf("%s: initializing cluster\n", h.c.Name) initOut, err := h.initializeCluster(nodeIdx) if err != nil { - log.Fatalf("unable to initialize cluster: %v", err) + log.Fatalf(context.Background(), "unable to initialize cluster: %v", err) } if initOut != "" { @@ -213,7 +217,7 @@ func (r Cockroach) Start(c *SyncedCluster, extraArgs []string) { markBootstrap := fmt.Sprintf("touch %s/%s", h.c.Impl.NodeDir(h.c, nodes[nodeIdx], 1 /* storeIndex */), "cluster-bootstrapped") cmdOut, err := h.run(nodeIdx, markBootstrap) if err != nil { - log.Fatalf("unable to run cmd: %v", err) + log.Fatalf(context.Background(), "unable to run cmd: %v", err) } if cmdOut != "" { fmt.Println(cmdOut) @@ -226,7 +230,7 @@ func (r Cockroach) Start(c *SyncedCluster, extraArgs []string) { fmt.Printf("%s: setting cluster settings\n", h.c.Name) clusterSettingsOut, err := h.setClusterSettings(nodeIdx) if err != nil { - log.Fatalf("unable to set cluster settings: %v", err) + log.Fatalf(context.Background(), "unable to set cluster settings: %v", err) } if clusterSettingsOut != "" { fmt.Println(clusterSettingsOut) diff --git a/pkg/cmd/roachprod/install/download.go b/pkg/roachprod/install/download.go similarity index 100% rename from pkg/cmd/roachprod/install/download.go rename to pkg/roachprod/install/download.go diff --git a/pkg/cmd/roachprod/install/expander.go b/pkg/roachprod/install/expander.go similarity index 100% rename from pkg/cmd/roachprod/install/expander.go rename to pkg/roachprod/install/expander.go diff --git a/pkg/cmd/roachprod/install/install.go b/pkg/roachprod/install/install.go similarity index 100% rename from pkg/cmd/roachprod/install/install.go rename to pkg/roachprod/install/install.go diff --git a/pkg/cmd/roachprod/install/iterm2.go b/pkg/roachprod/install/iterm2.go similarity index 100% rename from pkg/cmd/roachprod/install/iterm2.go rename to pkg/roachprod/install/iterm2.go diff --git a/pkg/cmd/roachprod/install/nodes.go b/pkg/roachprod/install/nodes.go similarity index 100% rename from pkg/cmd/roachprod/install/nodes.go rename to pkg/roachprod/install/nodes.go diff --git a/pkg/cmd/roachprod/install/scripts/download.sh b/pkg/roachprod/install/scripts/download.sh similarity index 100% rename from pkg/cmd/roachprod/install/scripts/download.sh rename to pkg/roachprod/install/scripts/download.sh diff --git a/pkg/cmd/roachprod/install/scripts/start.sh b/pkg/roachprod/install/scripts/start.sh similarity index 100% rename from pkg/cmd/roachprod/install/scripts/start.sh rename to pkg/roachprod/install/scripts/start.sh diff --git a/pkg/cmd/roachprod/install/scripts/start_test.sh b/pkg/roachprod/install/scripts/start_test.sh similarity index 100% rename from pkg/cmd/roachprod/install/scripts/start_test.sh rename to pkg/roachprod/install/scripts/start_test.sh diff --git a/pkg/cmd/roachprod/install/session.go b/pkg/roachprod/install/session.go similarity index 98% rename from pkg/cmd/roachprod/install/session.go rename to pkg/roachprod/install/session.go index 1840aa2bbd47..fdc2491b85de 100644 --- a/pkg/cmd/roachprod/install/session.go +++ b/pkg/roachprod/install/session.go @@ -18,7 +18,7 @@ import ( "path/filepath" "sync" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" "github.com/cockroachdb/errors" ) diff --git a/pkg/cmd/roachprod/install/staging.go b/pkg/roachprod/install/staging.go similarity index 100% rename from pkg/cmd/roachprod/install/staging.go rename to pkg/roachprod/install/staging.go diff --git a/pkg/cmd/roachprod/install/start_template_test.go b/pkg/roachprod/install/start_template_test.go similarity index 100% rename from pkg/cmd/roachprod/install/start_template_test.go rename to pkg/roachprod/install/start_template_test.go diff --git a/pkg/cmd/roachprod/install/testdata/start/start.txt b/pkg/roachprod/install/testdata/start/start.txt similarity index 100% rename from pkg/cmd/roachprod/install/testdata/start/start.txt rename to pkg/roachprod/install/testdata/start/start.txt diff --git a/pkg/cmd/roachprod/k8s/roachprod-gc.yaml b/pkg/roachprod/k8s/roachprod-gc.yaml similarity index 100% rename from pkg/cmd/roachprod/k8s/roachprod-gc.yaml rename to pkg/roachprod/k8s/roachprod-gc.yaml diff --git a/pkg/roachprod/roachprod.go b/pkg/roachprod/roachprod.go new file mode 100644 index 000000000000..836fff681eda --- /dev/null +++ b/pkg/roachprod/roachprod.go @@ -0,0 +1,1208 @@ +// Copyright 2018 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package roachprod + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "sort" + "strings" + "time" + + "github.com/cockroachdb/cockroach/pkg/build" + "github.com/cockroachdb/cockroach/pkg/cli/exit" + cld "github.com/cockroachdb/cockroach/pkg/roachprod/cloud" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/install" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/aws" + // azure registers its provider with the top-level vm package. + _ "github.com/cockroachdb/cockroach/pkg/roachprod/vm/azure" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/gce" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/local" + "github.com/cockroachdb/cockroach/pkg/util/ctxgroup" + "github.com/cockroachdb/cockroach/pkg/util/httputil" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" + "github.com/cockroachdb/errors/oserror" + "golang.org/x/sys/unix" + "golang.org/x/term" +) + +// verifyClusterName ensures that the given name conforms to +// our naming pattern of "-". The +// username must match one of the vm.Provider account names +// or the --username override. +func verifyClusterName(clusterName, username string) (string, error) { + if len(clusterName) == 0 { + return "", fmt.Errorf("cluster name cannot be blank") + } + if clusterName == config.Local { + return clusterName, nil + } + + alphaNum, err := regexp.Compile(`^[a-zA-Z0-9\-]+$`) + if err != nil { + return "", err + } + if !alphaNum.MatchString(clusterName) { + return "", errors.Errorf("cluster name must match %s", alphaNum.String()) + } + + // Use the vm.Provider account names, or --username. + var accounts []string + if len(username) > 0 { + accounts = []string{username} + } else { + seenAccounts := map[string]bool{} + active, err := vm.FindActiveAccounts() + if err != nil { + return "", err + } + for _, account := range active { + if !seenAccounts[account] { + seenAccounts[account] = true + cleanAccount := vm.DNSSafeAccount(account) + if cleanAccount != account { + log.Infof(context.TODO(), "WARN: using `%s' as username instead of `%s'", cleanAccount, account) + } + accounts = append(accounts, cleanAccount) + } + } + } + + // If we see -, accept it. + for _, account := range accounts { + if strings.HasPrefix(clusterName, account+"-") && len(clusterName) > len(account)+1 { + return clusterName, nil + } + } + + // Try to pick out a reasonable cluster name from the input. + i := strings.Index(clusterName, "-") + suffix := clusterName + if i != -1 { + // The user specified a username prefix, but it didn't match an active + // account name. For example, assuming the account is "peter", `roachprod + // create joe-perf` should be specified as `roachprod create joe-perf -u + // joe`. + suffix = clusterName[i+1:] + } else { + // The user didn't specify a username prefix. For example, assuming the + // account is "peter", `roachprod create perf` should be specified as + // `roachprod create peter-perf`. + _ = 0 + } + + // Suggest acceptable cluster names. + var suggestions []string + for _, account := range accounts { + suggestions = append(suggestions, fmt.Sprintf("%s-%s", account, suffix)) + } + return "", fmt.Errorf("malformed cluster name %s, did you mean one of %s", + clusterName, suggestions) +} + +// DefaultSyncedCluster returns install.SyncedCluster with default values. +//lint:ignore U1001 unused +func DefaultSyncedCluster() install.SyncedCluster { + return install.SyncedCluster{ + Name: "", + Tag: "", + CertsDir: "./certs", + Secure: false, + Quiet: false, + UseTreeDist: true, + Args: nil, + Env: []string{ + "COCKROACH_ENABLE_RPC_COMPRESSION=false", + "COCKROACH_UI_RELEASE_NOTES_SIGNUP_DISMISSED=true", + }, + NumRacks: 0, + MaxConcurrency: 32, + } +} + +func sortedClusters() []string { + var r []string + for n := range install.Clusters { + r = append(r, n) + } + sort.Strings(r) + return r +} + +func newCluster(opts install.SyncedCluster) (*install.SyncedCluster, error) { + nodeNames := "all" + { + parts := strings.Split(opts.Name, ":") + switch len(parts) { + case 2: + nodeNames = parts[1] + fallthrough + case 1: + opts.Name = parts[0] + case 0: + return nil, fmt.Errorf("no cluster specified") + default: + return nil, fmt.Errorf("invalid cluster name: %s", opts.Name) + } + } + + c, ok := install.Clusters[opts.Name] + if !ok { + err := errors.Newf(`unknown cluster: %s`, opts.Name) + err = errors.WithHintf(err, ` +Available clusters: + %s +`, strings.Join(sortedClusters(), "\n ")) + err = errors.WithHint(err, `Use "roachprod sync" to update the list of available clusters.`) + return nil, err + } + + c.Impl = install.Cockroach{} + c.NumRacks = opts.NumRacks + if c.NumRacks > 0 { + for i := range c.Localities { + rack := fmt.Sprintf("rack=%d", i%opts.NumRacks) + if c.Localities[i] != "" { + rack = "," + rack + } + c.Localities[i] += rack + } + } + + nodes, err := install.ListNodes(nodeNames, len(c.VMs)) + if err != nil { + return nil, err + } + for _, n := range nodes { + if n > len(c.VMs) { + return nil, fmt.Errorf("invalid node spec %s, cluster contains %d nodes", + nodeNames, len(c.VMs)) + } + } + c.Nodes = nodes + c.Secure = opts.Secure + c.CertsDir = opts.CertsDir + c.Env = opts.Env + c.Args = opts.Args + if opts.Tag != "" { + c.Tag = "/" + opts.Tag + } + c.UseTreeDist = opts.UseTreeDist + c.Quiet = opts.Quiet || !term.IsTerminal(int(os.Stdout.Fd())) + c.MaxConcurrency = opts.MaxConcurrency + return c, nil +} + +// userClusterNameRegexp returns a regexp that matches all clusters owned by the +// current user. +func userClusterNameRegexp() (*regexp.Regexp, error) { + // In general, we expect that users will have the same + // account name across the services they're using, + // but we still want to function even if this is not + // the case. + seenAccounts := map[string]bool{} + accounts, err := vm.FindActiveAccounts() + if err != nil { + return nil, err + } + pattern := "" + for _, account := range accounts { + if !seenAccounts[account] { + seenAccounts[account] = true + if len(pattern) > 0 { + pattern += "|" + } + pattern += fmt.Sprintf("(^%s-)", regexp.QuoteMeta(account)) + } + } + return regexp.Compile(pattern) +} + +// Version returns version/build information. +func Version() string { + info := build.GetInfo() + return info.Long() +} + +// CachedHosts returns a list of all roachprod clsuters from local cache. +func CachedHosts(cachedHostsCluster string) ([]string, error) { + if err := LoadClusters(); err != nil { + return nil, err + } + + names := make([]string, 0, len(install.Clusters)) + for name := range install.Clusters { + names = append(names, name) + } + sort.Strings(names) + + var retLines []string + for _, name := range names { + c := install.Clusters[name] + newLine := c.Name + // when invokved by bash-completion, cachedHostsCluster is what the user + // has currently typed -- if this cluster matches that, expand its hosts. + if strings.HasPrefix(cachedHostsCluster, c.Name) { + for i := range c.VMs { + newLine += fmt.Sprintf(" %s:%d", c.Name, i+1) + } + } + retLines = append(retLines, newLine) + + } + return retLines, nil +} + +// Sync grabs an exclusive lock on the roachprod state and then proceeds to +// read the current state from the cloud and write it out to disk. The locking +// protects both the reading and the writing in order to prevent the hazard +// caused by concurrent goroutines reading cloud state in a different order +// than writing it to disk. +func Sync(quiet bool) (*cld.Cloud, error) { + lockFile := os.ExpandEnv("$HOME/.roachprod/LOCK") + if !quiet { + fmt.Println("Syncing...") + } + // Acquire a filesystem lock so that two concurrent synchronizations of + // roachprod state don't clobber each other. + f, err := os.Create(lockFile) + if err != nil { + return nil, errors.Wrapf(err, "creating lock file %q", lockFile) + } + if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { + return nil, errors.Wrap(err, "acquiring lock on %q") + } + defer f.Close() + cloud, err := cld.ListCloud() + if err != nil { + return nil, err + } + if err := syncHosts(cloud); err != nil { + return nil, err + } + + var vms vm.List + for _, c := range cloud.Clusters { + vms = append(vms, c.VMs...) + } + + // Figure out if we're going to overwrite the DNS entries. We don't want to + // overwrite if we don't have all the VMs of interest, so we only do it if we + // have a list of all VMs from both AWS and GCE (so if both providers have + // been used to get the VMs and for GCP also if we listed the VMs in the + // default project). + refreshDNS := true + + if p := vm.Providers[gce.ProviderName]; !p.Active() { + refreshDNS = false + } else { + var defaultProjectFound bool + for _, prj := range p.(*gce.Provider).GetProjects() { + if prj == gce.DefaultProject() { + defaultProjectFound = true + break + } + } + if !defaultProjectFound { + refreshDNS = false + } + } + if !vm.Providers[aws.ProviderName].Active() { + refreshDNS = false + } + // DNS entries are maintained in the GCE DNS registry for all vms, from all + // clouds. + if refreshDNS { + if !quiet { + fmt.Println("Refreshing DNS entries...") + } + if err := gce.SyncDNS(vms); err != nil { + fmt.Fprintf(os.Stderr, "failed to update %s DNS: %v", gce.Subdomain, err) + } + } else { + if !quiet { + fmt.Println("Not refreshing DNS entries. We did not have all the VMs.") + } + } + + if err := vm.ProvidersSequential(vm.AllProviderNames(), func(p vm.Provider) error { + return p.CleanSSH() + }); err != nil { + return nil, err + } + + if err := vm.ProvidersSequential(vm.AllProviderNames(), func(p vm.Provider) error { + return p.ConfigSSH() + }); err != nil { + return nil, err + } + + err = os.Remove(lockFile) + if err != nil { + return nil, err + } + + return cloud, nil +} + +// List returns a cloud.Cloud struct of all roachprod clusters matching clusterNamePattern. +// Alternatively, the 'listMine' option can be provided to get the clusters that are owned +// by the current user. +func List(quiet, listMine bool, clusterNamePattern string) (cld.Cloud, error) { + listPattern := regexp.MustCompile(".*") + if clusterNamePattern == "" { + if listMine { + var err error + listPattern, err = userClusterNameRegexp() + if err != nil { + return cld.Cloud{}, err + } + } + } else { + if listMine { + return cld.Cloud{}, errors.New("'mine' option cannot be combined with 'pattern'") + } + var err error + listPattern, err = regexp.Compile(clusterNamePattern) + if err != nil { + return cld.Cloud{}, errors.Wrapf(err, "could not compile regex pattern: %s", clusterNamePattern) + } + } + + cloud, err := Sync(quiet) + if err != nil { + return cld.Cloud{}, err + } + + filteredCloud := cloud.Clone() + for name := range cloud.Clusters { + if !listPattern.MatchString(name) { + delete(filteredCloud.Clusters, name) + } + } + return *filteredCloud, nil +} + +// Run runs a command on the nodes in a cluster. +func Run(clusterOpts install.SyncedCluster, SSHOptions string, cmdArray []string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + + // Use "ssh" if an interactive session was requested (i.e. there is no + // remote command to run). + if len(cmdArray) == 0 { + return c.SSH(strings.Split(SSHOptions, " "), cmdArray) + } + + cmd := strings.TrimSpace(strings.Join(cmdArray, " ")) + title := cmd + if len(title) > 30 { + title = title[:27] + "..." + } + return c.Run(os.Stdout, os.Stderr, c.Nodes, title, cmd) +} + +// SQL runs `cockroach sql` on a remote cluster. +func SQL(clusterOpts install.SyncedCluster, remoteCockroachBinary string, cmdArray []string) error { + config.Binary = remoteCockroachBinary + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + cockroach, ok := c.Impl.(install.Cockroach) + if !ok { + return errors.New("sql is only valid on cockroach clusters") + } + return cockroach.SQL(c, cmdArray) +} + +// IP gets the ip addresses of the nodes in a cluster. +func IP(clusterOpts install.SyncedCluster, external bool) ([]string, error) { + c, err := newCluster(clusterOpts) + if err != nil { + return nil, err + } + + nodes := c.ServerNodes() + ips := make([]string, len(nodes)) + + if external { + for i := 0; i < len(nodes); i++ { + ips[i] = c.VMs[nodes[i]-1] + } + } else { + c.Parallel("", len(nodes), 0, func(i int) ([]byte, error) { + var err error + ips[i], err = c.GetInternalIP(nodes[i]) + return nil, err + }) + } + + return ips, nil +} + +// Status retrieves the status of nodes in a cluster. +func Status(clusterOpts install.SyncedCluster) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Status() + return nil +} + +// Stage stages release and edge binaries to the cluster. +// stageOS, stageDir, version can be "" to use default values +func Stage( + clusterOpts install.SyncedCluster, stageOS, stageDir, applicationName, version string, +) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + + os := "linux" + if stageOS != "" { + os = stageOS + } else if c.IsLocal() { + os = runtime.GOOS + } + + dir := "." + if stageDir != "" { + dir = stageDir + } + + return install.StageApplication(c, applicationName, version, os, dir) +} + +// Reset resets all VMs in a cluster. +func Reset(clusterOpts install.SyncedCluster, numNodes int, username string) error { + if numNodes <= 0 || numNodes >= 1000 { + // Upper limit is just for safety. + return fmt.Errorf("number of nodes must be in [1..999]") + } + + clusterName, err := verifyClusterName(clusterOpts.Name, username) + if err != nil { + return err + } + + if clusterName == config.Local { + return nil + } + + cloud, err := cld.ListCloud() + if err != nil { + return err + } + c, ok := cloud.Clusters[clusterName] + if !ok { + return errors.New("cluster not found") + } + + return vm.FanOut(c.VMs, func(p vm.Provider, vms vm.List) error { + return p.Reset(vms) + }) +} + +// SetupSSH sets up the keys and host keys for the vms in the cluster. +func SetupSSH(clusterOpts install.SyncedCluster, username string) error { + clusterName, err := verifyClusterName(clusterOpts.Name, username) + if err != nil { + return err + } + cloud, err := Sync(clusterOpts.Quiet) + if err != nil { + return err + } + cloudCluster, ok := cloud.Clusters[clusterName] + if !ok { + return fmt.Errorf("could not find %s in list of cluster", clusterName) + } + cloudCluster.PrintDetails() + // Run ssh-keygen -R serially on each new VM in case an IP address has been recycled + for _, v := range cloudCluster.VMs { + cmd := exec.Command("ssh-keygen", "-R", v.PublicIP) + out, err := cmd.CombinedOutput() + if err != nil { + log.Infof(context.TODO(), "could not clear ssh key for hostname %s:\n%s", v.PublicIP, string(out)) + } + + } + + // Wait for the nodes in the cluster to start. + install.Clusters = map[string]*install.SyncedCluster{} + if err := LoadClusters(); err != nil { + return err + } + installCluster, err := newCluster(clusterOpts) + if err != nil { + return err + } + // For GCP clusters we need to use the config.OSUser even if the client + // requested the shared user. + for i := range installCluster.VMs { + if cloudCluster.VMs[i].Provider == gce.ProviderName { + installCluster.Users[i] = config.OSUser.Username + } + } + if err := installCluster.Wait(); err != nil { + return err + } + // Fetch public keys from gcloud to set up ssh access for all users into the + // shared ubuntu user. + installCluster.AuthorizedKeys, err = gce.GetUserAuthorizedKeys() + if err != nil { + return errors.Wrap(err, "failed to retrieve authorized keys from gcloud") + } + return installCluster.SetupSSH() +} + +// Extend extends the lifetime of the specified cluster to prevent it from being destroyed. +func Extend(clusterOpts install.SyncedCluster, lifetime time.Duration) error { + cloud, err := cld.ListCloud() + if err != nil { + return err + } + + c, ok := cloud.Clusters[clusterOpts.Name] + if !ok { + return fmt.Errorf("cluster %s does not exist", clusterOpts.Name) + } + + if err := cld.ExtendCluster(c, lifetime); err != nil { + return err + } + + // Reload the clusters and print details. + cloud, err = cld.ListCloud() + if err != nil { + return err + } + + c, ok = cloud.Clusters[clusterOpts.Name] + if !ok { + return fmt.Errorf("cluster %s does not exist", clusterOpts.Name) + } + + c.PrintDetails() + return nil +} + +// Start starts nodes on a cluster. +func Start(clusterOpts install.SyncedCluster, startOpts install.StartOptsType) error { + install.StartOpts = startOpts + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Start() + return nil +} + +// Monitor monitors the status of cockroach nodes in a cluster. +func Monitor( + clusterOpts install.SyncedCluster, monitorIgnoreEmptyNodes, monitorOneShot bool, +) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + for msg := range c.Monitor(monitorIgnoreEmptyNodes, monitorOneShot) { + if msg.Err != nil { + msg.Msg += "error: " + msg.Err.Error() + } + thisError := errors.Newf("%d: %s", msg.Index, msg.Msg) + if msg.Err != nil || strings.Contains(msg.Msg, "dead") { + err = errors.CombineErrors(err, thisError) + } + fmt.Println(thisError.Error()) + } + return err +} + +// Stop starts nodes on a cluster. +func Stop(clusterOpts install.SyncedCluster, sig int, wait bool) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Stop(sig, wait) + return nil +} + +// Init initializes the cluster. +func Init(clusterOpts install.SyncedCluster, username string) error { + _, err := verifyClusterName(clusterOpts.Name, username) + if err != nil { + return err + } + + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Init() + return nil +} + +// Wipe wipes the nodes in a cluster. +func Wipe(clusterOpts install.SyncedCluster, wipePreserveCerts bool) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Wipe(wipePreserveCerts) + return nil +} + +// Reformat reformats disks in a cluster to use the specified filesystem. +func Reformat(clusterOpts install.SyncedCluster, fs string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + + var fsCmd string + switch fs { + case vm.Zfs: + if err := install.Install(c, []string{vm.Zfs}); err != nil { + return err + } + fsCmd = `sudo zpool create -f data1 -m /mnt/data1 /dev/sdb` + case vm.Ext4: + fsCmd = `sudo mkfs.ext4 -F /dev/sdb && sudo mount -o defaults /dev/sdb /mnt/data1` + default: + return fmt.Errorf("unknown filesystem %q", fs) + } + + err = c.Run(os.Stdout, os.Stderr, c.Nodes, "reformatting", fmt.Sprintf(` +set -euo pipefail +if sudo zpool list -Ho name 2>/dev/null | grep ^data1$; then +sudo zpool destroy -f data1 +fi +if mountpoint -q /mnt/data1; then +sudo umount -f /mnt/data1 +fi +%s +sudo chmod 777 /mnt/data1 +`, fsCmd)) + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + } + return nil +} + +// Install installs third party software. +func Install(clusterOpts install.SyncedCluster, software []string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + return install.Install(c, software) +} + +// Download downloads 3rd party tools, using a GCS cache if possible. +func Download(clusterOpts install.SyncedCluster, src, sha, dest string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + return install.Download(c, src, sha, dest) +} + +// DistributeCerts distributes certificates to the nodes in a cluster. +// If the certificates already exist, no action is taken. +func DistributeCerts(clusterOpts install.SyncedCluster) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.DistributeCerts() + return nil +} + +// Put copies a local file to the nodes in a cluster. +func Put(clusterOpts install.SyncedCluster, src, dest string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Put(src, dest) + return nil +} + +// Get copies a remote file from the nodes in a cluster. +// If the file is retrieved from multiple nodes the destination +// file name will be prefixed with the node number. +func Get(clusterOpts install.SyncedCluster, src, dest string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Get(src, dest) + return nil +} + +// PgURL generates pgurls for the nodes in a cluster. +func PgURL(clusterOpts install.SyncedCluster, external bool) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + nodes := c.ServerNodes() + ips := make([]string, len(nodes)) + + if external { + for i := 0; i < len(nodes); i++ { + ips[i] = c.VMs[nodes[i]-1] + } + } else { + c.Parallel("", len(nodes), 0, func(i int) ([]byte, error) { + var err error + ips[i], err = c.GetInternalIP(nodes[i]) + return nil, err + }) + } + + var urls []string + for i, ip := range ips { + if ip == "" { + return errors.Errorf("empty ip: %v", ips) + } + urls = append(urls, c.Impl.NodeURL(c, ip, c.Impl.NodePort(c, nodes[i]))) + } + fmt.Println(strings.Join(urls, " ")) + if len(urls) != len(nodes) { + return errors.Errorf("have nodes %v, but urls %v from ips %v", nodes, urls, ips) + } + return nil +} + +// AdminURL generates admin UI URLs for the nodes in a cluster. +func AdminURL( + clusterOpts install.SyncedCluster, adminurlIPs, adminurlOpen bool, adminurlPath string, +) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + + for i, node := range c.ServerNodes() { + host := vm.Name(c.Name, node) + "." + gce.Subdomain + + // verify DNS is working / fallback to IPs if not. + if i == 0 && !adminurlIPs { + if _, err := net.LookupHost(host); err != nil { + fmt.Fprintf(os.Stderr, "no valid DNS (yet?). might need to re-run `sync`?\n") + adminurlIPs = true + } + } + + if adminurlIPs { + host = c.VMs[node-1] + } + port := install.GetAdminUIPort(c.Impl.NodePort(c, node)) + scheme := "http" + if c.Secure { + scheme = "https" + } + if !strings.HasPrefix(adminurlPath, "/") { + adminurlPath = "/" + adminurlPath + } + url := fmt.Sprintf("%s://%s:%d%s", scheme, host, port, adminurlPath) + if adminurlOpen { + if err := exec.Command("python", "-m", "webbrowser", url).Run(); err != nil { + return err + } + } else { + fmt.Println(url) + } + } + return nil +} + +// Pprof TODO +func Pprof( + clusterOpts install.SyncedCluster, duration time.Duration, heap, open bool, startingPort int, +) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + + var profType string + var description string + if heap { + description = "capturing heap profile" + profType = "heap" + } else { + description = "capturing CPU profile" + profType = "profile" + } + + outputFiles := []string{} + mu := &syncutil.Mutex{} + pprofPath := fmt.Sprintf("debug/pprof/%s?seconds=%d", profType, int(duration.Seconds())) + + minTimeout := 30 * time.Second + timeout := 2 * duration + if timeout < minTimeout { + timeout = minTimeout + } + + httpClient := httputil.NewClientWithTimeout(timeout) + startTime := timeutil.Now().Unix() + failed, err := c.ParallelE(description, len(c.ServerNodes()), 0, func(i int) ([]byte, error) { + host := c.VMs[i] + port := install.GetAdminUIPort(c.Impl.NodePort(c, i)) + scheme := "http" + if c.Secure { + scheme = "https" + } + outputFile := fmt.Sprintf("pprof-%s-%d-%s-%04d.out", profType, startTime, c.Name, i+1) + outputDir := filepath.Dir(outputFile) + file, err := ioutil.TempFile(outputDir, ".pprof") + if err != nil { + return nil, errors.Wrap(err, "create tmpfile for pprof download") + } + + defer func() { + err := file.Close() + if err != nil && !errors.Is(err, oserror.ErrClosed) { + fmt.Fprintf(os.Stderr, "warning: could not close temporary file") + } + err = os.Remove(file.Name()) + if err != nil && !oserror.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "warning: could not remove temporary file") + } + }() + + pprofURL := fmt.Sprintf("%s://%s:%d/%s", scheme, host, port, pprofPath) + resp, err := httpClient.Get(context.Background(), pprofURL) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.Newf("unexpected status from pprof endpoint: %s", resp.Status) + } + + if _, err := io.Copy(file, resp.Body); err != nil { + return nil, err + } + if err := file.Sync(); err != nil { + return nil, err + } + if err := file.Close(); err != nil { + return nil, err + } + if err := os.Rename(file.Name(), outputFile); err != nil { + return nil, err + } + + mu.Lock() + outputFiles = append(outputFiles, outputFile) + mu.Unlock() + return nil, nil + }) + + for _, s := range outputFiles { + fmt.Printf("Created %s\n", s) + } + + if err != nil { + sort.Slice(failed, func(i, j int) bool { return failed[i].Index < failed[j].Index }) + for _, f := range failed { + fmt.Fprintf(os.Stderr, "%d: %+v: %s\n", f.Index, f.Err, f.Out) + } + exit.WithCode(exit.UnspecifiedError()) + } + + if open { + waitCommands := []*exec.Cmd{} + for i, file := range outputFiles { + port := startingPort + i + cmd := exec.Command("go", "tool", "pprof", + "-http", fmt.Sprintf(":%d", port), + file) + waitCommands = append(waitCommands, cmd) + if err := cmd.Start(); err != nil { + return err + } + } + + for _, cmd := range waitCommands { + err := cmd.Wait() + if err != nil { + return err + } + } + } + return nil +} + +// Destroy TODO +func Destroy(clusters []install.SyncedCluster, destroyAllMine bool, username string) error { + type cloudAndName struct { + name string + cloud *cld.Cloud + } + var cns []cloudAndName + switch len(clusters) { + case 0: + if !destroyAllMine { + return errors.New("no cluster name provided") + } + + destroyPattern, err := userClusterNameRegexp() + if err != nil { + return err + } + + cloud, err := cld.ListCloud() + if err != nil { + return err + } + + for name := range cloud.Clusters { + if destroyPattern.MatchString(name) { + cns = append(cns, cloudAndName{name: name, cloud: cloud}) + } + } + + default: + if destroyAllMine { + return errors.New("--all-mine cannot be combined with cluster names") + } + + var cloud *cld.Cloud + for _, cluster := range clusters { + clusterName, err := verifyClusterName(cluster.Name, username) + if err != nil { + return err + } + + if clusterName != config.Local { + if cloud == nil { + cloud, err = cld.ListCloud() + if err != nil { + return err + } + } + + cns = append(cns, cloudAndName{name: clusterName, cloud: cloud}) + } else { + if err := destroyLocalCluster(cluster); err != nil { + return err + } + } + } + } + + if err := ctxgroup.GroupWorkers(context.TODO(), len(cns), func(ctx context.Context, idx int) error { + return destroyCluster(cns[idx].cloud, cns[idx].name) + }); err != nil { + return err + } + fmt.Println("OK") + return nil +} + +func destroyCluster(cloud *cld.Cloud, clusterName string) error { + c, ok := cloud.Clusters[clusterName] + if !ok { + return fmt.Errorf("cluster %s does not exist", clusterName) + } + fmt.Printf("Destroying cluster %s with %d nodes\n", clusterName, len(c.VMs)) + return cld.DestroyCluster(c) +} + +func destroyLocalCluster(clusterOpts install.SyncedCluster) error { + if _, ok := install.Clusters[config.Local]; !ok { + return fmt.Errorf("cluster %s does not exist", config.Local) + } + clusterOpts.Name = config.Local + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + c.Wipe(false) + for _, i := range c.Nodes { + err := os.RemoveAll(fmt.Sprintf(os.ExpandEnv("${HOME}/local/%d"), i)) + if err != nil { + return err + } + } + return os.Remove(filepath.Join(os.ExpandEnv(config.DefaultHostDir), c.Name)) +} + +type clusterAlreadyExistsError struct { + name string +} + +func (e *clusterAlreadyExistsError) Error() string { + return fmt.Sprintf("cluster %s already exists", e.name) +} + +func newClusterAlreadyExistsError(name string) error { + return &clusterAlreadyExistsError{name: name} +} + +func cleanupFailedCreate(clusterName string) error { + cloud, err := cld.ListCloud() + if err != nil { + return err + } + c, ok := cloud.Clusters[clusterName] + if !ok { + // If the cluster doesn't exist, we didn't manage to create any VMs + // before failing. Not an error. + return nil + } + return cld.DestroyCluster(c) +} + +// Create TODO +func Create( + numNodes int, username string, createVMOpts vm.CreateOpts, clusterOpts install.SyncedCluster, +) (retErr error) { + if numNodes <= 0 || numNodes >= 1000 { + // Upper limit is just for safety. + return fmt.Errorf("number of nodes must be in [1..999]") + } + + clusterName, err := verifyClusterName(clusterOpts.Name, username) + if err != nil { + return err + } + createVMOpts.ClusterName = clusterName + + defer func() { + if retErr == nil || clusterName == config.Local { + return + } + if errors.HasType(retErr, (*clusterAlreadyExistsError)(nil)) { + return + } + fmt.Fprintf(os.Stderr, "Cleaning up partially-created cluster (prev err: %s)\n", retErr) + if err := cleanupFailedCreate(clusterName); err != nil { + fmt.Fprintf(os.Stderr, "Error while cleaning up partially-created cluster: %s\n", err) + } else { + fmt.Fprintf(os.Stderr, "Cleaning up OK\n") + } + }() + + if clusterName != config.Local { + cloud, err := cld.ListCloud() + if err != nil { + return err + } + if _, ok := cloud.Clusters[clusterName]; ok { + return newClusterAlreadyExistsError(clusterName) + } + } else { + if _, ok := install.Clusters[clusterName]; ok { + return newClusterAlreadyExistsError(clusterName) + } + + // If the local cluster is being created, force the local Provider to be used + createVMOpts.VMProviders = []string{local.ProviderName} + } + + if createVMOpts.SSDOpts.FileSystem == vm.Zfs { + for _, provider := range createVMOpts.VMProviders { + if provider != gce.ProviderName { + return fmt.Errorf( + "creating a node with --filesystem=zfs is currently only supported on gce", + ) + } + } + } + + fmt.Printf("Creating cluster %s with %d nodes\n", clusterName, numNodes) + if createErr := cld.CreateCluster(numNodes, createVMOpts); createErr != nil { + return createErr + } + + // Just create directories for the local cluster as there's no need for ssh. + if clusterName == config.Local { + for i := 0; i < numNodes; i++ { + err := os.MkdirAll(fmt.Sprintf(os.ExpandEnv("${HOME}/local/%d"), i+1), 0755) + if err != nil { + return err + } + } + return nil + } + return SetupSSH(clusterOpts, username) +} + +// GC garbage-collects expired clusters and unused SSH keypairs in AWS. +func GC(dryrun bool, slackToken string) error { + config.SlackToken = slackToken + cloud, err := cld.ListCloud() + if err == nil { + // GCClusters depends on ListCloud so only call it if ListCloud runs without errors + err = cld.GCClusters(cloud, dryrun) + } + otherErr := cld.GCAWSKeyPairs(dryrun) + return errors.CombineErrors(err, otherErr) +} + +// LogsOpts TODO +type LogsOpts struct { + Dir, Filter, ProgramFilter string + Interval time.Duration + From, To time.Time + Out io.Writer +} + +// Logs TODO +func Logs(logsOpts LogsOpts, clusterOpts install.SyncedCluster, dest, username string) error { + c, err := newCluster(clusterOpts) + if err != nil { + return err + } + return c.Logs(logsOpts.Dir, dest, username, logsOpts.Filter, logsOpts.ProgramFilter, logsOpts.Interval, logsOpts.From, logsOpts.To, logsOpts.Out) +} + +// StageURL TODO +func StageURL(applicationName, version, stageOS string) ([]*url.URL, error) { + os := runtime.GOOS + if stageOS != "" { + os = stageOS + } + urls, err := install.URLsForApplication(applicationName, version, os) + if err != nil { + return nil, err + } + return urls, nil +} diff --git a/pkg/cmd/roachprod/ssh/BUILD.bazel b/pkg/roachprod/ssh/BUILD.bazel similarity index 78% rename from pkg/cmd/roachprod/ssh/BUILD.bazel rename to pkg/roachprod/ssh/BUILD.bazel index 937134bf678c..98211d85038e 100644 --- a/pkg/cmd/roachprod/ssh/BUILD.bazel +++ b/pkg/roachprod/ssh/BUILD.bazel @@ -6,7 +6,7 @@ go_library( "io.go", "shell.go", ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ssh", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/ssh", visibility = ["//visibility:public"], ) diff --git a/pkg/cmd/roachprod/ssh/io.go b/pkg/roachprod/ssh/io.go similarity index 100% rename from pkg/cmd/roachprod/ssh/io.go rename to pkg/roachprod/ssh/io.go diff --git a/pkg/cmd/roachprod/ssh/io_test.go b/pkg/roachprod/ssh/io_test.go similarity index 100% rename from pkg/cmd/roachprod/ssh/io_test.go rename to pkg/roachprod/ssh/io_test.go diff --git a/pkg/cmd/roachprod/ssh/shell.go b/pkg/roachprod/ssh/shell.go similarity index 100% rename from pkg/cmd/roachprod/ssh/shell.go rename to pkg/roachprod/ssh/shell.go diff --git a/pkg/cmd/roachprod/ui/BUILD.bazel b/pkg/roachprod/ui/BUILD.bazel similarity index 72% rename from pkg/cmd/roachprod/ui/BUILD.bazel rename to pkg/roachprod/ui/BUILD.bazel index 525433dc0da8..a6d169a88a4d 100644 --- a/pkg/cmd/roachprod/ui/BUILD.bazel +++ b/pkg/roachprod/ui/BUILD.bazel @@ -6,6 +6,6 @@ go_library( "collate_errors.go", "writer.go", ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/ui", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/ui", visibility = ["//visibility:public"], ) diff --git a/pkg/cmd/roachprod/ui/collate_errors.go b/pkg/roachprod/ui/collate_errors.go similarity index 100% rename from pkg/cmd/roachprod/ui/collate_errors.go rename to pkg/roachprod/ui/collate_errors.go diff --git a/pkg/cmd/roachprod/ui/writer.go b/pkg/roachprod/ui/writer.go similarity index 100% rename from pkg/cmd/roachprod/ui/writer.go rename to pkg/roachprod/ui/writer.go diff --git a/pkg/cmd/roachprod/vm/BUILD.bazel b/pkg/roachprod/vm/BUILD.bazel similarity index 78% rename from pkg/cmd/roachprod/vm/BUILD.bazel rename to pkg/roachprod/vm/BUILD.bazel index b151e0494bda..92011d6d2095 100644 --- a/pkg/cmd/roachprod/vm/BUILD.bazel +++ b/pkg/roachprod/vm/BUILD.bazel @@ -3,10 +3,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "vm", srcs = ["vm.go"], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/config", + "//pkg/roachprod/config", + "//pkg/util/log", "@com_github_cockroachdb_errors//:errors", "@com_github_spf13_pflag//:pflag", "@org_golang_x_sync//errgroup", diff --git a/pkg/cmd/roachprod/vm/aws/.gitattributes b/pkg/roachprod/vm/aws/.gitattributes similarity index 100% rename from pkg/cmd/roachprod/vm/aws/.gitattributes rename to pkg/roachprod/vm/aws/.gitattributes diff --git a/pkg/cmd/roachprod/vm/aws/BUILD.bazel b/pkg/roachprod/vm/aws/BUILD.bazel similarity index 77% rename from pkg/cmd/roachprod/vm/aws/BUILD.bazel rename to pkg/roachprod/vm/aws/BUILD.bazel index b0af253894a7..d23f2570a03e 100644 --- a/pkg/cmd/roachprod/vm/aws/BUILD.bazel +++ b/pkg/roachprod/vm/aws/BUILD.bazel @@ -10,11 +10,12 @@ go_library( "support.go", ":embedded", # keep ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/aws", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm/aws", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/vm", - "//pkg/cmd/roachprod/vm/flagstub", + "//pkg/roachprod/vm", + "//pkg/roachprod/vm/flagstub", + "//pkg/util/log", "//pkg/util/retry", "//pkg/util/syncutil", "//pkg/util/timeutil", @@ -30,9 +31,9 @@ genrule( name = "gen-main-tf", outs = ["terraform/main.tf"], cmd = """ - $(location //pkg/cmd/roachprod/vm/aws/terraformgen) -o $@ + $(location //pkg/roachprod/vm/aws/terraformgen) -o $@ """, - exec_tools = ["//pkg/cmd/roachprod/vm/aws/terraformgen"], + exec_tools = ["//pkg/roachprod/vm/aws/terraformgen"], ) bindata( diff --git a/pkg/cmd/roachprod/vm/aws/README.md b/pkg/roachprod/vm/aws/README.md similarity index 100% rename from pkg/cmd/roachprod/vm/aws/README.md rename to pkg/roachprod/vm/aws/README.md diff --git a/pkg/cmd/roachprod/vm/aws/aws.go b/pkg/roachprod/vm/aws/aws.go similarity index 90% rename from pkg/cmd/roachprod/vm/aws/aws.go rename to pkg/roachprod/vm/aws/aws.go index a716915dfff6..27143a96475c 100644 --- a/pkg/cmd/roachprod/vm/aws/aws.go +++ b/pkg/roachprod/vm/aws/aws.go @@ -11,18 +11,19 @@ package aws import ( + "context" "encoding/json" "fmt" "io/ioutil" - "log" "math/rand" "os" "os/exec" "strings" "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/flagstub" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/flagstub" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/retry" "github.com/cockroachdb/cockroach/pkg/util/syncutil" "github.com/cockroachdb/errors" @@ -46,7 +47,7 @@ func init() { "(https://docs.aws.amazon.com/cli/latest/userguide/installing.html)" const noCredentials = "missing AWS credentials, expected ~/.aws/credentials file or AWS_ACCESS_KEY_ID env var" - var p vm.Provider = &Provider{} + var p vm.Provider = &Provider{opts: DefaultProviderOpts()} haveRequiredVersion := func() bool { cmd := exec.Command("aws", "--version") @@ -129,7 +130,7 @@ func (d *ebsDisk) Set(s string) error { return errors.AssertionFailedf("Iops required for gp3 disk: [3000, 16000]") } if d.IOPs == 0 { - // 30000 is a base IOPs for gp3. + // 3000 is a base IOPs for gp3. d.IOPs = 3000 } if d.Throughput == 0 { @@ -184,8 +185,26 @@ func (vl *ebsVolumeList) String() string { return "EBSVolumeList" } -// providerOpts implements the vm.ProviderFlags interface for aws.Provider. -type providerOpts struct { +// DefaultProviderOpts returns a new aws.ProviderOpts with default values set. +func DefaultProviderOpts() ProviderOpts { + configVal := awsConfigValue{awsConfig: *defaultConfig} + defaultEBSVolumeValue := ebsVolume{} + defaultEBSVolumeValue.Disk.VolumeSize = ebsDefaultVolumeSizeGB + defaultEBSVolumeValue.Disk.VolumeType = defaultEBSVolumeType + return ProviderOpts{ + Profile: os.Getenv("AWS_DEFAULT_PROFILE"), // "" if unset + Config: &configVal.awsConfig, + MachineType: "m5.xlarge", + SSDMachineType: "m5d.xlarge", + RemoteUserName: "ubuntu", + DefaultEBSVolume: defaultEBSVolumeValue, + IAMProfile: "roachprod-testing", + CreateRateLimit: 2, + } +} + +// ProviderOpts provides user-configurable, aws-specific create options. +type ProviderOpts struct { Profile string Config *awsConfig @@ -243,58 +262,58 @@ var defaultCreateZones = []string{ // regions and the ids of the things we want to use there. This is // somewhat complicated because different EC2 regions may as well // be parallel universes. -func (o *providerOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { +func (o *ProviderOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { // m5.xlarge is a 4core, 16Gb instance, approximately equal to a GCE n1-standard-4 - flags.StringVar(&o.MachineType, ProviderName+"-machine-type", defaultMachineType, + flags.StringVar(&o.MachineType, ProviderName+"-machine-type", o.MachineType, "Machine type (see https://aws.amazon.com/ec2/instance-types/)") // The m5 devices only support EBS volumes, so we need a different instance type // for directly-attached SSD support. This is 4 core, 16GB ram, 150GB ssd. - flags.StringVar(&o.SSDMachineType, ProviderName+"-machine-type-ssd", defaultSSDMachineType, + flags.StringVar(&o.SSDMachineType, ProviderName+"-machine-type-ssd", o.SSDMachineType, "Machine type for --local-ssd (see https://aws.amazon.com/ec2/instance-types/)") - flags.StringVar(&o.CPUOptions, ProviderName+"-cpu-options", "", + flags.StringVar(&o.CPUOptions, ProviderName+"-cpu-options", o.CPUOptions, "Options to specify number of cores and threads per core (see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-optimize-cpu.html#instance-specify-cpu-options)") // AWS images generally use "ubuntu" or "ec2-user" flags.StringVar(&o.RemoteUserName, ProviderName+"-user", - "ubuntu", "Name of the remote user to SSH as") + o.RemoteUserName, "Name of the remote user to SSH as") flags.StringVar(&o.DefaultEBSVolume.Disk.VolumeType, ProviderName+"-ebs-volume-type", - "", "Type of the EBS volume, only used if local-ssd=false") + o.DefaultEBSVolume.Disk.VolumeType, "Type of the EBS volume, only used if local-ssd=false") flags.IntVar(&o.DefaultEBSVolume.Disk.VolumeSize, ProviderName+"-ebs-volume-size", - ebsDefaultVolumeSizeGB, "Size in GB of EBS volume, only used if local-ssd=false") + o.DefaultEBSVolume.Disk.VolumeSize, "Size in GB of EBS volume, only used if local-ssd=false") flags.IntVar(&o.DefaultEBSVolume.Disk.IOPs, ProviderName+"-ebs-iops", - 0, "Number of IOPs to provision for supported disk types (io1, io2, gp3)") + o.DefaultEBSVolume.Disk.IOPs, "Number of IOPs to provision for supported disk types (io1, io2, gp3)") flags.IntVar(&o.DefaultEBSVolume.Disk.Throughput, ProviderName+"-ebs-throughput", - 0, "Additional throughput to provision, in MiB/s") + o.DefaultEBSVolume.Disk.Throughput, "Additional throughput to provision, in MiB/s") flags.VarP(&o.EBSVolumes, ProviderName+"-ebs-volume", "", `Additional EBS disk to attached, repeated for extra disks; specified as JSON: {"VolumeType":"io2","VolumeSize":213,"Iops":321}`) - flags.StringSliceVar(&o.CreateZones, ProviderName+"-zones", nil, + flags.StringSliceVar(&o.CreateZones, ProviderName+"-zones", o.CreateZones, fmt.Sprintf("aws availability zones to use for cluster creation. If zones are formatted\n"+ "as AZ:N where N is an integer, the zone will be repeated N times. If > 1\n"+ "zone specified, the cluster will be spread out evenly by zone regardless\n"+ "of geo (default [%s])", strings.Join(defaultCreateZones, ","))) flags.StringVar(&o.ImageAMI, ProviderName+"-image-ami", - "", "Override image AMI to use. See https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/describe-images.html") + o.ImageAMI, "Override image AMI to use. See https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/describe-images.html") flags.BoolVar(&o.UseMultipleDisks, ProviderName+"-enable-multiple-stores", - false, "Enable the use of multiple stores by creating one store directory per disk. "+ + o.UseMultipleDisks, "Enable the use of multiple stores by creating one store directory per disk. "+ "Default is to raid0 stripe all disks. "+ "See repeating --"+ProviderName+"-ebs-volume for adding extra volumes.") - flags.Float64Var(&o.CreateRateLimit, ProviderName+"-create-rate-limit", 2, "aws"+ + flags.Float64Var(&o.CreateRateLimit, ProviderName+"-create-rate-limit", o.CreateRateLimit, "aws"+ " rate limit (per second) for instance creation. This is used to avoid hitting the request"+ " limits from aws, which can vary based on the region, and the size of the cluster being"+ " created. Try lowering this limit when hitting 'Request limit exceeded' errors.") - flags.StringVar(&o.IAMProfile, ProviderName+"- iam-profile", "roachprod-testing", + flags.StringVar(&o.IAMProfile, ProviderName+"- iam-profile", o.IAMProfile, "the IAM instance profile to associate with created VMs if non-empty") } -func (o *providerOpts) ConfigureClusterFlags(flags *pflag.FlagSet, _ vm.MultipleProjectsOption) { - profile := os.Getenv("AWS_DEFAULT_PROFILE") // "" if unset - flags.StringVar(&o.Profile, ProviderName+"-profile", profile, +// ConfigureClusterFlags implements vm.ProviderFlags. +func (o *ProviderOpts) ConfigureClusterFlags(flags *pflag.FlagSet, _ vm.MultipleProjectsOption) { + flags.StringVar(&o.Profile, ProviderName+"-profile", o.Profile, "Profile to manage cluster in") configFlagVal := awsConfigValue{awsConfig: *defaultConfig} o.Config = &configFlagVal.awsConfig @@ -302,9 +321,17 @@ func (o *providerOpts) ConfigureClusterFlags(flags *pflag.FlagSet, _ vm.Multiple "Path to json for aws configuration, defaults to predefined configuration") } +// ConfigureProviderOpts implements vm.ProviderFlags. +// Usage: create a new struct with default values using DefaultProviderOpts() +// and update its values then pass it to ConfigureProviderOpts(). +func (o *ProviderOpts) ConfigureProviderOpts(updatedOpts interface{}) { + // cast interface to ProviderOpts before assisgning + *o = updatedOpts.(ProviderOpts) +} + // Provider implements the vm.Provider interface for AWS. type Provider struct { - opts providerOpts + opts ProviderOpts } // CleanSSH is part of vm.Provider. This implementation is a no-op, @@ -344,7 +371,7 @@ func (p *Provider) ConfigSSH() error { if err != nil { return err } - log.Printf("imported %s as %s in region %s", + log.Infof(context.Background(), "imported %s as %s in region %s", sshPublicKeyFile, keyName, region) } return nil @@ -884,7 +911,6 @@ func (p *Provider) runInstance(name string, zone string, opts vm.CreateOpts) err DeleteOnTermination: true, }, } - p.opts.EBSVolumes = append(p.opts.EBSVolumes, osDiskVolume) mapping, err := json.Marshal(p.opts.EBSVolumes) diff --git a/pkg/cmd/roachprod/vm/aws/config.go b/pkg/roachprod/vm/aws/config.go similarity index 100% rename from pkg/cmd/roachprod/vm/aws/config.go rename to pkg/roachprod/vm/aws/config.go diff --git a/pkg/cmd/roachprod/vm/aws/config.json b/pkg/roachprod/vm/aws/config.json similarity index 100% rename from pkg/cmd/roachprod/vm/aws/config.json rename to pkg/roachprod/vm/aws/config.json diff --git a/pkg/cmd/roachprod/vm/aws/embedded.go b/pkg/roachprod/vm/aws/embedded.go similarity index 100% rename from pkg/cmd/roachprod/vm/aws/embedded.go rename to pkg/roachprod/vm/aws/embedded.go diff --git a/pkg/cmd/roachprod/vm/aws/keys.go b/pkg/roachprod/vm/aws/keys.go similarity index 100% rename from pkg/cmd/roachprod/vm/aws/keys.go rename to pkg/roachprod/vm/aws/keys.go diff --git a/pkg/cmd/roachprod/vm/aws/old.json b/pkg/roachprod/vm/aws/old.json similarity index 100% rename from pkg/cmd/roachprod/vm/aws/old.json rename to pkg/roachprod/vm/aws/old.json diff --git a/pkg/cmd/roachprod/vm/aws/support.go b/pkg/roachprod/vm/aws/support.go similarity index 97% rename from pkg/cmd/roachprod/vm/aws/support.go rename to pkg/roachprod/vm/aws/support.go index 57e08862fe51..2da86a40f7eb 100644 --- a/pkg/cmd/roachprod/vm/aws/support.go +++ b/pkg/roachprod/vm/aws/support.go @@ -12,14 +12,15 @@ package aws import ( "bytes" + "context" "encoding/json" "io/ioutil" - "log" "os/exec" "strings" "text/template" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" ) @@ -188,7 +189,7 @@ func (p *Provider) runCommand(args []string) ([]byte, error) { output, err := cmd.Output() if err != nil { if exitErr := (*exec.ExitError)(nil); errors.As(err, &exitErr) { - log.Println(string(exitErr.Stderr)) + log.Infof(context.Background(), "%s", string(exitErr.Stderr)) } return nil, errors.Wrapf(err, "failed to run: aws %s: stderr: %v", strings.Join(args, " "), stderrBuf.String()) diff --git a/pkg/cmd/roachprod/vm/aws/terraform/.gitignore b/pkg/roachprod/vm/aws/terraform/.gitignore similarity index 100% rename from pkg/cmd/roachprod/vm/aws/terraform/.gitignore rename to pkg/roachprod/vm/aws/terraform/.gitignore diff --git a/pkg/cmd/roachprod/vm/aws/terraform/aws-region/ami.tf b/pkg/roachprod/vm/aws/terraform/aws-region/ami.tf similarity index 100% rename from pkg/cmd/roachprod/vm/aws/terraform/aws-region/ami.tf rename to pkg/roachprod/vm/aws/terraform/aws-region/ami.tf diff --git a/pkg/cmd/roachprod/vm/aws/terraform/aws-region/main.tf b/pkg/roachprod/vm/aws/terraform/aws-region/main.tf similarity index 100% rename from pkg/cmd/roachprod/vm/aws/terraform/aws-region/main.tf rename to pkg/roachprod/vm/aws/terraform/aws-region/main.tf diff --git a/pkg/cmd/roachprod/vm/aws/terraform/aws-region/network.tf b/pkg/roachprod/vm/aws/terraform/aws-region/network.tf similarity index 100% rename from pkg/cmd/roachprod/vm/aws/terraform/aws-region/network.tf rename to pkg/roachprod/vm/aws/terraform/aws-region/network.tf diff --git a/pkg/cmd/roachprod/vm/aws/terraform/aws-vpc-peer/main.tf b/pkg/roachprod/vm/aws/terraform/aws-vpc-peer/main.tf similarity index 100% rename from pkg/cmd/roachprod/vm/aws/terraform/aws-vpc-peer/main.tf rename to pkg/roachprod/vm/aws/terraform/aws-vpc-peer/main.tf diff --git a/pkg/cmd/roachprod/vm/aws/terraform/main.tf b/pkg/roachprod/vm/aws/terraform/main.tf similarity index 100% rename from pkg/cmd/roachprod/vm/aws/terraform/main.tf rename to pkg/roachprod/vm/aws/terraform/main.tf diff --git a/pkg/cmd/roachprod/vm/aws/terraformgen/BUILD.bazel b/pkg/roachprod/vm/aws/terraformgen/BUILD.bazel similarity index 63% rename from pkg/cmd/roachprod/vm/aws/terraformgen/BUILD.bazel rename to pkg/roachprod/vm/aws/terraformgen/BUILD.bazel index db8bdd628503..36a608d80874 100644 --- a/pkg/cmd/roachprod/vm/aws/terraformgen/BUILD.bazel +++ b/pkg/roachprod/vm/aws/terraformgen/BUILD.bazel @@ -3,9 +3,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "terraformgen_lib", srcs = ["terraformgen.go"], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/aws/terraformgen", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm/aws/terraformgen", visibility = ["//visibility:private"], - deps = ["@com_github_spf13_cobra//:cobra"], + deps = [ + "//pkg/cli/exit", + "@com_github_spf13_cobra//:cobra", + ], ) go_binary( diff --git a/pkg/cmd/roachprod/vm/aws/terraformgen/terraformgen.go b/pkg/roachprod/vm/aws/terraformgen/terraformgen.go similarity index 98% rename from pkg/cmd/roachprod/vm/aws/terraformgen/terraformgen.go rename to pkg/roachprod/vm/aws/terraformgen/terraformgen.go index de5f6875278c..f0bc3903a9ec 100644 --- a/pkg/cmd/roachprod/vm/aws/terraformgen/terraformgen.go +++ b/pkg/roachprod/vm/aws/terraformgen/terraformgen.go @@ -18,6 +18,7 @@ import ( "os" "text/template" + "github.com/cockroachdb/cockroach/pkg/cli/exit" "github.com/spf13/cobra" ) @@ -197,5 +198,5 @@ func exitIfError(err error) { return } fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) + exit.WithCode(exit.UnspecifiedError()) } diff --git a/pkg/cmd/roachprod/vm/azure/BUILD.bazel b/pkg/roachprod/vm/azure/BUILD.bazel similarity index 86% rename from pkg/cmd/roachprod/vm/azure/BUILD.bazel rename to pkg/roachprod/vm/azure/BUILD.bazel index 2a32fb4cebfa..3cc2e2d9676a 100644 --- a/pkg/cmd/roachprod/vm/azure/BUILD.bazel +++ b/pkg/roachprod/vm/azure/BUILD.bazel @@ -10,11 +10,12 @@ go_library( "ids.go", "utils.go", ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/azure", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm/azure", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/vm", - "//pkg/cmd/roachprod/vm/flagstub", + "//pkg/roachprod/vm", + "//pkg/roachprod/vm/flagstub", + "//pkg/util/log", "//pkg/util/syncutil", "//pkg/util/timeutil", "@com_github_azure_azure_sdk_for_go//profiles/latest/compute/mgmt/compute", diff --git a/pkg/cmd/roachprod/vm/azure/auth.go b/pkg/roachprod/vm/azure/auth.go similarity index 100% rename from pkg/cmd/roachprod/vm/azure/auth.go rename to pkg/roachprod/vm/azure/auth.go diff --git a/pkg/cmd/roachprod/vm/azure/azure.go b/pkg/roachprod/vm/azure/azure.go similarity index 94% rename from pkg/cmd/roachprod/vm/azure/azure.go rename to pkg/roachprod/vm/azure/azure.go index 3e27f27eb2c0..2ef7bff16528 100644 --- a/pkg/cmd/roachprod/vm/azure/azure.go +++ b/pkg/roachprod/vm/azure/azure.go @@ -16,7 +16,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "os" "strconv" "strings" @@ -28,8 +27,9 @@ import ( "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/subscriptions" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/flagstub" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/flagstub" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/syncutil" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" @@ -66,7 +66,7 @@ func init() { // Provider implements the vm.Provider interface for the Microsoft Azure // cloud. type Provider struct { - opts providerOpts + opts ProviderOpts mu struct { syncutil.Mutex @@ -80,7 +80,7 @@ type Provider struct { // New constructs a new Provider instance. func New() *Provider { - p := &Provider{} + p := &Provider{opts: DefaultProviderOpts()} p.mu.resourceGroups = make(map[string]resources.Group) p.mu.securityGroups = make(map[string]network.SecurityGroup) p.mu.subnets = make(map[string]network.Subnet) @@ -128,29 +128,29 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { return fmt.Sprintf("%s-%s", opts.ClusterName, location) } - ctx, cancel := context.WithTimeout(context.Background(), p.opts.operationTimeout) + ctx, cancel := context.WithTimeout(context.Background(), p.opts.OperationTimeout) defer cancel() - if len(p.opts.locations) == 0 { + if len(p.opts.Locations) == 0 { if opts.GeoDistributed { - p.opts.locations = defaultLocations + p.opts.Locations = defaultLocations } else { - p.opts.locations = []string{defaultLocations[0]} + p.opts.Locations = []string{defaultLocations[0]} } } - if len(p.opts.zone) == 0 { - p.opts.zone = defaultZone + if len(p.opts.Zone) == 0 { + p.opts.Zone = defaultZone } - if _, err := p.createVNets(ctx, p.opts.locations); err != nil { + if _, err := p.createVNets(ctx, p.opts.Locations); err != nil { return err } // Effectively a map of node number to location. - nodeLocations := vm.ZonePlacement(len(p.opts.locations), len(names)) + nodeLocations := vm.ZonePlacement(len(p.opts.Locations), len(names)) // Invert it. - nodesByLocIdx := make(map[int][]int, len(p.opts.locations)) + nodesByLocIdx := make(map[int][]int, len(p.opts.Locations)) for nodeIdx, locIdx := range nodeLocations { nodesByLocIdx[locIdx] = append(nodesByLocIdx[locIdx], nodeIdx) } @@ -161,7 +161,7 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { locIdx := locIdx nodes := nodes errs.Go(func() error { - location := p.opts.locations[locIdx] + location := p.opts.Locations[locIdx] // Create a resource group within the location. group, err := p.getOrCreateResourceGroup( @@ -183,7 +183,7 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { _, err := p.createVM(ctx, group, subnet, name, sshKey, opts) err = errors.Wrapf(err, "creating VM %s", name) if err == nil { - log.Printf("created VM %s", name) + log.Infof(context.Background(), "created VM %s", name) } return err }) @@ -196,7 +196,7 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { // Delete implements the vm.Provider interface. func (p *Provider) Delete(vms vm.List) error { - ctx, cancel := context.WithTimeout(context.Background(), p.opts.operationTimeout) + ctx, cancel := context.WithTimeout(context.Background(), p.opts.OperationTimeout) defer cancel() sub, err := p.getSubscription(ctx) @@ -221,7 +221,7 @@ func (p *Provider) Delete(vms vm.List) error { futures = append(futures, future) } - if !p.opts.syncDelete { + if !p.opts.SyncDelete { return nil } @@ -244,7 +244,7 @@ func (p *Provider) Reset(vms vm.List) error { // DeleteCluster implements the vm.DeleteCluster interface, providing // a fast-path to tear down all resources associated with a cluster. func (p *Provider) DeleteCluster(name string) error { - ctx, cancel := context.WithTimeout(context.Background(), p.opts.operationTimeout) + ctx, cancel := context.WithTimeout(context.Background(), p.opts.OperationTimeout) defer cancel() sub, err := p.getSubscription(ctx) @@ -270,7 +270,7 @@ func (p *Provider) DeleteCluster(name string) error { if err != nil { return err } - log.Printf("marked Azure resource group %s for deletion\n", *group.Name) + log.Infof(context.Background(), "marked Azure resource group %s for deletion\n", *group.Name) futures = append(futures, future) if err := it.NextWithContext(ctx); err != nil { @@ -278,7 +278,7 @@ func (p *Provider) DeleteCluster(name string) error { } } - if !p.opts.syncDelete { + if !p.opts.SyncDelete { return nil } @@ -295,7 +295,7 @@ func (p *Provider) DeleteCluster(name string) error { // Extend implements the vm.Provider interface. func (p *Provider) Extend(vms vm.List, lifetime time.Duration) error { - ctx, cancel := context.WithTimeout(context.Background(), p.opts.operationTimeout) + ctx, cancel := context.WithTimeout(context.Background(), p.opts.OperationTimeout) defer cancel() sub, err := p.getSubscription(ctx) @@ -371,7 +371,7 @@ func (p *Provider) Flags() vm.ProviderFlags { // List implements the vm.Provider interface. This will query all // Azure VMs in the subscription and select those with a roachprod tag. func (p *Provider) List() (vm.List, error) { - ctx, cancel := context.WithTimeout(context.Background(), p.opts.operationTimeout) + ctx, cancel := context.WithTimeout(context.Background(), p.opts.OperationTimeout) defer cancel() sub, err := p.getSubscription(ctx) @@ -500,18 +500,18 @@ func (p *Provider) createVM( osVolumeSize := int32(opts.OsVolumeSize) if osVolumeSize < 32 { - log.Print("WARNING: increasing the OS volume size to minimally allowed 32GB") + log.Info(context.Background(), "WARNING: increasing the OS volume size to minimally allowed 32GB") osVolumeSize = 32 } // Derived from // https://github.com/Azure-Samples/azure-sdk-for-go-samples/blob/79e3f3af791c3873d810efe094f9d61e93a6ccaa/compute/vm.go#L41 vm = compute.VirtualMachine{ Location: group.Location, - Zones: to.StringSlicePtr([]string{p.opts.zone}), + Zones: to.StringSlicePtr([]string{p.opts.Zone}), Tags: tags, VirtualMachineProperties: &compute.VirtualMachineProperties{ HardwareProfile: &compute.HardwareProfile{ - VMSize: compute.VirtualMachineSizeTypes(p.opts.machineType), + VMSize: compute.VirtualMachineSizeTypes(p.opts.MachineType), }, StorageProfile: &compute.StorageProfile{ // From https://discourse.ubuntu.com/t/find-ubuntu-images-on-microsoft-azure/18918 @@ -566,7 +566,7 @@ func (p *Provider) createVM( if !opts.SSDOpts.UseLocalSSD { caching := compute.CachingTypesNone - switch p.opts.diskCaching { + switch p.opts.DiskCaching { case "read-only": caching = compute.CachingTypesReadOnly case "read-write": @@ -574,18 +574,18 @@ func (p *Provider) createVM( case "none": caching = compute.CachingTypesNone default: - err = errors.Newf("unsupported caching behavior: %s", p.opts.diskCaching) + err = errors.Newf("unsupported caching behavior: %s", p.opts.DiskCaching) return } dataDisks := []compute.DataDisk{ { - DiskSizeGB: to.Int32Ptr(p.opts.networkDiskSize), + DiskSizeGB: to.Int32Ptr(p.opts.NetworkDiskSize), Caching: caching, Lun: to.Int32Ptr(42), }, } - switch p.opts.networkDiskType { + switch p.opts.NetworkDiskType { case "ultra-disk": var ultraDisk compute.Disk ultraDisk, err = p.createUltraDisk(ctx, group, name+"-ultra-disk") @@ -611,7 +611,7 @@ func (p *Provider) createVM( StorageAccountType: compute.StorageAccountTypesPremiumLRS, } default: - err = errors.Newf("unsupported network disk type: %s", p.opts.networkDiskType) + err = errors.Newf("unsupported network disk type: %s", p.opts.NetworkDiskType) return } @@ -671,7 +671,7 @@ func (p *Provider) createNIC( } iface, err = future.Result(client) if err == nil { - log.Printf("created NIC %s %s", *iface.Name, *(*iface.IPConfigurations)[0].PrivateIPAddress) + log.Infof(context.Background(), "created NIC %s %s", *iface.Name, *(*iface.IPConfigurations)[0].PrivateIPAddress) } return } @@ -896,7 +896,7 @@ func (p *Provider) createVNets( } newSubnetsCreated := false - for _, location := range p.opts.locations { + for _, location := range p.opts.Locations { p.mu.Lock() group := p.mu.resourceGroups[vnetResourceGroupName(location)] p.mu.Unlock() @@ -964,7 +964,7 @@ func (p *Provider) createVNet( securityGroup network.SecurityGroup, prefix int, ) (vnet network.VirtualNetwork, subnet network.Subnet, err error) { - vnetName := p.opts.vnetName + vnetName := p.opts.VnetName sub, err := p.getSubscription(ctx) if err != nil { @@ -1010,7 +1010,7 @@ func (p *Provider) createVNet( p.mu.Lock() p.mu.subnets[*resourceGroup.Location] = subnet p.mu.Unlock() - log.Printf("created Azure VNet %q in %q with prefix %d", vnetName, *resourceGroup.Name, prefix) + log.Infof(context.Background(), "created Azure VNet %q in %q with prefix %d", vnetName, *resourceGroup.Name, prefix) return } @@ -1068,7 +1068,7 @@ func (p *Provider) createVNetPeerings(ctx context.Context, vnets []network.Virtu if err != nil { return errors.Wrapf(err, "creating vnet peering %s", name) } - log.Printf("created vnet peering %s", *peering.Name) + log.Infof(context.Background(), "created vnet peering %s", *peering.Name) } return nil @@ -1093,7 +1093,7 @@ func (p *Provider) createIP( Name: network.PublicIPAddressSkuNameStandard, }, Location: group.Location, - Zones: to.StringSlicePtr([]string{p.opts.zone}), + Zones: to.StringSlicePtr([]string{p.opts.Zone}), PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ PublicIPAddressVersion: network.IPVersionIPv4, PublicIPAllocationMethod: network.IPAllocationMethodStatic, @@ -1109,7 +1109,7 @@ func (p *Provider) createIP( return } if ip, err = future.Result(ipc); err == nil { - log.Printf("created Azure IP %s", *ip.Name) + log.Infof(context.Background(), "created Azure IP %s", *ip.Name) } else { err = errors.Wrapf(err, "creating IP %s", name) } @@ -1238,7 +1238,7 @@ func (p *Provider) createUltraDisk( future, err := client.CreateOrUpdate(ctx, *group.Name, name, compute.Disk{ - Zones: to.StringSlicePtr([]string{p.opts.zone}), + Zones: to.StringSlicePtr([]string{p.opts.Zone}), Location: group.Location, Sku: &compute.DiskSku{ Name: compute.DiskStorageAccountTypesUltraSSDLRS, @@ -1247,8 +1247,8 @@ func (p *Provider) createUltraDisk( CreationData: &compute.CreationData{ CreateOption: compute.DiskCreateOptionEmpty, }, - DiskSizeGB: to.Int32Ptr(p.opts.networkDiskSize), - DiskIOPSReadWrite: to.Int64Ptr(p.opts.ultraDiskIOPS), + DiskSizeGB: to.Int32Ptr(p.opts.NetworkDiskSize), + DiskIOPSReadWrite: to.Int64Ptr(p.opts.UltraDiskIOPS), }, }) if err != nil { @@ -1261,7 +1261,7 @@ func (p *Provider) createUltraDisk( if err != nil { return compute.Disk{}, err } - log.Printf("created ultra-disk: %s\n", *disk.Name) + log.Infof(context.Background(), "created ultra-disk: %s\n", *disk.Name) return disk, err } diff --git a/pkg/cmd/roachprod/vm/azure/doc.go b/pkg/roachprod/vm/azure/doc.go similarity index 100% rename from pkg/cmd/roachprod/vm/azure/doc.go rename to pkg/roachprod/vm/azure/doc.go diff --git a/pkg/roachprod/vm/azure/flags.go b/pkg/roachprod/vm/azure/flags.go new file mode 100644 index 000000000000..576f249ec1ea --- /dev/null +++ b/pkg/roachprod/vm/azure/flags.go @@ -0,0 +1,96 @@ +// Copyright 2019 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package azure + +import ( + "fmt" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/spf13/pflag" +) + +// ProviderOpts provides user-configurable, azure-specific create options. +type ProviderOpts struct { + Locations []string + MachineType string + OperationTimeout time.Duration + SyncDelete bool + VnetName string + Zone string + NetworkDiskType string + NetworkDiskSize int32 + UltraDiskIOPS int64 + DiskCaching string +} + +var defaultLocations = []string{ + "eastus2", + "westus", + "westeurope", +} + +var defaultZone = "1" + +// DefaultProviderOpts returns a new azure.ProviderOpts with default values set. +func DefaultProviderOpts() ProviderOpts { + return ProviderOpts{ + Locations: nil, + MachineType: string(compute.VirtualMachineSizeTypesStandardD4V3), + OperationTimeout: 10 * time.Minute, + SyncDelete: false, + VnetName: "common", + Zone: "", + NetworkDiskType: "premium-disk", + NetworkDiskSize: 500, + UltraDiskIOPS: 5000, + DiskCaching: "none", + } +} + +// ConfigureCreateFlags implements vm.ProviderFlags. +func (o *ProviderOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { + flags.DurationVar(&o.OperationTimeout, ProviderName+"-timeout", 10*time.Minute, + "The maximum amount of time for an Azure API operation to take") + flags.BoolVar(&o.SyncDelete, ProviderName+"-sync-delete", false, + "Wait for deletions to finish before returning") + flags.StringVar(&o.MachineType, ProviderName+"-machine-type", + string(compute.VirtualMachineSizeTypesStandardD4V3), + "Machine type (see https://azure.microsoft.com/en-us/pricing/details/virtual-machines/linux/)") + flags.StringSliceVar(&o.Locations, ProviderName+"-locations", nil, + fmt.Sprintf("Locations for cluster (see `az account list-locations`) (default\n[%s])", + strings.Join(defaultLocations, ","))) + flags.StringVar(&o.VnetName, ProviderName+"-vnet-name", "common", + "The name of the VNet to use") + flags.StringVar(&o.Zone, ProviderName+"-availability-zone", "", "Availability Zone to create VMs in") + flags.StringVar(&o.NetworkDiskType, ProviderName+"-network-disk-type", "premium-disk", + "type of network disk [premium-disk, ultra-disk]. only used if local-ssd is false") + flags.Int32Var(&o.NetworkDiskSize, ProviderName+"-volume-size", 500, + "Size in GB of network disk volume, only used if local-ssd=false") + flags.Int64Var(&o.UltraDiskIOPS, ProviderName+"-ultra-disk-iops", 5000, + "Number of IOPS provisioned for ultra disk, only used if network-disk-type=ultra-disk") + flags.StringVar(&o.DiskCaching, ProviderName+"-disk-caching", "none", + "Disk caching behavior for attached storage. Valid values are: none, read-only, read-write. Not applicable to Ultra disks.") +} + +// ConfigureClusterFlags implements vm.ProviderFlags and is a no-op. +func (o *ProviderOpts) ConfigureClusterFlags(*pflag.FlagSet, vm.MultipleProjectsOption) { +} + +// ConfigureProviderOpts implements vm.ProviderFlags. +// Usage: create a new struct with default values using DefaultProviderOpts() +// and update its values then pass it to ConfigureProviderOpts(). +func (o *ProviderOpts) ConfigureProviderOpts(updatedOpts interface{}) { + // cast interface to ProviderOpts before assisgning + *o = updatedOpts.(ProviderOpts) +} diff --git a/pkg/cmd/roachprod/vm/azure/ids.go b/pkg/roachprod/vm/azure/ids.go similarity index 100% rename from pkg/cmd/roachprod/vm/azure/ids.go rename to pkg/roachprod/vm/azure/ids.go diff --git a/pkg/cmd/roachprod/vm/azure/utils.go b/pkg/roachprod/vm/azure/utils.go similarity index 100% rename from pkg/cmd/roachprod/vm/azure/utils.go rename to pkg/roachprod/vm/azure/utils.go diff --git a/pkg/cmd/roachprod/vm/flagstub/BUILD.bazel b/pkg/roachprod/vm/flagstub/BUILD.bazel similarity index 66% rename from pkg/cmd/roachprod/vm/flagstub/BUILD.bazel rename to pkg/roachprod/vm/flagstub/BUILD.bazel index 55c2d7054ab2..7515a75cc8ed 100644 --- a/pkg/cmd/roachprod/vm/flagstub/BUILD.bazel +++ b/pkg/roachprod/vm/flagstub/BUILD.bazel @@ -3,10 +3,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "flagstub", srcs = ["flagstub.go"], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/flagstub", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm/flagstub", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/vm", + "//pkg/roachprod/vm", "@com_github_cockroachdb_errors//:errors", ], ) diff --git a/pkg/cmd/roachprod/vm/flagstub/flagstub.go b/pkg/roachprod/vm/flagstub/flagstub.go similarity index 97% rename from pkg/cmd/roachprod/vm/flagstub/flagstub.go rename to pkg/roachprod/vm/flagstub/flagstub.go index 723775ab745b..dcfe745ef182 100644 --- a/pkg/cmd/roachprod/vm/flagstub/flagstub.go +++ b/pkg/roachprod/vm/flagstub/flagstub.go @@ -13,7 +13,7 @@ package flagstub import ( "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" "github.com/cockroachdb/errors" ) diff --git a/pkg/cmd/roachprod/vm/gce/BUILD.bazel b/pkg/roachprod/vm/gce/BUILD.bazel similarity index 63% rename from pkg/cmd/roachprod/vm/gce/BUILD.bazel rename to pkg/roachprod/vm/gce/BUILD.bazel index a0764d7b1e3a..d9e3878dfca5 100644 --- a/pkg/cmd/roachprod/vm/gce/BUILD.bazel +++ b/pkg/roachprod/vm/gce/BUILD.bazel @@ -6,12 +6,12 @@ go_library( "gcloud.go", "utils.go", ], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/gce", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm/gce", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/config", - "//pkg/cmd/roachprod/vm", - "//pkg/cmd/roachprod/vm/flagstub", + "//pkg/roachprod/config", + "//pkg/roachprod/vm", + "//pkg/roachprod/vm/flagstub", "@com_github_cockroachdb_errors//:errors", "@com_github_spf13_pflag//:pflag", "@org_golang_x_sync//errgroup", diff --git a/pkg/cmd/roachprod/vm/gce/gcloud.go b/pkg/roachprod/vm/gce/gcloud.go similarity index 90% rename from pkg/cmd/roachprod/vm/gce/gcloud.go rename to pkg/roachprod/vm/gce/gcloud.go index e65ad9fc4c05..d07fd9dc5857 100644 --- a/pkg/cmd/roachprod/vm/gce/gcloud.go +++ b/pkg/roachprod/vm/gce/gcloud.go @@ -21,9 +21,9 @@ import ( "strings" "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/flagstub" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm/flagstub" "github.com/cockroachdb/errors" "github.com/spf13/pflag" "golang.org/x/sync/errgroup" @@ -98,7 +98,7 @@ type jsonVM struct { } // Convert the JSON VM data into our common VM type -func (jsonVM *jsonVM) toVM(project string, opts *providerOpts) (ret *vm.VM) { +func (jsonVM *jsonVM) toVM(project string, opts *ProviderOpts) (ret *vm.VM) { var vmErrors []error var err error @@ -170,8 +170,31 @@ type jsonAuth struct { Status string } -// User-configurable, provider-specific options -type providerOpts struct { +// DefaultProviderOpts returns a new gce.ProviderOpts with default values set. +func DefaultProviderOpts() ProviderOpts { + project := os.Getenv("GCE_PROJECT") + if project == "" { + project = defaultProject + } + return ProviderOpts{ + // projects needs space for one project, which is set by the flags for + // commands that accept a single project. + projects: []string{project}, + ServiceAccount: os.Getenv("GCE_SERVICE_ACCOUNT"), + MachineType: "n1-standard-4", + MinCPUPlatform: "", + Zones: nil, + Image: "ubuntu-2004-focal-v20210603", + SSDCount: 1, + PDVolumeType: "pd-ssd", + PDVolumeSize: 500, + useSharedUser: true, + preemptible: false, + } +} + +// ProviderOpts provides user-configurable, gce-specific create options. +type ProviderOpts struct { // projects represent the GCE projects to operate on. Accessed through // GetProject() or GetProjects() depending on whether the command accepts // multiple projects or a single one. @@ -192,11 +215,11 @@ type providerOpts struct { preemptible bool } -// projectsVal is the implementation for the --gce-projects flag. It populates +// ProjectsVal is the implementation for the --gce-projects flag. It populates // opts.projects. -type projectsVal struct { - acceptMultipleProjects bool - opts *providerOpts +type ProjectsVal struct { + AcceptMultipleProjects bool + Opts *ProviderOpts } // defaultZones is the list of zones used by default for cluster creation. @@ -208,41 +231,29 @@ var defaultZones = []string{ } // Set is part of the pflag.Value interface. -func (v projectsVal) Set(projects string) error { +func (v ProjectsVal) Set(projects string) error { if projects == "" { return fmt.Errorf("empty GCE project") } prj := strings.Split(projects, ",") - if !v.acceptMultipleProjects && len(prj) > 1 { + if !v.AcceptMultipleProjects && len(prj) > 1 { return fmt.Errorf("multiple GCE projects not supported for command") } - v.opts.projects = prj + v.Opts.projects = prj return nil } // Type is part of the pflag.Value interface. -func (v projectsVal) Type() string { - if v.acceptMultipleProjects { +func (v ProjectsVal) Type() string { + if v.AcceptMultipleProjects { return "comma-separated list of GCE projects" } return "GCE project name" } // String is part of the pflag.Value interface. -func (v projectsVal) String() string { - return strings.Join(v.opts.projects, ",") -} - -func makeProviderOpts() providerOpts { - project := os.Getenv("GCE_PROJECT") - if project == "" { - project = defaultProject - } - return providerOpts{ - // projects needs space for one project, which is set by the flags for - // commands that accept a single project. - projects: []string{project}, - } +func (v ProjectsVal) String() string { + return strings.Join(v.Opts.projects, ",") } // GetProject returns the GCE project on which we're configured to operate. @@ -262,7 +273,8 @@ func (p *Provider) GetProjects() []string { return p.opts.projects } -func (o *providerOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { +// ConfigureCreateFlags implements vm.ProviderFlags. +func (o *ProviderOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { flags.StringVar(&o.MachineType, "machine-type", "n1-standard-4", "DEPRECATED") _ = flags.MarkDeprecated("machine-type", "use "+ProviderName+"-machine-type instead") flags.StringSliceVar(&o.Zones, "zones", nil, "DEPRECATED") @@ -294,7 +306,8 @@ func (o *providerOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.preemptible, ProviderName+"-preemptible", false, "use preemptible GCE instances") } -func (o *providerOpts) ConfigureClusterFlags(flags *pflag.FlagSet, opt vm.MultipleProjectsOption) { +// ConfigureClusterFlags implements vm.ProviderFlags. +func (o *ProviderOpts) ConfigureClusterFlags(flags *pflag.FlagSet, opt vm.MultipleProjectsOption) { var usage string if opt == vm.SingleProject { usage = "GCE project to manage" @@ -303,9 +316,9 @@ func (o *providerOpts) ConfigureClusterFlags(flags *pflag.FlagSet, opt vm.Multip } flags.Var( - projectsVal{ - acceptMultipleProjects: opt == vm.AcceptMultipleProjects, - opts: o, + ProjectsVal{ + AcceptMultipleProjects: opt == vm.AcceptMultipleProjects, + Opts: o, }, ProviderName+"-project", /* name */ usage) @@ -316,13 +329,21 @@ func (o *providerOpts) ConfigureClusterFlags(flags *pflag.FlagSet, opt vm.Multip config.SharedUser, config.OSUser.Username)) } +// ConfigureProviderOpts implements vm.ProviderFlags. +// Usage: create a new struct with default values using DefaultProviderOpts() +// and update its values then pass it to ConfigureProviderOpts(). +func (o *ProviderOpts) ConfigureProviderOpts(updatedOpts interface{}) { + // cast interface to ProviderOpts before assisgning + *o = updatedOpts.(ProviderOpts) +} + // Provider is the GCE implementation of the vm.Provider interface. type Provider struct { - opts providerOpts + opts ProviderOpts } func makeProvider() Provider { - return Provider{opts: makeProviderOpts()} + return Provider{opts: DefaultProviderOpts()} } // CleanSSH TODO(peter): document diff --git a/pkg/cmd/roachprod/vm/gce/utils.go b/pkg/roachprod/vm/gce/utils.go similarity index 99% rename from pkg/cmd/roachprod/vm/gce/utils.go rename to pkg/roachprod/vm/gce/utils.go index 44e8fc596181..89d24a078d0a 100644 --- a/pkg/cmd/roachprod/vm/gce/utils.go +++ b/pkg/roachprod/vm/gce/utils.go @@ -21,7 +21,7 @@ import ( "strings" "text/template" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" "github.com/cockroachdb/errors" ) diff --git a/pkg/cmd/roachprod/vm/local/BUILD.bazel b/pkg/roachprod/vm/local/BUILD.bazel similarity index 61% rename from pkg/cmd/roachprod/vm/local/BUILD.bazel rename to pkg/roachprod/vm/local/BUILD.bazel index 92ef85e280a1..4a0be9889cab 100644 --- a/pkg/cmd/roachprod/vm/local/BUILD.bazel +++ b/pkg/roachprod/vm/local/BUILD.bazel @@ -3,12 +3,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "local", srcs = ["local.go"], - importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm/local", + importpath = "github.com/cockroachdb/cockroach/pkg/roachprod/vm/local", visibility = ["//visibility:public"], deps = [ - "//pkg/cmd/roachprod/config", - "//pkg/cmd/roachprod/install", - "//pkg/cmd/roachprod/vm", + "//pkg/roachprod/config", + "//pkg/roachprod/install", + "//pkg/roachprod/vm", "//pkg/util/timeutil", "@com_github_cockroachdb_errors//:errors", "@com_github_spf13_pflag//:pflag", diff --git a/pkg/cmd/roachprod/vm/local/local.go b/pkg/roachprod/vm/local/local.go similarity index 92% rename from pkg/cmd/roachprod/vm/local/local.go rename to pkg/roachprod/vm/local/local.go index 6dd261e2f648..214135ef85be 100644 --- a/pkg/cmd/roachprod/vm/local/local.go +++ b/pkg/roachprod/vm/local/local.go @@ -17,9 +17,9 @@ import ( "text/tabwriter" "time" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/install" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/vm" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/install" + "github.com/cockroachdb/cockroach/pkg/roachprod/vm" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" "github.com/spf13/pflag" @@ -46,6 +46,10 @@ func (o *emptyFlags) ConfigureCreateFlags(flags *pflag.FlagSet) { func (o *emptyFlags) ConfigureClusterFlags(*pflag.FlagSet, vm.MultipleProjectsOption) { } +// ConfigureProviderOpts is part of ProviderFlags. This implementation is a no-op. +func (o *emptyFlags) ConfigureProviderOpts(newOpts interface{}) { +} + // CleanSSH is part of the vm.Provider interface. This implementation is a no-op. func (p *Provider) CleanSSH() error { return nil diff --git a/pkg/cmd/roachprod/vm/vm.go b/pkg/roachprod/vm/vm.go similarity index 93% rename from pkg/cmd/roachprod/vm/vm.go rename to pkg/roachprod/vm/vm.go index c71c2e15ddbc..0536f182fcd5 100644 --- a/pkg/cmd/roachprod/vm/vm.go +++ b/pkg/roachprod/vm/vm.go @@ -11,15 +11,16 @@ package vm import ( + "context" "fmt" - "log" "regexp" "strconv" "strings" "time" "unicode" - "github.com/cockroachdb/cockroach/pkg/cmd/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/roachprod/config" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" "github.com/spf13/pflag" "golang.org/x/sync/errgroup" @@ -85,7 +86,7 @@ func (vm *VM) Locality() string { } else if match := regionRE.FindStringSubmatch(vm.Zone); len(match) == 2 { region = match[1] } else { - log.Fatalf("unable to parse region from zone %q", vm.Zone) + log.Fatalf(context.Background(), "unable to parse region from zone %q", vm.Zone) } return fmt.Sprintf("cloud=%s,region=%s,zone=%s", vm.Provider, region, vm.Zone) } @@ -152,6 +153,23 @@ type CreateOpts struct { OsVolumeSize int } +// DefaultCreateOpts returns a new vm.CreateOpts with default values set. +//lint:ignore U1001 unused +func DefaultCreateOpts() CreateOpts { + defaultCreateOpts := CreateOpts{ + ClusterName: "", + Lifetime: 12 * time.Hour, + GeoDistributed: false, + VMProviders: []string{}, + OsVolumeSize: 10, + } + defaultCreateOpts.SSDOpts.UseLocalSSD = true + defaultCreateOpts.SSDOpts.NoExt4Barrier = true + defaultCreateOpts.SSDOpts.FileSystem = Ext4 + + return defaultCreateOpts +} + // MultipleProjectsOption is used to specify whether a command accepts multiple // values for the --gce-project flag. type MultipleProjectsOption bool @@ -177,6 +195,8 @@ type ProviderFlags interface { // Configures a FlagSet with any options relevant to cluster manipulation // commands (`create`, `destroy`, `list`, `sync` and `gc`). ConfigureClusterFlags(*pflag.FlagSet, MultipleProjectsOption) + // Updates provider opts values to match the passed provider opts struct + ConfigureProviderOpts(interface{}) } // A Provider is a source of virtual machines running on some hosting platform. diff --git a/pkg/cmd/roachprod/vm/vm_test.go b/pkg/roachprod/vm/vm_test.go similarity index 100% rename from pkg/cmd/roachprod/vm/vm_test.go rename to pkg/roachprod/vm/vm_test.go diff --git a/pkg/testutils/lint/lint_test.go b/pkg/testutils/lint/lint_test.go index 08cb18f207a4..39a85eae1895 100644 --- a/pkg/testutils/lint/lint_test.go +++ b/pkg/testutils/lint/lint_test.go @@ -462,6 +462,7 @@ func TestLint(t *testing.T) { ":!util/log/tracebacks.go", ":!util/sdnotify/sdnotify_unix.go", ":!util/grpcutil", // GRPC_GO_* variables + ":!roachprod", // roachprod requires AWS environment variables }, }, } { @@ -803,6 +804,7 @@ func TestLint(t *testing.T) { ":!*_test.go", ":!cli/debug_synctest.go", ":!cmd/**", + ":!roachprod", // TODO: switch to contextutil ) if err != nil { t.Fatal(err) @@ -1516,6 +1518,7 @@ func TestLint(t *testing.T) { stream.GrepNot(`cockroach/pkg/cmd/`), stream.GrepNot(`cockroach/pkg/testutils/lint: log$`), stream.GrepNot(`cockroach/pkg/util/sysutil: syscall$`), + stream.GrepNot(`cockroach/pkg/roachprod/install: syscall$`), // TODO: switch to sysutil stream.GrepNot(`cockroach/pkg/util/log: github\.com/pkg/errors$`), stream.GrepNot(`cockroach/pkg/(base|release|security|util/(log|randutil|stop)): log$`), stream.GrepNot(`cockroach/pkg/(server/serverpb|ts/tspb): github\.com/golang/protobuf/proto$`), From 5b132309088a54b5727b8ead57e1cceb1185b5a1 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Mon, 1 Nov 2021 17:56:57 -0500 Subject: [PATCH 165/205] dev: allow linting a single package Tie into the `lint` test's support for specifiying `PKG` in the environment. Closes #72093. Release note: None --- pkg/cmd/dev/io/exec/exec.go | 15 +++++++++++++++ pkg/cmd/dev/lint.go | 20 ++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/dev/io/exec/exec.go b/pkg/cmd/dev/io/exec/exec.go index 4667119e3320..b2d10576e173 100644 --- a/pkg/cmd/dev/io/exec/exec.go +++ b/pkg/cmd/dev/io/exec/exec.go @@ -97,10 +97,24 @@ func (e *Exec) CommandContextWithInput( return e.commandContextImpl(ctx, r, false, name, args...) } +// CommandContextWithEnv is like CommandContextInheritingStdStreams, but +// accepting an additional argument for environment variables. +func (e *Exec) CommandContextWithEnv( + ctx context.Context, env []string, name string, args ...string, +) error { + return e.commandContextInheritingStdStreamsImpl(ctx, env, name, args...) +} + // CommandContextInheritingStdStreams is like CommandContext, but stdin, // stdout, and stderr are passed directly to the terminal. func (e *Exec) CommandContextInheritingStdStreams( ctx context.Context, name string, args ...string, +) error { + return e.commandContextInheritingStdStreamsImpl(ctx, nil, name, args...) +} + +func (e *Exec) commandContextInheritingStdStreamsImpl( + ctx context.Context, env []string, name string, args ...string, ) error { var command string if len(args) > 0 { @@ -117,6 +131,7 @@ func (e *Exec) CommandContextInheritingStdStreams( cmd.Stdout = e.stdout cmd.Stderr = e.stderr cmd.Dir = e.dir + cmd.Env = env if err := cmd.Start(); err != nil { return err diff --git a/pkg/cmd/dev/lint.go b/pkg/cmd/dev/lint.go index 477ac6a9533f..457dd52b9b29 100644 --- a/pkg/cmd/dev/lint.go +++ b/pkg/cmd/dev/lint.go @@ -12,6 +12,8 @@ package main import ( "fmt" + "os" + "strings" "github.com/spf13/cobra" ) @@ -23,8 +25,10 @@ func makeLintCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Comm Short: `Run the specified linters`, Long: `Run the specified linters.`, Example: ` - dev lint --filter=TestLowercaseFunctionNames --short --timeout=1m`, - Args: cobra.NoArgs, + dev lint --filter=TestLowercaseFunctionNames --short --timeout=1m + dev lint pkg/cmd/dev +`, + Args: cobra.MaximumNArgs(1), RunE: runE, } addCommonBuildFlags(lintCmd) @@ -32,7 +36,7 @@ func makeLintCmd(runE func(cmd *cobra.Command, args []string) error) *cobra.Comm return lintCmd } -func (d *dev) lint(cmd *cobra.Command, _ []string) error { +func (d *dev) lint(cmd *cobra.Command, pkgs []string) error { ctx := cmd.Context() filter := mustGetFlagString(cmd, filterFlag) timeout := mustGetFlagDuration(cmd, timeoutFlag) @@ -56,7 +60,15 @@ func (d *dev) lint(cmd *cobra.Command, _ []string) error { if filter != "" { args = append(args, "-test.run", fmt.Sprintf("Lint/%s", filter)) } - logCommand("bazel", args...) + if len(pkgs) > 0 { + pkg := strings.TrimRight(pkgs[0], "/") + if !strings.HasPrefix(pkg, "./") { + pkg = "./" + pkg + } + env := os.Environ() + env = append(env, fmt.Sprintf("PKG=%s", pkg)) + return d.exec.CommandContextWithEnv(ctx, env, "bazel", args...) + } return d.exec.CommandContextInheritingStdStreams(ctx, "bazel", args...) } From 9fdd40597fcec2dee2a5fed25c830c6a206947fd Mon Sep 17 00:00:00 2001 From: Gary Lau Date: Tue, 2 Nov 2021 11:19:15 -0700 Subject: [PATCH 166/205] authors: add Gary Lau to authors Release note: None --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 11c1878f20f4..6f3bbe300f06 100644 --- a/AUTHORS +++ b/AUTHORS @@ -157,6 +157,7 @@ fangwens Francis Bergin funkygao Garvit Juniwal +Gary Lau George Buckerfield georgebuckerfield George Papadrosou George Utsin From c2ec757a88c5f6bc9992e8d3a419597a399433e6 Mon Sep 17 00:00:00 2001 From: Zach Lite Date: Tue, 2 Nov 2021 14:25:49 -0400 Subject: [PATCH 167/205] authors: add to authors Release note: None --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 11c1878f20f4..1fe7751f4f2d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -430,6 +430,7 @@ YZ Chin Rafael Yim yznming Ryan Kuo <8740013+taroface@users.noreply.github.com> taroface Zach Brock +Zach Lite Zachary Smith Zachary.smith Zane Teh 何羿宏 From 98e94c98962ba1e8722502ae6a43d1049b8a12d2 Mon Sep 17 00:00:00 2001 From: Christopher Fitzner Date: Tue, 2 Nov 2021 11:31:05 -0700 Subject: [PATCH 168/205] authors: add fantapop to authors Release note: None --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 11c1878f20f4..e70a7328a6ea 100644 --- a/AUTHORS +++ b/AUTHORS @@ -104,6 +104,7 @@ chengwei <252684445@qq.com> Chengxiong Ruan Chris Seto <@cockroachlabs.com> Christian Meunier +Christopher Fitzner Christopher Routh christopherrouth Christopher Ye yecs1999 Chervine Majeri CMajeri From 3d40e2884d56c1c1ca23e95ee78f624fd3f397e4 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Tue, 2 Nov 2021 13:46:18 -0500 Subject: [PATCH 169/205] bazel: allow using `cross` configs without opting into CI-specific logic `dev` recommends using `--config=crosslinux`, which up to this point implied `--config=ci`. One of the configurations set by `--config=ci` is setting `/artifacts/tmp` as the `$TMPDIR` location for tests; this works in the Bazel builder image, which does mount the `/artifacts` directory, but will not exist on arbitrary dev machines. So we refactor the configurations slightly: now `--config=crosslinux` implies `--config=cross`, which is general-use, but CI jobs also have to opt into `--config=ci` separately. Closes #72324. Release note: None --- .bazelrc | 15 ++++++++------- build/teamcity/cockroach/ci/builds/build_impl.sh | 2 +- build/teamcity/cockroach/ci/tests/bench_impl.sh | 2 +- .../cockroach/ci/tests/local_roachtest_impl.sh | 4 ++-- pkg/cmd/dev/build.go | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.bazelrc b/.bazelrc index 72044484a931..4ab6de70abb8 100644 --- a/.bazelrc +++ b/.bazelrc @@ -10,8 +10,6 @@ query --ui_event_filters=-DEBUG # CI should always run with `--config=ci`. build:ci --experimental_convenience_symlinks=ignore -build:ci --stamp -build:ci --host_crosstool_top=@toolchain_cross_x86_64-unknown-linux-gnu//:suite # Set `-test.v` in Go tests. # Ref: https://github.com/bazelbuild/rules_go/pull/2456 test:ci --test_env=GO_TEST_WRAP_TESTV=1 @@ -20,26 +18,29 @@ test:ci --test_output=errors # Put all tmp artifacts in /artifacts/tmp. test:ci --test_tmpdir=/artifacts/tmp +build:cross --stamp +build:cross --host_crosstool_top=@toolchain_cross_x86_64-unknown-linux-gnu//:suite +build:cross --define cockroach_cross=y + # cross-compilation configurations. Add e.g. --config=crosslinux to turn these on # TODO(ricky): Having to specify both the `platform` and the `crosstool_top` is # weird, but I think `rules_foreign_cc` doesn't play too nicely with `--platforms`? build:crosslinux --platforms=//build/toolchains:cross_linux build:crosslinux --crosstool_top=@toolchain_cross_x86_64-unknown-linux-gnu//:suite build:crosslinux '--workspace_status_command=./build/bazelutil/stamp.sh x86_64-pc-linux-gnu' -build:crosslinux --config=ci --config=cross +build:crosslinux --config=cross build:crosswindows --platforms=//build/toolchains:cross_windows build:crosswindows --crosstool_top=@toolchain_cross_x86_64-w64-mingw32//:suite build:crosswindows '--workspace_status_command=./build/bazelutil/stamp.sh x86_64-w64-mingw32' -build:crosswindows --config=ci --config=cross +build:crosswindows --config=cross build:crossmacos --platforms=//build/toolchains:cross_macos build:crossmacos --crosstool_top=@toolchain_cross_x86_64-apple-darwin19//:suite build:crossmacos '--workspace_status_command=./build/bazelutil/stamp.sh x86_64-apple-darwin19' -build:crossmacos --config=ci --config=cross +build:crossmacos --config=cross build:crosslinuxarm --platforms=//build/toolchains:cross_linux_arm build:crosslinuxarm --crosstool_top=@toolchain_cross_aarch64-unknown-linux-gnu//:suite build:crosslinuxarm '--workspace_status_command=./build/bazelutil/stamp.sh aarch64-unknown-linux-gnu' -build:crosslinuxarm --config=ci --config=cross -build:cross --define cockroach_cross=y +build:crosslinuxarm --config=cross # developer configurations. Add e.g. --config=devdarwinx86_64 to turn these on. build:devdarwinx86_64 --crosstool_top=@toolchain_dev_darwin_x86-64//:suite diff --git a/build/teamcity/cockroach/ci/builds/build_impl.sh b/build/teamcity/cockroach/ci/builds/build_impl.sh index 26f699414819..d98b0242ce3b 100755 --- a/build/teamcity/cockroach/ci/builds/build_impl.sh +++ b/build/teamcity/cockroach/ci/builds/build_impl.sh @@ -21,6 +21,6 @@ fi bazel build //pkg/cmd/bazci --config=ci $(bazel info bazel-bin --config=ci)/pkg/cmd/bazci/bazci_/bazci --compilation_mode opt \ - --config "$CONFIG" --config with_ui \ + --config "$CONFIG" --config ci --config with_ui \ build //pkg/cmd/cockroach-short //pkg/cmd/cockroach \ //pkg/cmd/cockroach-oss //c-deps:libgeos $GENFILES_TARGETS diff --git a/build/teamcity/cockroach/ci/tests/bench_impl.sh b/build/teamcity/cockroach/ci/tests/bench_impl.sh index 9969c91884f9..56d6bf09cfdb 100755 --- a/build/teamcity/cockroach/ci/tests/bench_impl.sh +++ b/build/teamcity/cockroach/ci/tests/bench_impl.sh @@ -21,7 +21,7 @@ do tc_start_block "Bench $target" # We need the `test_sharding_strategy` flag or else the benchmarks will # fail to run sharded tests like //pkg/ccl/importccl:importccl_test. - bazel run --config=test --config=crosslinux --test_sharding_strategy=disabled $target -- \ + bazel run --config=test --config=crosslinux --config=ci --test_sharding_strategy=disabled $target -- \ -test.bench=. -test.benchtime=1ns -test.short -test.run=- tc_end_block "Bench $target" done diff --git a/build/teamcity/cockroach/ci/tests/local_roachtest_impl.sh b/build/teamcity/cockroach/ci/tests/local_roachtest_impl.sh index e7455e07d96e..a8c1f0736d83 100755 --- a/build/teamcity/cockroach/ci/tests/local_roachtest_impl.sh +++ b/build/teamcity/cockroach/ci/tests/local_roachtest_impl.sh @@ -2,12 +2,12 @@ set -euo pipefail -bazel build --config=crosslinux //pkg/cmd/cockroach-short \ +bazel build --config=crosslinux --config=ci //pkg/cmd/cockroach-short \ //pkg/cmd/roachprod \ //pkg/cmd/roachtest \ //pkg/cmd/workload -BAZEL_BIN=$(bazel info bazel-bin --config=crosslinux) +BAZEL_BIN=$(bazel info bazel-bin --config=crosslinux --config=ci) $BAZEL_BIN/pkg/cmd/roachtest/roachtest_/roachtest run acceptance kv/splits cdc/bank \ --local \ --parallelism=1 \ diff --git a/pkg/cmd/dev/build.go b/pkg/cmd/dev/build.go index c59a1e4e2e70..59d2ea05d93d 100644 --- a/pkg/cmd/dev/build.go +++ b/pkg/cmd/dev/build.go @@ -117,7 +117,7 @@ func (d *dev) build(cmd *cobra.Command, commandLine []string) error { } cross = "cross" + cross volume := mustGetFlagString(cmd, volumeFlag) - args = append(args, fmt.Sprintf("--config=%s", cross)) + args = append(args, fmt.Sprintf("--config=%s", cross), "--config=ci") dockerArgs, err := d.getDockerRunArgs(ctx, volume, false) if err != nil { return err @@ -129,7 +129,7 @@ func (d *dev) build(cmd *cobra.Command, commandLine []string) error { // TODO(ricky): Actually, we need to shell-quote the arguments, // but that's hard and I don't think it's necessary for now. script.WriteString(fmt.Sprintf("bazel %s\n", strings.Join(args, " "))) - script.WriteString(fmt.Sprintf("BAZELBIN=`bazel info bazel-bin --color=no --config=%s`\n", cross)) + script.WriteString(fmt.Sprintf("BAZELBIN=`bazel info bazel-bin --color=no --config=%s --config=ci`\n", cross)) for _, target := range buildTargets { script.WriteString(fmt.Sprintf("cp $BAZELBIN/%s /artifacts\n", bazelutil.OutputOfBinaryRule(target.fullName))) } From dc18ece3ca4a84882ecea3517b493c1e79d4001b Mon Sep 17 00:00:00 2001 From: Abhinav Garg Date: Tue, 2 Nov 2021 14:38:19 -0400 Subject: [PATCH 170/205] authors: add abhinavg6 to authors Release note: None --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 11c1878f20f4..e04c0f3717ca 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,6 +24,7 @@ Aaron Zinger Aayush Shah <@cockroachlabs.com> Abby Hersh Abbey Russell Abigail Russell +Abhinav Garg Abhishek Madan Abhemailk abhi.madan01@gmail.com Abhishek Soni Abhishek Saha AbhishekSaha From 9bba7a37ba6ec1ed4ffd224ab57bdea6c199cb2b Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Tue, 2 Nov 2021 12:59:17 -0400 Subject: [PATCH 171/205] tabledesc: fix bug involving DEFAULT exprs in MakeColumnDefDescs This commit fixes a bug in MakeColumnDefDescs where the typed expression for DEFAULT would get overwritten by the ON UPDATE expression. This would cause any sequence dependencies to not properly be tracked. Fixes #72116. Release note (bug fix): Fixes potential descriptor corruption bug for tables with a column with a DEFAULT expression referencing a SEQUENCE and with an ON UPDATE expression. --- pkg/ccl/changefeedccl/avro_test.go | 7 +- pkg/sql/add_column.go | 13 ++- pkg/sql/catalog/tabledesc/table.go | 94 +++++++++++++------ pkg/sql/crdb_internal_test.go | 3 +- pkg/sql/create_table.go | 37 ++++---- pkg/sql/create_view.go | 4 +- .../logictest/testdata/logic_test/on_update | 33 +++++++ pkg/sql/schemachanger/scbuild/table.go | 16 ++-- 8 files changed, 144 insertions(+), 63 deletions(-) diff --git a/pkg/ccl/changefeedccl/avro_test.go b/pkg/ccl/changefeedccl/avro_test.go index 42ceacae49ae..e8a3b94956bc 100644 --- a/pkg/ccl/changefeedccl/avro_test.go +++ b/pkg/ccl/changefeedccl/avro_test.go @@ -148,8 +148,11 @@ func avroFieldMetadataToColDesc(metadata string) (*descpb.ColumnDescriptor, erro def := parsed.AST.(*tree.AlterTable).Cmds[0].(*tree.AlterTableAddColumn).ColumnDef ctx := context.Background() semaCtx := makeTestSemaCtx() - col, _, _, err := tabledesc.MakeColumnDefDescs(ctx, def, &semaCtx, &tree.EvalContext{}) - return col, err + cdd, err := tabledesc.MakeColumnDefDescs(ctx, def, &semaCtx, &tree.EvalContext{}) + if err != nil { + return nil, err + } + return cdd.ColumnDescriptor, err } // randTime generates a random time.Time whose .UnixNano result doesn't diff --git a/pkg/sql/add_column.go b/pkg/sql/add_column.go index 14da1714e118..43f9f7ec2ae2 100644 --- a/pkg/sql/add_column.go +++ b/pkg/sql/add_column.go @@ -72,10 +72,12 @@ func (p *planner) addColumnImpl( } d = newDef - col, idx, expr, err := tabledesc.MakeColumnDefDescs(params.ctx, d, ¶ms.p.semaCtx, params.EvalContext()) + cdd, err := tabledesc.MakeColumnDefDescs(params.ctx, d, ¶ms.p.semaCtx, params.EvalContext()) if err != nil { return err } + col := cdd.ColumnDescriptor + idx := cdd.PrimaryKeyOrUniqueIndexDescriptor incTelemetryForNewColumn(d, col) // Ensure all new indexes are partitioned appropriately. @@ -101,9 +103,9 @@ func (p *planner) addColumnImpl( } } - // If the new column has a DEFAULT expression that uses a sequence, add references between - // its descriptor and this column descriptor. - if d.HasDefaultExpr() { + // If the new column has a DEFAULT or an ON UPDATE expression that uses a + // sequence, add references between its descriptor and this column descriptor. + if err := cdd.ForEachTypedExpr(func(expr tree.TypedExpr) error { changedSeqDescs, err := maybeAddSequenceDependencies( params.ctx, params.ExecCfg().Settings, params.p, n.tableDesc, col, expr, nil, ) @@ -117,6 +119,9 @@ func (p *planner) addColumnImpl( return err } } + return nil + }); err != nil { + return err } // We're checking to see if a user is trying add a non-nullable column without a default to a diff --git a/pkg/sql/catalog/tabledesc/table.go b/pkg/sql/catalog/tabledesc/table.go index 4e42f59244b1..2b59e2ebbe73 100644 --- a/pkg/sql/catalog/tabledesc/table.go +++ b/pkg/sql/catalog/tabledesc/table.go @@ -31,6 +31,41 @@ import ( "github.com/cockroachdb/errors" ) +// ColumnDefDescs contains the non-error return values for MakeColumnDefDescs. +type ColumnDefDescs struct { + // tree.ColumnTableDef is the column definition from which this struct is + // derived. + *tree.ColumnTableDef + + // descpb.ColumnDescriptor is the column descriptor built from the column + // definition. + *descpb.ColumnDescriptor + + // PrimaryKeyOrUniqueIndexDescriptor is the index descriptor for the index implied by a + // PRIMARY KEY or UNIQUE column. + PrimaryKeyOrUniqueIndexDescriptor *descpb.IndexDescriptor + + // DefaultExpr and OnUpdateExpr are the DEFAULT and ON UPDATE expressions, + // returned in tree.TypedExpr form for analysis, e.g. recording sequence + // dependencies. + DefaultExpr, OnUpdateExpr tree.TypedExpr +} + +// ForEachTypedExpr iterates over each typed expression in this struct. +func (cdd *ColumnDefDescs) ForEachTypedExpr(fn func(tree.TypedExpr) error) error { + if cdd.ColumnTableDef.HasDefaultExpr() { + if err := fn(cdd.DefaultExpr); err != nil { + return err + } + } + if cdd.ColumnTableDef.HasOnUpdateExpr() { + if err := fn(cdd.OnUpdateExpr); err != nil { + return err + } + } + return nil +} + // MakeColumnDefDescs creates the column descriptor for a column, as well as the // index descriptor if the column is a primary key or unique. // @@ -43,28 +78,27 @@ import ( // semaCtx can be nil if no default expression is used for the // column or during cluster bootstrapping. // -// The DEFAULT expression is returned in TypedExpr form for analysis (e.g. recording -// sequence dependencies). +// See the ColumnDefDescs definition for a description of the return values. func MakeColumnDefDescs( ctx context.Context, d *tree.ColumnTableDef, semaCtx *tree.SemaContext, evalCtx *tree.EvalContext, -) (*descpb.ColumnDescriptor, *descpb.IndexDescriptor, tree.TypedExpr, error) { +) (*ColumnDefDescs, error) { if d.IsSerial { // To the reader of this code: if control arrives here, this means // the caller has not suitably called processSerialInColumnDef() // prior to calling MakeColumnDefDescs. The dependent sequences // must be created, and the SERIAL type eliminated, prior to this // point. - return nil, nil, nil, pgerror.New(pgcode.FeatureNotSupported, + return nil, pgerror.New(pgcode.FeatureNotSupported, "SERIAL cannot be used in this context") } if len(d.CheckExprs) > 0 { // Should never happen since `HoistConstraints` moves these to table level - return nil, nil, nil, errors.New("unexpected column CHECK constraint") + return nil, errors.New("unexpected column CHECK constraint") } if d.HasFKConstraint() { // Should never happen since `HoistConstraints` moves these to table level - return nil, nil, nil, errors.New("unexpected column REFERENCED constraint") + return nil, errors.New("unexpected column REFERENCED constraint") } col := &descpb.ColumnDescriptor{ @@ -73,6 +107,10 @@ func MakeColumnDefDescs( Virtual: d.IsVirtual(), Hidden: d.Hidden, } + ret := &ColumnDefDescs{ + ColumnTableDef: d, + ColumnDescriptor: col, + } if d.GeneratedIdentity.IsGeneratedAsIdentity { switch d.GeneratedIdentity.GeneratedAsIdentityType { @@ -81,7 +119,7 @@ func MakeColumnDefDescs( case tree.GeneratedByDefault: col.GeneratedAsIdentityType = descpb.GeneratedAsIdentityType_GENERATED_BY_DEFAULT default: - return nil, nil, nil, errors.AssertionFailedf( + return nil, errors.AssertionFailedf( "column %s is of invalid generated as identity type (neither ALWAYS nor BY DEFAULT)", string(d.Name)) } if genSeqOpt := d.GeneratedIdentity.SeqOptions; genSeqOpt != nil { @@ -93,29 +131,28 @@ func MakeColumnDefDescs( // Validate and assign column type. resType, err := tree.ResolveType(ctx, d.Type, semaCtx.GetTypeResolver()) if err != nil { - return nil, nil, nil, err + return nil, err } - if err := colinfo.ValidateColumnDefType(resType); err != nil { - return nil, nil, nil, err + if err = colinfo.ValidateColumnDefType(resType); err != nil { + return nil, err } col.Type = resType - var typedExpr tree.TypedExpr if d.HasDefaultExpr() { // Verify the default expression type is compatible with the column type // and does not contain invalid functions. - var err error - if typedExpr, err = schemaexpr.SanitizeVarFreeExpr( + ret.DefaultExpr, err = schemaexpr.SanitizeVarFreeExpr( ctx, d.DefaultExpr.Expr, resType, "DEFAULT", semaCtx, tree.VolatilityVolatile, - ); err != nil { - return nil, nil, nil, err + ) + if err != nil { + return nil, err } // Keep the type checked expression so that the type annotation gets // properly stored, only if the default expression is not NULL. // Otherwise we want to keep the default expression nil. - if typedExpr != tree.DNull { - d.DefaultExpr.Expr = typedExpr + if ret.DefaultExpr != tree.DNull { + d.DefaultExpr.Expr = ret.DefaultExpr s := tree.Serialize(d.DefaultExpr.Expr) col.DefaultExpr = &s } @@ -127,21 +164,21 @@ func MakeColumnDefDescs( ctx, clusterversion.OnUpdateExpressions, ) { - return nil, nil, nil, pgerror.Newf(pgcode.FeatureNotSupported, + return nil, pgerror.Newf(pgcode.FeatureNotSupported, "version %v must be finalized to use ON UPDATE", clusterversion.ByKey(clusterversion.OnUpdateExpressions)) } // Verify the on update expression type is compatible with the column type // and does not contain invalid functions. - var err error - if typedExpr, err = schemaexpr.SanitizeVarFreeExpr( + ret.OnUpdateExpr, err = schemaexpr.SanitizeVarFreeExpr( ctx, d.OnUpdateExpr.Expr, resType, "ON UPDATE", semaCtx, tree.VolatilityVolatile, - ); err != nil { - return nil, nil, nil, err + ) + if err != nil { + return nil, err } - d.OnUpdateExpr.Expr = typedExpr + d.OnUpdateExpr.Expr = ret.OnUpdateExpr s := tree.Serialize(d.OnUpdateExpr.Expr) col.OnUpdateExpr = &s } @@ -156,10 +193,9 @@ func MakeColumnDefDescs( col.ComputeExpr = &s } - var idx *descpb.IndexDescriptor if d.PrimaryKey.IsPrimaryKey || (d.Unique.IsUnique && !d.Unique.WithoutIndex) { if !d.PrimaryKey.Sharded { - idx = &descpb.IndexDescriptor{ + ret.PrimaryKeyOrUniqueIndexDescriptor = &descpb.IndexDescriptor{ Unique: true, KeyColumnNames: []string{string(d.Name)}, KeyColumnDirections: []descpb.IndexDescriptor_Direction{descpb.IndexDescriptor_ASC}, @@ -167,10 +203,10 @@ func MakeColumnDefDescs( } else { buckets, err := EvalShardBucketCount(ctx, semaCtx, evalCtx, d.PrimaryKey.ShardBuckets) if err != nil { - return nil, nil, nil, err + return nil, err } shardColName := GetShardColumnName([]string{string(d.Name)}, buckets) - idx = &descpb.IndexDescriptor{ + ret.PrimaryKeyOrUniqueIndexDescriptor = &descpb.IndexDescriptor{ Unique: true, KeyColumnNames: []string{shardColName, string(d.Name)}, KeyColumnDirections: []descpb.IndexDescriptor_Direction{descpb.IndexDescriptor_ASC, descpb.IndexDescriptor_ASC}, @@ -183,11 +219,11 @@ func MakeColumnDefDescs( } } if d.Unique.ConstraintName != "" { - idx.Name = string(d.Unique.ConstraintName) + ret.PrimaryKeyOrUniqueIndexDescriptor.Name = string(d.Unique.ConstraintName) } } - return col, idx, typedExpr, nil + return ret, nil } // EvalShardBucketCount evaluates and checks the integer argument to a `USING HASH WITH diff --git a/pkg/sql/crdb_internal_test.go b/pkg/sql/crdb_internal_test.go index 60c69f12081c..ba521a64e39b 100644 --- a/pkg/sql/crdb_internal_test.go +++ b/pkg/sql/crdb_internal_test.go @@ -220,10 +220,11 @@ CREATE TABLE t.test (k INT); t.Fatal(err) } colDef := alterCmd.AST.(*tree.AlterTable).Cmds[0].(*tree.AlterTableAddColumn).ColumnDef - col, _, _, err := tabledesc.MakeColumnDefDescs(ctx, colDef, nil, nil) + cdd, err := tabledesc.MakeColumnDefDescs(ctx, colDef, nil, nil) if err != nil { t.Fatal(err) } + col := cdd.ColumnDescriptor col.ID = tableDesc.NextColumnID tableDesc.NextColumnID++ tableDesc.Families[0].ColumnNames = append(tableDesc.Families[0].ColumnNames, col.Name) diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index 6e25afc41e1b..90d8f741551e 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -1430,7 +1430,7 @@ func NewTableDesc( ) (*tabledesc.Mutable, error) { // Used to delay establishing Column/Sequence dependency until ColumnIDs have // been populated. - columnDefaultExprs := make([]tree.TypedExpr, len(n.Defs)) + cdd := make([]*tabledesc.ColumnDefDescs, len(n.Defs)) var opts newTableDescOptions for _, o := range inOpts { @@ -1575,7 +1575,7 @@ func NewTableDesc( maybeRegionalByRowOnUpdateExpr(evalCtx, oid), ), ) - columnDefaultExprs = append(columnDefaultExprs, nil) + cdd = append(cdd, nil) } // Construct the partitioning for the PARTITION ALL BY. @@ -1689,16 +1689,18 @@ func NewTableDesc( // It'll then be added to this table's resulting table descriptor below in // the constraint pass. n.Defs = append(n.Defs, checkConstraint) - columnDefaultExprs = append(columnDefaultExprs, nil) + cdd = append(cdd, nil) } if d.IsVirtual() && d.HasColumnFamily() { return nil, pgerror.Newf(pgcode.Syntax, "virtual columns cannot have family specifications") } - col, idx, expr, err := tabledesc.MakeColumnDefDescs(ctx, d, semaCtx, evalCtx) + cdd[i], err = tabledesc.MakeColumnDefDescs(ctx, d, semaCtx, evalCtx) if err != nil { return nil, err } + col := cdd[i].ColumnDescriptor + idx := cdd[i].PrimaryKeyOrUniqueIndexDescriptor // Do not include virtual tables in these statistics. if !descpb.IsVirtualTable(id) { @@ -1706,12 +1708,6 @@ func NewTableDesc( } desc.AddColumn(col) - if d.HasDefaultExpr() { - // This resolution must be delayed until ColumnIDs have been populated. - columnDefaultExprs[i] = expr - } else { - columnDefaultExprs[i] = nil - } if idx != nil { idx.Version = indexEncodingVersion @@ -1813,7 +1809,7 @@ func NewTableDesc( return nil, err } n.Defs = append(n.Defs, checkConstraint) - columnDefaultExprs = append(columnDefaultExprs, nil) + cdd = append(cdd, nil) } return newColumns, nil } @@ -2221,15 +2217,20 @@ func NewTableDesc( colIdx := 0 for i := range n.Defs { if _, ok := n.Defs[i].(*tree.ColumnTableDef); ok { - if expr := columnDefaultExprs[i]; expr != nil { - changedSeqDescs, err := maybeAddSequenceDependencies( - ctx, st, vt, &desc, &desc.Columns[colIdx], expr, affected) - if err != nil { + if cdd[i] != nil { + if err := cdd[i].ForEachTypedExpr(func(expr tree.TypedExpr) error { + changedSeqDescs, err := maybeAddSequenceDependencies( + ctx, st, vt, &desc, &desc.Columns[colIdx], expr, affected) + if err != nil { + return err + } + for _, changedSeqDesc := range changedSeqDescs { + affected[changedSeqDesc.ID] = changedSeqDesc + } + return nil + }); err != nil { return nil, err } - for _, changedSeqDesc := range changedSeqDescs { - affected[changedSeqDesc.ID] = changedSeqDesc - } } colIdx++ } diff --git a/pkg/sql/create_view.go b/pkg/sql/create_view.go index e70478c4d8c2..1d06a22950b1 100644 --- a/pkg/sql/create_view.go +++ b/pkg/sql/create_view.go @@ -600,11 +600,11 @@ func addResultColumns( columnTableDef.Nullable.Nullability = tree.SilentNull // The new types in the CREATE VIEW column specs never use // SERIAL so we need not process SERIAL types here. - col, _, _, err := tabledesc.MakeColumnDefDescs(ctx, &columnTableDef, semaCtx, evalCtx) + cdd, err := tabledesc.MakeColumnDefDescs(ctx, &columnTableDef, semaCtx, evalCtx) if err != nil { return err } - desc.AddColumn(col) + desc.AddColumn(cdd.ColumnDescriptor) } if err := desc.AllocateIDs(ctx); err != nil { return err diff --git a/pkg/sql/logictest/testdata/logic_test/on_update b/pkg/sql/logictest/testdata/logic_test/on_update index 2574757e25e1..1e64d5c9228d 100644 --- a/pkg/sql/logictest/testdata/logic_test/on_update +++ b/pkg/sql/logictest/testdata/logic_test/on_update @@ -279,6 +279,39 @@ pk1 3 pk2 4 pk3 2 + +# Regression test for issue #72116. +statement ok +CREATE SEQUENCE seq_72116; +CREATE TABLE table_72116 (a INT); +ALTER TABLE table_72116 ADD COLUMN b INT DEFAULT nextval('seq_72116') ON UPDATE NULL + +# Make sure that our DEFAULT has a dependency on seq_72116 +statement error pq: cannot drop sequence seq_72116 because other objects depend on it +DROP SEQUENCE seq_72116 + +statement ok +DROP TABLE table_72116; +CREATE TABLE table_72116 (a INT DEFAULT nextval('seq_72116') ON UPDATE NULL) + +statement error pq: cannot drop sequence seq_72116 because other objects depend on it +DROP SEQUENCE seq_72116 + +statement ok +DROP TABLE table_72116; +CREATE TABLE table_72116 (a INT); +ALTER TABLE table_72116 ADD COLUMN b INT DEFAULT (1) ON UPDATE nextval('seq_72116') + +statement error pq: cannot drop sequence seq_72116 because other objects depend on it +DROP SEQUENCE seq_72116 + +statement ok +DROP TABLE table_72116; +CREATE TABLE table_72116 (a INT DEFAULT (1) ON UPDATE nextval('seq_72116')) + +statement error pq: cannot drop sequence seq_72116 because other objects depend on it +DROP SEQUENCE seq_72116 + subtest EnumDependencies statement ok diff --git a/pkg/sql/schemachanger/scbuild/table.go b/pkg/sql/schemachanger/scbuild/table.go index 355683a50663..bc2c31a99878 100644 --- a/pkg/sql/schemachanger/scbuild/table.go +++ b/pkg/sql/schemachanger/scbuild/table.go @@ -108,17 +108,19 @@ func (b *buildContext) alterTableAddColumn( if d.Unique.IsUnique { panic(¬ImplementedError{n: t.ColumnDef, detail: "contains unique constraint"}) } - col, idx, defaultExpr, err := tabledesc.MakeColumnDefDescs(ctx, d, semaCtx(b), evalCtx(ctx, b)) + cdd, err := tabledesc.MakeColumnDefDescs(ctx, d, semaCtx(b), evalCtx(ctx, b)) onErrPanic(err) + col := cdd.ColumnDescriptor colID := b.nextColumnID(table) col.ID = colID - // If the new column has a DEFAULT expression that uses a sequence, add - // references between its descriptor and this column descriptor. - if d.HasDefaultExpr() { - b.maybeAddSequenceReferenceDependencies(ctx, table.GetID(), col, defaultExpr) - } + // If the new column has a DEFAULT or ON UPDATE expression that uses a + // sequence, add references between its descriptor and this column descriptor. + _ = cdd.ForEachTypedExpr(func(expr tree.TypedExpr) error { + b.maybeAddSequenceReferenceDependencies(ctx, table.GetID(), col, expr) + return nil + }) b.validateColumnName(table, d, col, t.IfNotExists) @@ -179,7 +181,7 @@ func (b *buildContext) alterTableAddColumn( }) newPrimaryIdxID := b.addOrUpdatePrimaryIndexTargetsForAddColumn(table, colID, col.Name) - if idx != nil { + if idx := cdd.PrimaryKeyOrUniqueIndexDescriptor; idx != nil { idxID := b.nextIndexID(table) idx.ID = idxID b.addNode(scpb.Target_ADD, &scpb.SecondaryIndex{ From 2d644a40ac8e33745212b3b21621d6e950e9f4ca Mon Sep 17 00:00:00 2001 From: Cameron Nunez Date: Mon, 1 Nov 2021 16:58:15 -0400 Subject: [PATCH 172/205] rpc/nodedialer: fix race condition in TestConnHealthTryDial There existed a race condition in TestConnHealthTryDial that resulted in a call to ConnHealthTryDial occasionally not returning ErrNoHeartbeat, causing an assertion failure. This patch fixes this by checking connection status using ConnHealth. Release note: None --- pkg/rpc/nodedialer/nodedialer_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/rpc/nodedialer/nodedialer_test.go b/pkg/rpc/nodedialer/nodedialer_test.go index a8858ff8b866..60e4a3151870 100644 --- a/pkg/rpc/nodedialer/nodedialer_test.go +++ b/pkg/rpc/nodedialer/nodedialer_test.go @@ -184,9 +184,11 @@ func TestConnHealthTryDial(t *testing.T) { defer stopper.Stop(ctx) nd := New(rpcCtx, newSingleNodeResolver(staticNodeID, ln.Addr())) + // Make sure no connection exists yet, via ConnHealth(). + require.Equal(t, rpc.ErrNoConnection, nd.ConnHealth(staticNodeID, rpc.DefaultClass)) + // When no connection exists, we expect ConnHealthTryDial to dial the node, // which will return ErrNoHeartbeat at first but eventually succeed. - require.Equal(t, rpc.ErrNotHeartbeated, nd.ConnHealthTryDial(staticNodeID, rpc.DefaultClass)) require.Eventually(t, func() bool { return nd.ConnHealthTryDial(staticNodeID, rpc.DefaultClass) == nil }, time.Second, 10*time.Millisecond) From 1ea762eee6767cff78d0ad4d50639314a4adfd41 Mon Sep 17 00:00:00 2001 From: Ricky Stewart Date: Tue, 2 Nov 2021 14:53:07 -0500 Subject: [PATCH 173/205] bazel: increase test size of `rttanalysisccl` test Prevent timeouts in CI. Release note: None --- pkg/ccl/benchccl/rttanalysisccl/BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ccl/benchccl/rttanalysisccl/BUILD.bazel b/pkg/ccl/benchccl/rttanalysisccl/BUILD.bazel index 991a914aedc9..a4a2fc5eda82 100644 --- a/pkg/ccl/benchccl/rttanalysisccl/BUILD.bazel +++ b/pkg/ccl/benchccl/rttanalysisccl/BUILD.bazel @@ -19,7 +19,7 @@ go_library( go_test( name = "rttanalysisccl_test", - size = "large", + size = "enormous", srcs = [ "bench_test.go", "multi_region_bench_test.go", From b16e747c423d081143f9d35f1239afda38a7338f Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 1 Nov 2021 01:05:29 -0400 Subject: [PATCH 174/205] ccl/sqlproxyccl: provide hints to the user whenever params parsing fails Previously, whenever params parsing fails during connection startup, an error message is returned without any hints. Users often get confused on what to do next as the message can be ambiguous and provide no action items. This led to a lot of confusion among end users connecting through the sqlproxy, which can be seen in the recent posts in community forums. This commit updates the error messages to also return hints, and provide users with more information. At the same time, we also relabeled "cluster name" to "cluster identifier", which correctly reflects the format that we're using at the moment: -. Release note: None --- pkg/ccl/sqlproxyccl/BUILD.bazel | 1 + pkg/ccl/sqlproxyccl/proxy.go | 2 + pkg/ccl/sqlproxyccl/proxy_handler.go | 52 +++++++++++++++--- pkg/ccl/sqlproxyccl/proxy_handler_test.go | 66 ++++++++++++++--------- 4 files changed, 89 insertions(+), 32 deletions(-) diff --git a/pkg/ccl/sqlproxyccl/BUILD.bazel b/pkg/ccl/sqlproxyccl/BUILD.bazel index c156cb81fd6c..b8e3002f07cc 100644 --- a/pkg/ccl/sqlproxyccl/BUILD.bazel +++ b/pkg/ccl/sqlproxyccl/BUILD.bazel @@ -68,6 +68,7 @@ go_test( "//pkg/server", "//pkg/sql", "//pkg/sql/pgwire", + "//pkg/sql/pgwire/pgerror", "//pkg/testutils", "//pkg/testutils/serverutils", "//pkg/testutils/skip", diff --git a/pkg/ccl/sqlproxyccl/proxy.go b/pkg/ccl/sqlproxyccl/proxy.go index fe09dcf7fb57..ff815f17ca99 100644 --- a/pkg/ccl/sqlproxyccl/proxy.go +++ b/pkg/ccl/sqlproxyccl/proxy.go @@ -54,10 +54,12 @@ func toPgError(err error) *pgproto3.ErrorResponse { } else { pgCode = "08004" // rejected connection } + return &pgproto3.ErrorResponse{ Severity: "FATAL", Code: pgCode, Message: msg, + Hint: errors.FlattenHints(codeErr.err), } } // Return a generic "internal server error" message. diff --git a/pkg/ccl/sqlproxyccl/proxy_handler.go b/pkg/ccl/sqlproxyccl/proxy_handler.go index ad6a5e0b0940..69485bd3f952 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler.go @@ -589,11 +589,15 @@ func clusterNameAndTenantFromParams( } if clusterNameFromDB == "" && clusterNameFromOpt == "" { - return msg, "", roachpb.MaxTenantID, errors.New("missing cluster name in connection string") + err := errors.New("missing cluster identifier") + err = errors.WithHint(err, strings.TrimLeft(clusterIdentifierHint, "\n")) + return msg, "", roachpb.MaxTenantID, err } if clusterNameFromDB != "" && clusterNameFromOpt != "" { - return msg, "", roachpb.MaxTenantID, errors.New("multiple cluster names provided") + err := errors.New("multiple cluster identifiers provided") + err = errors.WithHint(err, strings.TrimLeft(clusterIdentifierHint, "\n")) + return msg, "", roachpb.MaxTenantID, err } if clusterNameFromDB == "" { @@ -602,27 +606,42 @@ func clusterNameAndTenantFromParams( sepIdx := strings.LastIndex(clusterNameFromDB, clusterTenantSep) - // Cluster name provided without a tenant ID in the end. + // Cluster identifier provided without a tenant ID in the end. if sepIdx == -1 || sepIdx == len(clusterNameFromDB)-1 { - return msg, "", roachpb.MaxTenantID, errors.Errorf("invalid cluster name '%s'", clusterNameFromDB) + err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) + err = errors.WithHint(err, missingTenantIDHint) + err = errors.WithHint(err, clusterNameFormHint) + return msg, "", roachpb.MaxTenantID, err } clusterNameSansTenant, tenantIDStr := clusterNameFromDB[:sepIdx], clusterNameFromDB[sepIdx+1:] + + // Cluster name does not conform to the specified format. if !clusterNameRegex.MatchString(clusterNameSansTenant) { - return msg, "", roachpb.MaxTenantID, errors.Errorf("invalid cluster name '%s'", clusterNameFromDB) + err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) + err = errors.WithHintf(err, "is '%s' a valid cluster name?", clusterNameSansTenant) + err = errors.WithHint(err, clusterNameFormHint) + return msg, "", roachpb.MaxTenantID, err } + // Tenant ID cannot be parsed. tenID, err := strconv.ParseUint(tenantIDStr, 10, 64) if err != nil { // Log these non user-facing errors. log.Errorf(ctx, "cannot parse tenant ID in %s: %v", clusterNameFromDB, err) - return msg, "", roachpb.MaxTenantID, errors.Errorf("invalid cluster name '%s'", clusterNameFromDB) + err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) + err = errors.WithHintf(err, "is '%s' a valid tenant ID?", tenantIDStr) + err = errors.WithHint(err, clusterNameFormHint) + return msg, "", roachpb.MaxTenantID, err } + // This case only happens if tenID is 0 or 1 (system tenant). if tenID < roachpb.MinTenantID.ToUint64() { // Log these non user-facing errors. log.Errorf(ctx, "%s contains an invalid tenant ID", clusterNameFromDB) - return msg, "", roachpb.MaxTenantID, errors.Errorf("invalid cluster name '%s'", clusterNameFromDB) + err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) + err = errors.WithHintf(err, "tenant ID %d is invalid", tenID) + return msg, "", roachpb.MaxTenantID, err } // Make and return a copy of the startup msg so the original is not modified. @@ -717,3 +736,22 @@ func parseOptionsParam(optionsParam string) (clusterName, newOptionsParam string newOptionsParam = strings.TrimSpace(newOptionsParam) return matches[0][1], newOptionsParam, nil } + +const clusterIdentifierHint = ` +ensure that your cluster identifier is specified through only one of the following methods: + +1) Database parameter: + Use "." as the database parameter. + (e.g. database="active-roach-42.defaultdb") + +2) Options parameter: + Use "--cluster=" as the options parameter. + (e.g. options="--cluster=active-roach-42") + +For more details, please visit our docs site at: + https://www.cockroachlabs.com/docs/cockroachcloud/connect-to-a-serverless-cluster +` + +const clusterNameFormHint = "Cluster identifiers come in the form of - (e.g. lazy-roach-3)" + +const missingTenantIDHint = "did you forget to include your tenant ID in the cluster identifier?" diff --git a/pkg/ccl/sqlproxyccl/proxy_handler_test.go b/pkg/ccl/sqlproxyccl/proxy_handler_test.go index 00c9ab787d63..8019a8301791 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler_test.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler_test.go @@ -30,6 +30,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/server" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/pgwire" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/skip" @@ -142,21 +143,21 @@ func TestFailedConnection(t *testing.T) { // TenantID rejected as malformed. te.TestConnectErr( ctx, t, u+"?options=--cluster=dimdog&sslmode="+sslmode, - codeParamsRoutingFailed, "invalid cluster name 'dimdog'", + codeParamsRoutingFailed, "invalid cluster identifier 'dimdog'", ) require.Equal(t, int64(1+(i*3)), s.metrics.RoutingErrCount.Count()) // No cluster name and TenantID. te.TestConnectErr( ctx, t, u+"?sslmode="+sslmode, - codeParamsRoutingFailed, "missing cluster name in connection string", + codeParamsRoutingFailed, "missing cluster identifier", ) require.Equal(t, int64(2+(i*3)), s.metrics.RoutingErrCount.Count()) // Bad TenantID. Ensure that we don't leak any parsing errors. te.TestConnectErr( ctx, t, u+"?options=--cluster=dim-dog-foo3&sslmode="+sslmode, - codeParamsRoutingFailed, "invalid cluster name 'dim-dog-foo3'", + codeParamsRoutingFailed, "invalid cluster identifier 'dim-dog-foo3'", ) require.Equal(t, int64(3+(i*3)), s.metrics.RoutingErrCount.Count()) } @@ -742,51 +743,58 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { expectedTenantID uint64 expectedParams map[string]string expectedError string + expectedHint string }{ { name: "empty params", params: map[string]string{}, - expectedError: "missing cluster name in connection string", + expectedError: "missing cluster identifier", + expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), }, { - name: "cluster name is not provided", + name: "cluster identifier is not provided", params: map[string]string{ "database": "defaultdb", "options": "--foo=bar", }, - expectedError: "missing cluster name in connection string", + expectedError: "missing cluster identifier", + expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), }, { - name: "multiple similar cluster names", + name: "multiple similar cluster identifiers", params: map[string]string{ "database": "happy-koala-7.defaultdb", "options": "--cluster=happy-koala", }, - expectedError: "multiple cluster names provided", + expectedError: "multiple cluster identifiers provided", + expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), }, { - name: "multiple different cluster names", + name: "multiple different cluster identifiers", params: map[string]string{ "database": "happy-koala-7.defaultdb", "options": "--cluster=happy-tiger", }, - expectedError: "multiple cluster names provided", + expectedError: "multiple cluster identifiers provided", + expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), }, { - name: "invalid cluster name in database param", + name: "invalid cluster identifier in database param", params: map[string]string{ // Cluster names need to be between 6 to 20 alphanumeric characters. "database": "short-0.defaultdb", }, - expectedError: "invalid cluster name 'short-0'", + expectedError: "invalid cluster identifier 'short-0'", + expectedHint: "is 'short' a valid cluster name?\n--\n" + clusterNameFormHint, }, { - name: "invalid cluster name in options param", + name: "invalid cluster identifier in options param", params: map[string]string{ // Cluster names need to be between 6 to 20 alphanumeric characters. "options": "--cluster=cockroachlabsdotcomfoobarbaz-0", }, - expectedError: "invalid cluster name 'cockroachlabsdotcomfoobarbaz-0'", + expectedError: "invalid cluster identifier 'cockroachlabsdotcomfoobarbaz-0'", + expectedHint: "is 'cockroachlabsdotcomfoobarbaz' a valid cluster name?\n--\n" + clusterNameFormHint, }, { name: "invalid database param (1)", @@ -827,30 +835,35 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { { name: "no tenant id", params: map[string]string{"database": "happy2koala.defaultdb"}, - expectedError: "invalid cluster name 'happy2koala'", + expectedError: "invalid cluster identifier 'happy2koala'", + expectedHint: missingTenantIDHint + "\n--\n" + clusterNameFormHint, }, { name: "missing tenant id", params: map[string]string{"database": "happy2koala-.defaultdb"}, - expectedError: "invalid cluster name 'happy2koala-'", + expectedError: "invalid cluster identifier 'happy2koala-'", + expectedHint: missingTenantIDHint + "\n--\n" + clusterNameFormHint, }, { name: "missing cluster name", params: map[string]string{"database": "-7.defaultdb"}, - expectedError: "invalid cluster name '-7'", + expectedError: "invalid cluster identifier '-7'", + expectedHint: "is '' a valid cluster name?\n--\n" + clusterNameFormHint, }, { name: "bad tenant id", params: map[string]string{"database": "happy-koala-0-7a.defaultdb"}, - expectedError: "invalid cluster name 'happy-koala-0-7a'", + expectedError: "invalid cluster identifier 'happy-koala-0-7a'", + expectedHint: "is '7a' a valid tenant ID?\n--\n" + clusterNameFormHint, }, { name: "zero tenant id", params: map[string]string{"database": "happy-koala-0.defaultdb"}, - expectedError: "invalid cluster name 'happy-koala-0'", + expectedError: "invalid cluster identifier 'happy-koala-0'", + expectedHint: "tenant ID 0 is invalid", }, { - name: "cluster name in database param", + name: "cluster identifier in database param", params: map[string]string{ "database": "happy-koala-7.defaultdb", "foo": "bar", @@ -860,7 +873,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { expectedParams: map[string]string{"database": "defaultdb", "foo": "bar"}, }, { - name: "valid cluster name with invalid arrangements", + name: "valid cluster identifier with invalid arrangements", params: map[string]string{ "database": "defaultdb", "options": "-c --cluster=happy-koala-7 -c -c -c", @@ -873,7 +886,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { }, }, { - name: "short option: cluster name in options param", + name: "short option: cluster identifier in options param", params: map[string]string{ "database": "defaultdb", "options": "-ccluster=happy-koala-7", @@ -883,7 +896,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { expectedParams: map[string]string{"database": "defaultdb"}, }, { - name: "short option with spaces: cluster name in options param", + name: "short option with spaces: cluster identifier in options param", params: map[string]string{ "database": "defaultdb", "options": "-c cluster=happy-koala-7", @@ -893,7 +906,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { expectedParams: map[string]string{"database": "defaultdb"}, }, { - name: "long option: cluster name in options param", + name: "long option: cluster identifier in options param", params: map[string]string{ "database": "defaultdb", "options": "--cluster=happy-koala-7\t--foo=test", @@ -906,7 +919,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { }, }, { - name: "long option: cluster name in options param with other options", + name: "long option: cluster identifier in options param with other options", params: map[string]string{ "database": "defaultdb", "options": "-csearch_path=public --cluster=happy-koala-7\t--foo=test", @@ -946,6 +959,9 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { require.Equal(t, tc.expectedParams, outMsg.Parameters) } else { require.EqualErrorf(t, err, tc.expectedError, "failed test case\n%+v", tc) + + pgerr := pgerror.Flatten(err) + require.Equal(t, tc.expectedHint, pgerr.Hint) } // Check that the original parameters were not modified. From 388d4fcab8ef85509eaa6f0d88e0311c8857d188 Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 1 Nov 2021 23:32:09 -0400 Subject: [PATCH 175/205] ccl/sqlproxyccl: allow multiple cluster identifiers provided they are unique Previously, the proxy will return an error if multiple cluster identifiers were specified in both the database and options parameters, regardless of whether they are different or unique. Ideally we should only return an error if the supplied cluster identifiers are non-ambiguous. We went with the constraint earlier to simplify things, but thinking about it again, it seems fine to specify cluster identifiers through both parameters provided that they are unique. This commit changes the behavior of the proxy such that we now allow multiple cluster identifiers to be supplied within the connection string provided that they are unique so that the proxy knows which cluster the connection is for. Release note: None --- pkg/ccl/sqlproxyccl/proxy_handler.go | 11 ++++++++--- pkg/ccl/sqlproxyccl/proxy_handler_test.go | 24 ++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/pkg/ccl/sqlproxyccl/proxy_handler.go b/pkg/ccl/sqlproxyccl/proxy_handler.go index 69485bd3f952..f584fc16994e 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler.go @@ -594,8 +594,12 @@ func clusterNameAndTenantFromParams( return msg, "", roachpb.MaxTenantID, err } - if clusterNameFromDB != "" && clusterNameFromOpt != "" { - err := errors.New("multiple cluster identifiers provided") + if clusterNameFromDB != "" && clusterNameFromOpt != "" && + clusterNameFromDB != clusterNameFromOpt { + err := errors.New("multiple different cluster identifiers provided") + err = errors.WithHintf(err, + "is '%s' or '%s' the identifier for the cluster that you're connecting to?", + clusterNameFromDB, clusterNameFromOpt) err = errors.WithHint(err, strings.TrimLeft(clusterIdentifierHint, "\n")) return msg, "", roachpb.MaxTenantID, err } @@ -738,7 +742,8 @@ func parseOptionsParam(optionsParam string) (clusterName, newOptionsParam string } const clusterIdentifierHint = ` -ensure that your cluster identifier is specified through only one of the following methods: +Ensure that your cluster identifier is uniquely specified using any of the +following methods: 1) Database parameter: Use "." as the database parameter. diff --git a/pkg/ccl/sqlproxyccl/proxy_handler_test.go b/pkg/ccl/sqlproxyccl/proxy_handler_test.go index 8019a8301791..2190c22e1e77 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler_test.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler_test.go @@ -760,23 +760,15 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { expectedError: "missing cluster identifier", expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), }, - { - name: "multiple similar cluster identifiers", - params: map[string]string{ - "database": "happy-koala-7.defaultdb", - "options": "--cluster=happy-koala", - }, - expectedError: "multiple cluster identifiers provided", - expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), - }, { name: "multiple different cluster identifiers", params: map[string]string{ "database": "happy-koala-7.defaultdb", "options": "--cluster=happy-tiger", }, - expectedError: "multiple cluster identifiers provided", - expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), + expectedError: "multiple different cluster identifiers provided", + expectedHint: "is 'happy-koala-7' or 'happy-tiger' the identifier for the cluster that you're connecting to?\n--\n" + + strings.TrimLeft(clusterIdentifierHint, "\n"), }, { name: "invalid cluster identifier in database param", @@ -862,6 +854,16 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { expectedError: "invalid cluster identifier 'happy-koala-0'", expectedHint: "tenant ID 0 is invalid", }, + { + name: "multiple similar cluster identifiers", + params: map[string]string{ + "database": "happy-koala-7.defaultdb", + "options": "--cluster=happy-koala-7", + }, + expectedClusterName: "happy-koala", + expectedTenantID: 7, + expectedParams: map[string]string{"database": "defaultdb"}, + }, { name: "cluster identifier in database param", params: map[string]string{ From 3b1262bae4c3211bfe53a0b4a8e30cc23d4af51e Mon Sep 17 00:00:00 2001 From: Jay Date: Mon, 1 Nov 2021 23:47:54 -0400 Subject: [PATCH 176/205] ccl/sqlproxyccl: relabel variables and comments to reflect cluster identifiers Previously, we used the term "cluster name" in two different ways, one with the tenant ID, and one without. This can be really confusing to readers who are not familiar with the sqlproxy code. Concretely, cluster name is actually the string without the tenant ID. The combination of cluster name and tenant ID is known as the cluster identifier. This commit is purely mechanical to fix comments and relabel variables. Release note: None --- pkg/ccl/sqlproxyccl/BUILD.bazel | 1 + pkg/ccl/sqlproxyccl/proxy.go | 7 +- pkg/ccl/sqlproxyccl/proxy_handler.go | 123 ++++++++++++---------- pkg/ccl/sqlproxyccl/proxy_handler_test.go | 18 ++-- 4 files changed, 79 insertions(+), 70 deletions(-) diff --git a/pkg/ccl/sqlproxyccl/BUILD.bazel b/pkg/ccl/sqlproxyccl/BUILD.bazel index b8e3002f07cc..09a67468efb0 100644 --- a/pkg/ccl/sqlproxyccl/BUILD.bazel +++ b/pkg/ccl/sqlproxyccl/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "//pkg/ccl/sqlproxyccl/throttler", "//pkg/roachpb:with-mocks", "//pkg/security/certmgr", + "//pkg/sql/pgwire/pgcode", "//pkg/util/contextutil", "//pkg/util/grpcutil", "//pkg/util/httputil", diff --git a/pkg/ccl/sqlproxyccl/proxy.go b/pkg/ccl/sqlproxyccl/proxy.go index ff815f17ca99..00ac6de13dfa 100644 --- a/pkg/ccl/sqlproxyccl/proxy.go +++ b/pkg/ccl/sqlproxyccl/proxy.go @@ -12,6 +12,7 @@ import ( "io" "net" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" "github.com/cockroachdb/errors" "github.com/jackc/pgproto3/v2" ) @@ -50,9 +51,9 @@ func toPgError(err error) *pgproto3.ErrorResponse { var pgCode string if codeErr.code == codeIdleDisconnect { - pgCode = "57P01" // admin shutdown + pgCode = pgcode.AdminShutdown.String() } else { - pgCode = "08004" // rejected connection + pgCode = pgcode.SQLserverRejectedEstablishmentOfSQLconnection.String() } return &pgproto3.ErrorResponse{ @@ -65,7 +66,7 @@ func toPgError(err error) *pgproto3.ErrorResponse { // Return a generic "internal server error" message. return &pgproto3.ErrorResponse{ Severity: "FATAL", - Code: "08004", // rejected connection + Code: pgcode.SQLserverRejectedEstablishmentOfSQLconnection.String(), Message: "internal server error", } } diff --git a/pkg/ccl/sqlproxyccl/proxy_handler.go b/pkg/ccl/sqlproxyccl/proxy_handler.go index f584fc16994e..cff6c9fd7f8e 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler.go @@ -42,7 +42,7 @@ var ( // Unlike the original spec, this does not handle escaping rules. // // See "options" in https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS. - clusterNameLongOptionRE = regexp.MustCompile(`(?:-c\s*|--)cluster=([\S]*)`) + clusterIdentifierLongOptionRE = regexp.MustCompile(`(?:-c\s*|--)cluster=([\S]*)`) // clusterNameRegex restricts cluster names to have between 6 and 20 // alphanumeric characters, with dashes allowed within the name (but not as a @@ -51,10 +51,9 @@ var ( ) const ( - // Cluster identifier is in the form "clustername-. Tenant id is - // always in the end but the cluster name can also contain '-' or digits. - // For example: - // "foo-7-10" -> cluster name is "foo-7" and tenant id is 10. + // Cluster identifier is in the form "-. Tenant ID + // is always in the end but the cluster name can also contain '-' or digits. + // (e.g. In "foo-7-10", cluster name is "foo-7" and tenant ID is "10") clusterTenantSep = "-" ) @@ -566,64 +565,69 @@ var reportFailureToDirectory = func( return directory.ReportFailure(ctx, tenantID, addr) } -// clusterNameAndTenantFromParams extracts the cluster name from the connection -// parameters, and rewrites the database param, if necessary. We currently -// support embedding the cluster name in two ways: -// - Within the database param (e.g. "happy-koala.defaultdb") +// clusterNameAndTenantFromParams extracts the cluster name and tenant ID from +// the connection parameters, and rewrites the database and options parameters, +// if necessary. // -// - Within the options param (e.g. "... --cluster=happy-koala ..."). +// We currently support embedding the cluster identifier in two ways: +// +// - Within the database param (e.g. "happy-koala-3.defaultdb") +// +// - Within the options param (e.g. "... --cluster=happy-koala-5 ..."). // PostgreSQL supports three different ways to set a run-time parameter // through its command-line options, i.e. "-c NAME=VALUE", "-cNAME=VALUE", and // "--NAME=VALUE". func clusterNameAndTenantFromParams( ctx context.Context, msg *pgproto3.StartupMessage, ) (*pgproto3.StartupMessage, string, roachpb.TenantID, error) { - clusterNameFromDB, databaseName, err := parseDatabaseParam(msg.Parameters["database"]) + clusterIdentifierDB, databaseName, err := parseDatabaseParam(msg.Parameters["database"]) if err != nil { return msg, "", roachpb.MaxTenantID, err } - clusterNameFromOpt, newOptionsParam, err := parseOptionsParam(msg.Parameters["options"]) + clusterIdentifierOpt, newOptionsParam, err := parseOptionsParam(msg.Parameters["options"]) if err != nil { return msg, "", roachpb.MaxTenantID, err } - if clusterNameFromDB == "" && clusterNameFromOpt == "" { + // No cluster identifiers were specified. + if clusterIdentifierDB == "" && clusterIdentifierOpt == "" { err := errors.New("missing cluster identifier") - err = errors.WithHint(err, strings.TrimLeft(clusterIdentifierHint, "\n")) + err = errors.WithHint(err, clusterIdentifierHint) return msg, "", roachpb.MaxTenantID, err } - if clusterNameFromDB != "" && clusterNameFromOpt != "" && - clusterNameFromDB != clusterNameFromOpt { + // Ambiguous cluster identifiers. + if clusterIdentifierDB != "" && clusterIdentifierOpt != "" && + clusterIdentifierDB != clusterIdentifierOpt { err := errors.New("multiple different cluster identifiers provided") err = errors.WithHintf(err, - "is '%s' or '%s' the identifier for the cluster that you're connecting to?", - clusterNameFromDB, clusterNameFromOpt) - err = errors.WithHint(err, strings.TrimLeft(clusterIdentifierHint, "\n")) + "Is '%s' or '%s' the identifier for the cluster that you're connecting to?", + clusterIdentifierDB, clusterIdentifierOpt) + err = errors.WithHint(err, clusterIdentifierHint) return msg, "", roachpb.MaxTenantID, err } - if clusterNameFromDB == "" { - clusterNameFromDB = clusterNameFromOpt + if clusterIdentifierDB == "" { + clusterIdentifierDB = clusterIdentifierOpt } - sepIdx := strings.LastIndex(clusterNameFromDB, clusterTenantSep) + sepIdx := strings.LastIndex(clusterIdentifierDB, clusterTenantSep) // Cluster identifier provided without a tenant ID in the end. - if sepIdx == -1 || sepIdx == len(clusterNameFromDB)-1 { - err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) + if sepIdx == -1 || sepIdx == len(clusterIdentifierDB)-1 { + err := errors.Errorf("invalid cluster identifier '%s'", clusterIdentifierDB) err = errors.WithHint(err, missingTenantIDHint) err = errors.WithHint(err, clusterNameFormHint) return msg, "", roachpb.MaxTenantID, err } - clusterNameSansTenant, tenantIDStr := clusterNameFromDB[:sepIdx], clusterNameFromDB[sepIdx+1:] + clusterName, tenantIDStr := clusterIdentifierDB[:sepIdx], clusterIdentifierDB[sepIdx+1:] - // Cluster name does not conform to the specified format. - if !clusterNameRegex.MatchString(clusterNameSansTenant) { - err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) - err = errors.WithHintf(err, "is '%s' a valid cluster name?", clusterNameSansTenant) + // Cluster name does not conform to the expected format (e.g. too short). + if !clusterNameRegex.MatchString(clusterName) { + err := errors.Errorf("invalid cluster identifier '%s'", clusterIdentifierDB) + err = errors.WithHintf(err, "Is '%s' a valid cluster name?", clusterName) err = errors.WithHint(err, clusterNameFormHint) return msg, "", roachpb.MaxTenantID, err } @@ -632,9 +636,9 @@ func clusterNameAndTenantFromParams( tenID, err := strconv.ParseUint(tenantIDStr, 10, 64) if err != nil { // Log these non user-facing errors. - log.Errorf(ctx, "cannot parse tenant ID in %s: %v", clusterNameFromDB, err) - err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) - err = errors.WithHintf(err, "is '%s' a valid tenant ID?", tenantIDStr) + log.Errorf(ctx, "cannot parse tenant ID in %s: %v", clusterIdentifierDB, err) + err := errors.Errorf("invalid cluster identifier '%s'", clusterIdentifierDB) + err = errors.WithHintf(err, "Is '%s' a valid tenant ID?", tenantIDStr) err = errors.WithHint(err, clusterNameFormHint) return msg, "", roachpb.MaxTenantID, err } @@ -642,13 +646,14 @@ func clusterNameAndTenantFromParams( // This case only happens if tenID is 0 or 1 (system tenant). if tenID < roachpb.MinTenantID.ToUint64() { // Log these non user-facing errors. - log.Errorf(ctx, "%s contains an invalid tenant ID", clusterNameFromDB) - err := errors.Errorf("invalid cluster identifier '%s'", clusterNameFromDB) - err = errors.WithHintf(err, "tenant ID %d is invalid", tenID) + log.Errorf(ctx, "%s contains an invalid tenant ID", clusterIdentifierDB) + err := errors.Errorf("invalid cluster identifier '%s'", clusterIdentifierDB) + err = errors.WithHintf(err, "Tenant ID %d is invalid.", tenID) return msg, "", roachpb.MaxTenantID, err } // Make and return a copy of the startup msg so the original is not modified. + // We will rewrite database and options in the new startup message. paramsOut := map[string]string{} for key, value := range msg.Parameters { if key == "database" { @@ -661,20 +666,21 @@ func clusterNameAndTenantFromParams( paramsOut[key] = value } } + outMsg := &pgproto3.StartupMessage{ ProtocolVersion: msg.ProtocolVersion, Parameters: paramsOut, } - - return outMsg, clusterNameSansTenant, roachpb.MakeTenantID(tenID), nil + return outMsg, clusterName, roachpb.MakeTenantID(tenID), nil } // parseDatabaseParam parses the database parameter from the PG connection -// string, and tries to extract the cluster name if present. The cluster -// name should be embedded in the database parameter using the dot (".") -// delimiter in the form of ".". This approach -// is safe because dots are not allowed in the database names themselves. -func parseDatabaseParam(databaseParam string) (clusterName, databaseName string, err error) { +// string, and tries to extract the cluster identifier if present. The cluster +// identifier should be embedded in the database parameter using the dot (".") +// delimiter in the form of ".". This +// approach is safe because dots are not allowed in the database names +// themselves. +func parseDatabaseParam(databaseParam string) (clusterIdentifier, databaseName string, err error) { // Database param is not provided. if databaseParam == "" { return "", "", nil @@ -687,21 +693,21 @@ func parseDatabaseParam(databaseParam string) (clusterName, databaseName string, return "", databaseParam, nil } - clusterName, databaseName = parts[0], parts[1] + clusterIdentifier, databaseName = parts[0], parts[1] // Ensure that the param is in the right format if the delimiter is provided. - if len(parts) > 2 || clusterName == "" || databaseName == "" { + if len(parts) > 2 || clusterIdentifier == "" || databaseName == "" { return "", "", errors.New("invalid database param") } - return clusterName, databaseName, nil + return clusterIdentifier, databaseName, nil } // parseOptionsParam parses the options parameter from the PG connection string, -// and tries to return the cluster name if present. It also returns the options -// parameter with the cluster name stripped out. Just like PostgreSQL, the -// sqlproxy supports three different ways to set a run-time parameter through -// its command-line options: +// and tries to return the cluster identifier if present. It also returns the +// options parameter with the cluster key stripped out. Just like PostgreSQL, +// the sqlproxy supports three different ways to set a run-time parameter +// through its command-line options: // -c NAME=VALUE (commonly used throughout documentation around PGOPTIONS) // -cNAME=VALUE // --NAME=VALUE @@ -711,22 +717,23 @@ func parseDatabaseParam(databaseParam string) (clusterName, databaseName string, // parse this, we need to traverse the string from left to right, and look at // every single argument, but that involves quite a bit of work, so we'll punt // for now. -func parseOptionsParam(optionsParam string) (clusterName, newOptionsParam string, err error) { +func parseOptionsParam(optionsParam string) (clusterIdentifier, newOptionsParam string, err error) { // Only search up to 2 in case of large inputs. - matches := clusterNameLongOptionRE.FindAllStringSubmatch(optionsParam, 2 /* n */) + matches := clusterIdentifierLongOptionRE.FindAllStringSubmatch(optionsParam, 2 /* n */) if len(matches) == 0 { return "", optionsParam, nil } if len(matches) > 1 { // Technically we could still allow requests to go through if all - // cluster names match, but we don't want to parse the entire string, so - // we will just error out if at least two cluster flags are provided. + // cluster identifiers match, but we don't want to parse the entire + // string, so we will just error out if at least two cluster flags are + // provided. return "", "", errors.New("multiple cluster flags provided") } // Length of each match should always be 2 with the given regex, one for - // the full string, and the other for the cluster name. + // the full string, and the other for the cluster identifier. if len(matches[0]) != 2 { // We don't want to panic here. return "", "", errors.New("internal server error") @@ -736,13 +743,13 @@ func parseOptionsParam(optionsParam string) (clusterName, newOptionsParam string if len(matches[0][1]) == 0 { return "", "", errors.New("invalid cluster flag") } + newOptionsParam = strings.ReplaceAll(optionsParam, matches[0][0], "") newOptionsParam = strings.TrimSpace(newOptionsParam) return matches[0][1], newOptionsParam, nil } -const clusterIdentifierHint = ` -Ensure that your cluster identifier is uniquely specified using any of the +const clusterIdentifierHint = `Ensure that your cluster identifier is uniquely specified using any of the following methods: 1) Database parameter: @@ -757,6 +764,6 @@ For more details, please visit our docs site at: https://www.cockroachlabs.com/docs/cockroachcloud/connect-to-a-serverless-cluster ` -const clusterNameFormHint = "Cluster identifiers come in the form of - (e.g. lazy-roach-3)" +const clusterNameFormHint = "Cluster identifiers come in the form of - (e.g. lazy-roach-3)." -const missingTenantIDHint = "did you forget to include your tenant ID in the cluster identifier?" +const missingTenantIDHint = "Did you forget to include your tenant ID in the cluster identifier?" diff --git a/pkg/ccl/sqlproxyccl/proxy_handler_test.go b/pkg/ccl/sqlproxyccl/proxy_handler_test.go index 2190c22e1e77..b48c0d53d6bf 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler_test.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler_test.go @@ -749,7 +749,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { name: "empty params", params: map[string]string{}, expectedError: "missing cluster identifier", - expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), + expectedHint: clusterIdentifierHint, }, { name: "cluster identifier is not provided", @@ -758,7 +758,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { "options": "--foo=bar", }, expectedError: "missing cluster identifier", - expectedHint: strings.TrimLeft(clusterIdentifierHint, "\n"), + expectedHint: clusterIdentifierHint, }, { name: "multiple different cluster identifiers", @@ -767,8 +767,8 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { "options": "--cluster=happy-tiger", }, expectedError: "multiple different cluster identifiers provided", - expectedHint: "is 'happy-koala-7' or 'happy-tiger' the identifier for the cluster that you're connecting to?\n--\n" + - strings.TrimLeft(clusterIdentifierHint, "\n"), + expectedHint: "Is 'happy-koala-7' or 'happy-tiger' the identifier for the cluster that you're connecting to?\n--\n" + + clusterIdentifierHint, }, { name: "invalid cluster identifier in database param", @@ -777,7 +777,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { "database": "short-0.defaultdb", }, expectedError: "invalid cluster identifier 'short-0'", - expectedHint: "is 'short' a valid cluster name?\n--\n" + clusterNameFormHint, + expectedHint: "Is 'short' a valid cluster name?\n--\n" + clusterNameFormHint, }, { name: "invalid cluster identifier in options param", @@ -786,7 +786,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { "options": "--cluster=cockroachlabsdotcomfoobarbaz-0", }, expectedError: "invalid cluster identifier 'cockroachlabsdotcomfoobarbaz-0'", - expectedHint: "is 'cockroachlabsdotcomfoobarbaz' a valid cluster name?\n--\n" + clusterNameFormHint, + expectedHint: "Is 'cockroachlabsdotcomfoobarbaz' a valid cluster name?\n--\n" + clusterNameFormHint, }, { name: "invalid database param (1)", @@ -840,19 +840,19 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { name: "missing cluster name", params: map[string]string{"database": "-7.defaultdb"}, expectedError: "invalid cluster identifier '-7'", - expectedHint: "is '' a valid cluster name?\n--\n" + clusterNameFormHint, + expectedHint: "Is '' a valid cluster name?\n--\n" + clusterNameFormHint, }, { name: "bad tenant id", params: map[string]string{"database": "happy-koala-0-7a.defaultdb"}, expectedError: "invalid cluster identifier 'happy-koala-0-7a'", - expectedHint: "is '7a' a valid tenant ID?\n--\n" + clusterNameFormHint, + expectedHint: "Is '7a' a valid tenant ID?\n--\n" + clusterNameFormHint, }, { name: "zero tenant id", params: map[string]string{"database": "happy-koala-0.defaultdb"}, expectedError: "invalid cluster identifier 'happy-koala-0'", - expectedHint: "tenant ID 0 is invalid", + expectedHint: "Tenant ID 0 is invalid.", }, { name: "multiple similar cluster identifiers", From bf60012c117befc03b79e70274f4c8b9daa2d024 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Tue, 2 Nov 2021 16:51:17 -0400 Subject: [PATCH 177/205] jobs: fix improperly wrapped errors I'm working on a linter that detects errors that are not wrapped correctly, and it discovered these. Release note: None --- pkg/jobs/adopt.go | 2 +- pkg/jobs/jobs.go | 4 ++-- pkg/jobs/jobs_test.go | 2 +- .../jobsprotectedts/jobs_protected_ts_test.go | 5 ++--- pkg/jobs/registry.go | 20 +++++++++++++++---- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/pkg/jobs/adopt.go b/pkg/jobs/adopt.go index 130707bac221..2758a0c2fa2a 100644 --- a/pkg/jobs/adopt.go +++ b/pkg/jobs/adopt.go @@ -481,7 +481,7 @@ func (r *Registry) servePauseAndCancelRequests(ctx context.Context, s sqllivenes } return nil }); err != nil { - return errors.Wrapf(err, "job %d: tried to cancel but could not mark as reverting: %s", id, err) + return errors.Wrapf(err, "job %d: tried to cancel but could not mark as reverting", id) } log.Infof(ctx, "job %d, session id: %s canceled: the job is now reverting", id, s.ID()) diff --git a/pkg/jobs/jobs.go b/pkg/jobs/jobs.go index 9fd0934096f7..cb8e2ba894ec 100644 --- a/pkg/jobs/jobs.go +++ b/pkg/jobs/jobs.go @@ -452,8 +452,8 @@ func (j *Job) cancelRequested( } if md.Status == StatusPaused && md.Payload.FinalResumeError != nil { decodedErr := errors.DecodeError(ctx, *md.Payload.FinalResumeError) - return fmt.Errorf("job %d is paused and has non-nil FinalResumeError "+ - "%s hence cannot be canceled and should be reverted", j.ID(), decodedErr.Error()) + return errors.Wrapf(decodedErr, "job %d is paused and has non-nil FinalResumeError "+ + "hence cannot be canceled and should be reverted", j.ID()) } if fn != nil { if err := fn(ctx, txn); err != nil { diff --git a/pkg/jobs/jobs_test.go b/pkg/jobs/jobs_test.go index 0cdf5aedceed..b8725f911c6e 100644 --- a/pkg/jobs/jobs_test.go +++ b/pkg/jobs/jobs_test.go @@ -587,7 +587,7 @@ func TestRegistryLifecycle(t *testing.T) { rts.sqlDB.Exec(t, "PAUSE JOB $1", j.ID()) rts.check(t, jobs.StatusPaused) - rts.sqlDB.ExpectErr(t, "paused and has non-nil FinalResumeError resume", "CANCEL JOB $1", j.ID()) + rts.sqlDB.ExpectErr(t, "paused and has non-nil FinalResumeError .* resume failed", "CANCEL JOB $1", j.ID()) rts.check(t, jobs.StatusPaused) rts.sqlDB.Exec(t, "RESUME JOB $1", j.ID()) diff --git a/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go b/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go index e0ddecd75db2..e8bfa763a605 100644 --- a/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go +++ b/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go @@ -12,7 +12,6 @@ package jobsprotectedts_test import ( "context" - "fmt" "io" "testing" @@ -100,7 +99,7 @@ func TestJobsProtectedTimestamp(t *testing.T) { if errors.Is(err, protectedts.ErrNotExists) { return nil } - return fmt.Errorf("waiting for %v, got %v", protectedts.ErrNotExists, err) + return errors.NewAssertionErrorWithWrappedErrf(err, "waiting for ErrNotExists") } testutils.SucceedsSoon(t, func() (err error) { return s0.DB().Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { @@ -177,7 +176,7 @@ func TestSchedulesProtectedTimestamp(t *testing.T) { if errors.Is(err, protectedts.ErrNotExists) { return nil } - return fmt.Errorf("waiting for %v, got %v", protectedts.ErrNotExists, err) + return errors.NewAssertionErrorWithWrappedErrf(err, "waiting for ErrNotExists") } testutils.SucceedsSoon(t, func() (err error) { return s0.DB().Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { diff --git a/pkg/jobs/registry.go b/pkg/jobs/registry.go index 6c3eb8c6e08e..80249d35344c 100644 --- a/pkg/jobs/registry.go +++ b/pkg/jobs/registry.go @@ -1241,7 +1241,10 @@ func (r *Registry) stepThroughStateMachine( if err := job.canceled(ctx, nil /* txn */, nil /* fn */); err != nil { // If we can't transactionally mark the job as canceled then it will be // restarted during the next adopt loop and reverting will be retried. - return errors.Wrapf(err, "job %d: could not mark as canceled: %v", job.ID(), jobErr) + return errors.WithSecondaryError( + errors.Wrapf(err, "job %d: could not mark as canceled", job.ID()), + jobErr, + ) } telemetry.Inc(TelemetryMetrics[jobType].Canceled) return errors.WithSecondaryError(errors.Errorf("job %s", status), jobErr) @@ -1264,7 +1267,10 @@ func (r *Registry) stepThroughStateMachine( if err := job.reverted(ctx, nil /* txn */, jobErr, nil /* fn */); err != nil { // If we can't transactionally mark the job as reverting then it will be // restarted during the next adopt loop and it will be retried. - return errors.Wrapf(err, "job %d: could not mark as reverting: %s", job.ID(), jobErr) + return errors.WithSecondaryError( + errors.Wrapf(err, "job %d: could not mark as reverting", job.ID()), + jobErr, + ) } onFailOrCancelCtx := logtags.AddTag(ctx, "job", job.ID()) var err error @@ -1301,7 +1307,10 @@ func (r *Registry) stepThroughStateMachine( if err := job.failed(ctx, nil /* txn */, jobErr, nil /* fn */); err != nil { // If we can't transactionally mark the job as failed then it will be // restarted during the next adopt loop and reverting will be retried. - return errors.Wrapf(err, "job %d: could not mark as failed: %s", job.ID(), jobErr) + return errors.WithSecondaryError( + errors.Wrapf(err, "job %d: could not mark as failed", job.ID()), + jobErr, + ) } telemetry.Inc(TelemetryMetrics[jobType].Failed) return jobErr @@ -1316,7 +1325,10 @@ func (r *Registry) stepThroughStateMachine( if err := job.revertFailed(ctx, nil /* txn */, jobErr, nil /* fn */); err != nil { // If we can't transactionally mark the job as failed then it will be // restarted during the next adopt loop and reverting will be retried. - return errors.Wrapf(err, "job %d: could not mark as revert field: %s", job.ID(), jobErr) + return errors.WithSecondaryError( + errors.Wrapf(err, "job %d: could not mark as revert field", job.ID()), + jobErr, + ) } return jobErr default: From 8a0066b8b8a561ec0fca1de8dcde621b3386e934 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 27 Oct 2021 12:45:29 -0400 Subject: [PATCH 178/205] sql: destroy_tenant queues gc job Previously, destory_tenant marked the tenant as inactive. A seperate call to gc_tenant was required to trigger the deletion. Now, the gc is scheduled by the destroy_tenant command. The gc_tenant command will be deleted once this change is live in the Cockroach Cloud Serverless clusters and all inactive tenants are deleted. Release note: None --- pkg/ccl/backupccl/backup_test.go | 5 +---- .../testdata/logic_test/tenant_usage | 3 ++- pkg/sql/logictest/testdata/logic_test/tenant | 15 +++------------ pkg/sql/sem/builtins/builtins.go | 8 ++------ pkg/sql/tenant.go | 19 ++++++++++--------- 5 files changed, 18 insertions(+), 32 deletions(-) diff --git a/pkg/ccl/backupccl/backup_test.go b/pkg/ccl/backupccl/backup_test.go index 6c044f2fcae6..72dc66fa0ce1 100644 --- a/pkg/ccl/backupccl/backup_test.go +++ b/pkg/ccl/backupccl/backup_test.go @@ -7127,11 +7127,9 @@ func TestBackupRestoreTenant(t *testing.T) { [][]string{{`10`, `false`, `{"id": "10", "state": "DROP"}`}}, ) - // Make GC jobs run in 1 second. + // Make GC job scheduled by destroy_tenant run in 1 second. restoreDB.Exec(t, "SET CLUSTER SETTING kv.range_merge.queue_enabled = false") restoreDB.Exec(t, "ALTER RANGE tenants CONFIGURE ZONE USING gc.ttlseconds = 1;") - // Now run the GC job to delete the tenant and its data. - restoreDB.Exec(t, `SELECT crdb_internal.gc_tenant(10)`) // Wait for tenant GC job to complete. restoreDB.CheckQueryResultsRetry( t, @@ -7163,7 +7161,6 @@ func TestBackupRestoreTenant(t *testing.T) { restoreTenant10.CheckQueryResults(t, `select * from foo.bar2`, tenant10.QueryStr(t, `select * from foo.bar2`)) restoreDB.Exec(t, `SELECT crdb_internal.destroy_tenant(10)`) - restoreDB.Exec(t, `SELECT crdb_internal.gc_tenant(10)`) // Wait for tenant GC job to complete. restoreDB.CheckQueryResultsRetry( t, diff --git a/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage b/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage index 02d50524012b..b81296f5d3db 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage +++ b/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage @@ -15,7 +15,8 @@ SELECT crdb_internal.update_tenant_resource_limits(5, 1000, 100, 0, now(), 0) # TODO(radu): inspect internal tenant_usage state. -# Note this just marks the tenant as dropped but does not call GC. +# Note this marks the tenant as dropped. The GC will not delete the tenant +# until after the ttl expires. query I SELECT crdb_internal.destroy_tenant(5) ---- diff --git a/pkg/sql/logictest/testdata/logic_test/tenant b/pkg/sql/logictest/testdata/logic_test/tenant index 7528f05905bd..0791955cfef3 100644 --- a/pkg/sql/logictest/testdata/logic_test/tenant +++ b/pkg/sql/logictest/testdata/logic_test/tenant @@ -46,18 +46,9 @@ id active crdb_internal.pb_to_json 5 false {"id": "5", "state": "DROP"} 10 true {"id": "10", "state": "ACTIVE"} -# Try to recreate an existing tenant. - -query error pgcode 42710 tenant "5" already exists -SELECT crdb_internal.create_tenant(5) - -query I -SELECT crdb_internal.gc_tenant(5) ----- -5 - -# GC job has not run yet. +# Try to recreate an existing tenant. +# GC job for tenant 5 has not run yet. query error pgcode 42710 tenant "5" already exists SELECT crdb_internal.create_tenant(5) @@ -142,7 +133,7 @@ SELECT status FROM [ succeeded query error pgcode 42704 tenant "5" does not exist -SELECT crdb_internal.gc_tenant(5) +SELECT crdb_internal.destroy_tenant(5) query IBT colnames SELECT id, active, crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info, true) diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index a3b9fd24c2c7..e2d3a1f51011 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -4566,12 +4566,6 @@ value if you rely on the HLC for accuracy.`, } return args[0], nil }, - // TODO(spaskob): this built-in currently does not actually delete the - // data but just marks it as DROP. This is for done for safety in case we - // would like to restore the tenant later. If data in needs to be removed - // use gc_tenant built-in. - // We should just add a new built-in called `drop_tenant` instead and use - // this one to really destroy the tenant. Info: "Destroys a tenant with the provided ID. Must be run by the System tenant.", Volatility: tree.VolatilityVolatile, }, @@ -5663,6 +5657,8 @@ value if you rely on the HLC for accuracy.`, ), "crdb_internal.gc_tenant": makeBuiltin( + // TODO(jeffswenson): Delete internal_crdb.gc_tenant after the DestroyTenant + // changes are deployed to all Cockroach Cloud serverless hosts. tree.FunctionProperties{ Category: categoryMultiTenancy, Undocumented: true, diff --git a/pkg/sql/tenant.go b/pkg/sql/tenant.go index 8f571696ea92..d28f1634f29d 100644 --- a/pkg/sql/tenant.go +++ b/pkg/sql/tenant.go @@ -330,11 +330,6 @@ func clearTenant(ctx context.Context, execCfg *ExecutorConfig, info *descpb.Tena } // DestroyTenant implements the tree.TenantOperator interface. -// TODO(spaskob): this function currently does not actually delete the data but -// just marks it as DROP. This is for done for safety in case we would like to -// restore the tenant later. -// We should just add a new function DropTenant to the interface and convert -// this one to really remove the tenant and its data. func (p *planner) DestroyTenant(ctx context.Context, tenID uint64) error { const op = "destroy" if err := rejectIfCantCoordinateMultiTenancy(p.execCfg.Codec, op); err != nil { @@ -356,7 +351,11 @@ func (p *planner) DestroyTenant(ctx context.Context, tenID uint64) error { // Mark the tenant as dropping. info.State = descpb.TenantInfo_DROP - return errors.Wrap(updateTenantRecord(ctx, p.execCfg, p.txn, info), "destroying tenant") + if err := updateTenantRecord(ctx, p.execCfg, p.txn, info); err != nil { + return errors.Wrap(err, "destroying tenant") + } + + return errors.Wrap(gcTenantJob(ctx, p.execCfg, p.txn, p.User(), tenID), "scheduling gc job") } // GCTenantSync clears the tenant's data and removes its record. @@ -394,8 +393,8 @@ func GCTenantSync(ctx context.Context, execCfg *ExecutorConfig, info *descpb.Ten return errors.Wrapf(err, "deleting tenant %d record", info.ID) } -// GCTenantJob clears the tenant's data and removes its record using a GC job. -func GCTenantJob( +// gcTenantJob clears the tenant's data and removes its record using a GC job. +func gcTenantJob( ctx context.Context, execCfg *ExecutorConfig, txn *kv.Txn, @@ -425,6 +424,8 @@ func GCTenantJob( // GCTenant implements the tree.TenantOperator interface. func (p *planner) GCTenant(ctx context.Context, tenID uint64) error { + // TODO(jeffswenson): Delete internal_crdb.gc_tenant after the DestroyTenant + // changes are deployed to all Cockroach Cloud serverless hosts. if !p.ExtendedEvalContext().TxnImplicit { return errors.Errorf("gc_tenant cannot be used inside a transaction") } @@ -442,7 +443,7 @@ func (p *planner) GCTenant(ctx context.Context, tenID uint64) error { return errors.Errorf("tenant %d is not in state DROP", info.ID) } - return GCTenantJob(ctx, p.ExecCfg(), p.Txn(), p.User(), tenID) + return gcTenantJob(ctx, p.ExecCfg(), p.Txn(), p.User(), tenID) } // UpdateTenantResourceLimits implements the tree.TenantOperator interface. From 2ccf86bf28fe1eaad7ab04910dc66f87ab30a1c7 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Tue, 2 Nov 2021 16:49:13 -0400 Subject: [PATCH 179/205] sql/*: fix improperly wrapped errors I'm working on a linter that detects errors that are not wrapped correctly, and it discovered these. Release note: None --- pkg/geo/geos/geos.go | 6 ++--- pkg/sql/colflow/colrpc/inbox.go | 10 ++++---- pkg/sql/exec_util.go | 2 +- pkg/sql/flowinfra/inbound.go | 2 +- .../logictest/testdata/logic_test/drop_index | 2 +- pkg/sql/opt/optgen/cmd/langgen/main.go | 2 +- pkg/sql/opt/optgen/cmd/optgen/main.go | 2 +- pkg/sql/opt/optgen/lang/compiler.go | 2 +- pkg/sql/opt/props/func_dep_rand_test.go | 6 ++--- pkg/sql/pg_metadata_test.go | 11 +++++---- pkg/sql/pgwire/server.go | 24 ++++++++++++------- pkg/sql/region_util.go | 12 +++++----- pkg/sql/row/errors.go | 8 +++---- pkg/sql/sem/builtins/builtins.go | 4 ++-- pkg/sql/sem/tree/eval.go | 12 +++++----- pkg/sql/sem/tree/interval.go | 8 +++---- pkg/sql/set_zone_config.go | 12 ++++------ pkg/sql/sqlerrors/errors.go | 6 +---- pkg/sql/sqlstats/sslocal/BUILD.bazel | 1 + pkg/sql/sqlstats/sslocal/sslocal_provider.go | 4 ++-- .../sqlstats/ssmemstorage/ss_mem_storage.go | 2 +- pkg/sql/type_change_test.go | 2 +- 22 files changed, 71 insertions(+), 69 deletions(-) diff --git a/pkg/geo/geos/geos.go b/pkg/geo/geos/geos.go index 2d928af8a81f..7176892f17aa 100644 --- a/pkg/geo/geos/geos.go +++ b/pkg/geo/geos/geos.go @@ -194,10 +194,10 @@ func initGEOS(dirs []string) (*C.CR_GEOS, string, error) { } err = errors.CombineErrors( err, - errors.Newf( - "geos: cannot load GEOS from dir %q: %s", - dir, + errors.Wrapf( newErr, + "geos: cannot load GEOS from dir %q", + dir, ), ) } diff --git a/pkg/sql/colflow/colrpc/inbox.go b/pkg/sql/colflow/colrpc/inbox.go index 2958ca8b63b5..6962014a357b 100644 --- a/pkg/sql/colflow/colrpc/inbox.go +++ b/pkg/sql/colflow/colrpc/inbox.go @@ -12,7 +12,6 @@ package colrpc import ( "context" - "fmt" "io" "math" "sync/atomic" @@ -33,6 +32,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/log/logcrash" "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" "github.com/cockroachdb/logtags" ) @@ -208,7 +208,7 @@ func (i *Inbox) RunWithStream( case readerCtx = <-i.contextCh: log.VEvent(streamCtx, 2, "Inbox reader arrived") case <-streamCtx.Done(): - return fmt.Errorf("%s: streamCtx while waiting for reader (remote client canceled)", streamCtx.Err()) + return errors.Wrap(streamCtx.Err(), "streamCtx error while waiting for reader (remote client canceled)") case <-flowCtxDone: // The flow context of the inbox host has been canceled. This can occur // e.g. when the query is canceled, or when another stream encountered @@ -233,7 +233,7 @@ func (i *Inbox) RunWithStream( return nil case <-streamCtx.Done(): // The client canceled the stream. - return fmt.Errorf("%s: streamCtx in Inbox stream handler (remote client canceled)", streamCtx.Err()) + return errors.Wrap(streamCtx.Err(), "streamCtx error in Inbox stream handler (remote client canceled)") } } @@ -258,7 +258,7 @@ func (i *Inbox) Init(ctx context.Context) { select { case i.stream = <-i.streamCh: case err := <-i.timeoutCh: - i.errCh <- fmt.Errorf("%s: remote stream arrived too late", err) + i.errCh <- errors.Wrap(err, "remote stream arrived too late") return err case <-i.Ctx.Done(): // Our reader canceled the context meaning that it no longer needs @@ -325,7 +325,7 @@ func (i *Inbox) Next() coldata.Batch { // Regardless of the cause we want to propagate such an error as // expected one in all cases so that the caller could decide on how // to handle it. - err = pgerror.Newf(pgcode.InternalConnectionFailure, "inbox communication error: %s", err) + err = pgerror.Wrap(err, pgcode.InternalConnectionFailure, "inbox communication error") i.errCh <- err colexecerror.ExpectedError(err) } diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index 2dd9b178dacf..ce65bdf351a8 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -1798,7 +1798,7 @@ type registrySession interface { func (r *SessionRegistry) CancelQuery(queryIDStr string) (bool, error) { queryID, err := StringToClusterWideID(queryIDStr) if err != nil { - return false, fmt.Errorf("query ID %s malformed: %s", queryID, err) + return false, errors.Wrapf(err, "query ID %s malformed", queryID) } r.Lock() diff --git a/pkg/sql/flowinfra/inbound.go b/pkg/sql/flowinfra/inbound.go index b752b25ba96d..d478c7d770b5 100644 --- a/pkg/sql/flowinfra/inbound.go +++ b/pkg/sql/flowinfra/inbound.go @@ -137,7 +137,7 @@ func processInboundStreamHelper( if err != nil { if err != io.EOF { // Communication error. - err = pgerror.Newf(pgcode.InternalConnectionFailure, "inbox communication error: %s", err) + err = pgerror.Wrap(err, pgcode.InternalConnectionFailure, "inbox communication error") sendErrToConsumer(err) errChan <- err return diff --git a/pkg/sql/logictest/testdata/logic_test/drop_index b/pkg/sql/logictest/testdata/logic_test/drop_index index 2439f8206e71..34a5cdd22bdc 100644 --- a/pkg/sql/logictest/testdata/logic_test/drop_index +++ b/pkg/sql/logictest/testdata/logic_test/drop_index @@ -327,7 +327,7 @@ DROP INDEX t_secondary CASCADE; ALTER TABLE t DROP COLUMN b; INSERT INTO t SELECT a + 1 FROM t; -statement error pgcode 23505 duplicate key value: decoding err=column-id "2" does not exist +statement error pgcode 23505 duplicate key value got decoding error: column-id "2" does not exist UPSERT INTO t SELECT a + 1 FROM t; statement ok diff --git a/pkg/sql/opt/optgen/cmd/langgen/main.go b/pkg/sql/opt/optgen/cmd/langgen/main.go index 8993d8eff825..7771a162b1cc 100644 --- a/pkg/sql/opt/optgen/cmd/langgen/main.go +++ b/pkg/sql/opt/optgen/cmd/langgen/main.go @@ -130,7 +130,7 @@ func generate(compiled *lang.CompiledExpr, out string, genFunc genFunc) error { if err != nil { // Write out incorrect source for easier debugging. b = buf.Bytes() - err = fmt.Errorf("code formatting failed with Go parse error\n%s:%s", out, err) + err = errors.Wrapf(err, "code formatting failed with Go parse error\n%s", out) } } else { b = buf.Bytes() diff --git a/pkg/sql/opt/optgen/cmd/optgen/main.go b/pkg/sql/opt/optgen/cmd/optgen/main.go index cbc19b3e83a9..7e13322f6178 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/main.go +++ b/pkg/sql/opt/optgen/cmd/optgen/main.go @@ -218,7 +218,7 @@ func (g *optgen) generate(compiled *lang.CompiledExpr, genFunc genFunc) error { // Write out incorrect source for easier debugging. b = buf.Bytes() out := g.cmdLine.Lookup("out").Value.String() - err = fmt.Errorf("code formatting failed with Go parse error\n%s:%s", out, err) + err = errors.Wrapf(err, "code formatting failed with Go parse error\n%s", out) } } else { b = buf.Bytes() diff --git a/pkg/sql/opt/optgen/lang/compiler.go b/pkg/sql/opt/optgen/lang/compiler.go index 1b0d203dc30b..bf9b18aab8a4 100644 --- a/pkg/sql/opt/optgen/lang/compiler.go +++ b/pkg/sql/opt/optgen/lang/compiler.go @@ -200,7 +200,7 @@ func (c *Compiler) compileRules(rules RuleSetExpr) bool { func (c *Compiler) addErr(src *SourceLoc, err error) { if src != nil { - err = fmt.Errorf("%s: %s", src, err.Error()) + err = errors.Wrapf(err, "%s", src) } c.errors = append(c.errors, err) } diff --git a/pkg/sql/opt/props/func_dep_rand_test.go b/pkg/sql/opt/props/func_dep_rand_test.go index c8361fbc5548..8fb8f4f76651 100644 --- a/pkg/sql/opt/props/func_dep_rand_test.go +++ b/pkg/sql/opt/props/func_dep_rand_test.go @@ -432,7 +432,7 @@ func (tc *testConfig) checkAPIs(fd *FuncDepSet, tr testRelation) error { to: closure, strict: true, }); err != nil { - return fmt.Errorf("ComputeClosure%s incorrectly returns %s: %s", cols, closure, err) + return errors.Wrapf(err, "ComputeClosure%s incorrectly returns %s", cols, closure) } reduced := fd.ReduceCols(cols) @@ -441,7 +441,7 @@ func (tc *testConfig) checkAPIs(fd *FuncDepSet, tr testRelation) error { to: cols, strict: true, }); err != nil { - return fmt.Errorf("ReduceCols%s incorrectly returns %s: %s", cols, reduced, err) + return errors.Wrapf(err, "ReduceCols%s incorrectly returns %s", cols, reduced) } var proj FuncDepSet @@ -449,7 +449,7 @@ func (tc *testConfig) checkAPIs(fd *FuncDepSet, tr testRelation) error { proj.ProjectCols(cols) // The FDs after projection should still hold on the table. if err := tr.checkFDs(&proj); err != nil { - return fmt.Errorf("ProjectCols%s incorrectly returns %s: %s", cols, proj.String(), err) + return errors.Wrapf(err, "ProjectCols%s incorrectly returns %s", cols, proj.String()) } } diff --git a/pkg/sql/pg_metadata_test.go b/pkg/sql/pg_metadata_test.go index 3c418f24af37..14a1a7c3aecb 100644 --- a/pkg/sql/pg_metadata_test.go +++ b/pkg/sql/pg_metadata_test.go @@ -123,6 +123,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/errors" "github.com/cockroachdb/errors/oserror" "github.com/lib/pq/oid" ) @@ -678,7 +679,7 @@ type outputFile struct { // appendString calls WriteString and panics on error. func (o outputFile) appendString(s string) { if _, err := o.f.WriteString(s); err != nil { - panic(fmt.Errorf("error while writing string: %s: %v", s, err)) + panic(errors.Wrapf(err, "error while writing string: %s", s)) } } @@ -703,7 +704,7 @@ func rewriteFile(fileName string, f func(*os.File, outputFile)) { updateFile(tmpName, fileName, func(input *os.File, output outputFile) { if _, err := io.Copy(output.f, input); err != nil { - panic(fmt.Errorf("problem at rewriting file %s into %s: %v", tmpName, fileName, err)) + panic(errors.Wrapf(err, "problem at rewriting file %s into %s", tmpName, fileName)) } }) } @@ -711,13 +712,13 @@ func rewriteFile(fileName string, f func(*os.File, outputFile)) { func updateFile(inputFileName, outputFileName string, f func(input *os.File, output outputFile)) { input, err := os.Open(inputFileName) if err != nil { - panic(fmt.Errorf("error opening file %s: %v", inputFileName, err)) + panic(errors.Wrapf(err, "error opening file %s", inputFileName)) } defer dClose(input) output, err := os.OpenFile(outputFileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { - panic(fmt.Errorf("error opening file %s: %v", outputFileName, err)) + panic(errors.Wrapf(err, "error opening file %s", outputFileName)) } defer dClose(output) @@ -895,7 +896,7 @@ func (scf schemaCodeFixer) getTableDefinitionsText(unimplementedTables PGMetadat maxLength := 0 f, err := os.Open(fileName) if err != nil { - panic(fmt.Errorf("could not open file %s: %v", fileName, err)) + panic(errors.Wrapf(err, "could not open file %s", fileName)) } defer dClose(f) reader := bufio.NewScanner(f) diff --git a/pkg/sql/pgwire/server.go b/pkg/sql/pgwire/server.go index ec6450a411a8..34321a3e88d3 100644 --- a/pkg/sql/pgwire/server.go +++ b/pkg/sql/pgwire/server.go @@ -739,8 +739,10 @@ func parseClientProvidedSessionParameters( // Read a key-value pair from the client. key, err := buf.GetString() if err != nil { - return sql.SessionArgs{}, pgerror.Newf(pgcode.ProtocolViolation, - "error reading option key: %s", err) + return sql.SessionArgs{}, pgerror.Wrap( + err, pgcode.ProtocolViolation, + "error reading option key", + ) } if len(key) == 0 { // End of parameter list. @@ -748,8 +750,10 @@ func parseClientProvidedSessionParameters( } value, err := buf.GetString() if err != nil { - return sql.SessionArgs{}, pgerror.Newf(pgcode.ProtocolViolation, - "error reading option value: %s", err) + return sql.SessionArgs{}, pgerror.Wrapf( + err, pgcode.ProtocolViolation, + "error reading option value for key %q", key, + ) } // Case-fold for the key for easier comparison. @@ -788,13 +792,17 @@ func parseClientProvidedSessionParameters( hostS, portS, err := net.SplitHostPort(value) if err != nil { - return sql.SessionArgs{}, pgerror.Newf(pgcode.ProtocolViolation, - "invalid address format: %v", err) + return sql.SessionArgs{}, pgerror.Wrap( + err, pgcode.ProtocolViolation, + "invalid address format", + ) } port, err := strconv.Atoi(portS) if err != nil { - return sql.SessionArgs{}, pgerror.Newf(pgcode.ProtocolViolation, - "remote port is not numeric: %v", err) + return sql.SessionArgs{}, pgerror.Wrap( + err, pgcode.ProtocolViolation, + "remote port is not numeric", + ) } ip := net.ParseIP(hostS) if ip == nil { diff --git a/pkg/sql/region_util.go b/pkg/sql/region_util.go index ff161f9e0f74..939d6074fd39 100644 --- a/pkg/sql/region_util.go +++ b/pkg/sql/region_util.go @@ -663,17 +663,17 @@ func prepareZoneConfigForMultiRegionTable( return nil, nil } if err := newZoneConfig.Validate(); err != nil { - return nil, pgerror.Newf( - pgcode.CheckViolation, - "could not validate zone config: %v", + return nil, pgerror.Wrap( err, + pgcode.CheckViolation, + "could not validate zone config", ) } if err := newZoneConfig.ValidateTandemFields(); err != nil { - return nil, pgerror.Newf( - pgcode.CheckViolation, - "could not validate zone config: %v", + return nil, pgerror.Wrap( err, + pgcode.CheckViolation, + "could not validate zone config", ) } return prepareZoneConfigWrites( diff --git a/pkg/sql/row/errors.go b/pkg/sql/row/errors.go index dfbb137ad7d0..b21aa8cd70c4 100644 --- a/pkg/sql/row/errors.go +++ b/pkg/sql/row/errors.go @@ -115,8 +115,8 @@ func NewUniquenessConstraintViolationError( ) error { index, names, values, err := DecodeRowInfo(ctx, tableDesc, key, value, false) if err != nil { - return pgerror.Newf(pgcode.UniqueViolation, - "duplicate key value: decoding err=%s", err) + return pgerror.Wrap(err, pgcode.UniqueViolation, + "duplicate key value got decoding error") } // Exclude implicit partitioning columns and hash sharded index columns from @@ -156,8 +156,8 @@ func NewLockNotAvailableError( index, colNames, values, err := DecodeRowInfo(ctx, tableDesc, key, nil, false) if err != nil { - return pgerror.Newf(pgcode.LockNotAvailable, - "%s: decoding err=%s", baseMsg, err) + return pgerror.Wrapf(err, pgcode.LockNotAvailable, + "%s: got decoding error", baseMsg) } return pgerror.Newf(pgcode.LockNotAvailable, diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index a3b9fd24c2c7..f51a1e3a1922 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -4895,7 +4895,7 @@ value if you rely on the HLC for accuracy.`, }, }) if err := ctx.Txn.Run(ctx.Context, b); err != nil { - return nil, pgerror.Newf(pgcode.InvalidParameterValue, "message: %s", err) + return nil, pgerror.Wrap(err, pgcode.InvalidParameterValue, "error fetching leaseholder") } resp := b.RawResponse().Responses[0].GetInner().(*roachpb.LeaseInfoResponse) @@ -4990,7 +4990,7 @@ value if you rely on the HLC for accuracy.`, }, }) if err := ctx.Txn.Run(ctx.Context, b); err != nil { - return nil, pgerror.Newf(pgcode.InvalidParameterValue, "message: %s", err) + return nil, pgerror.Wrap(err, pgcode.InvalidParameterValue, "error fetching range stats") } resp := b.RawResponse().Responses[0].GetInner().(*roachpb.RangeStatsResponse).MVCCStats jsonStr, err := gojson.Marshal(&resp) diff --git a/pkg/sql/sem/tree/eval.go b/pkg/sql/sem/tree/eval.go index 806d9f63716c..e66e71eabf6e 100644 --- a/pkg/sql/sem/tree/eval.go +++ b/pkg/sql/sem/tree/eval.go @@ -2983,8 +2983,8 @@ func MatchLikeEscape( like, err := optimizedLikeFunc(pattern, caseInsensitive, escapeRune) if err != nil { - return DBoolFalse, pgerror.Newf( - pgcode.InvalidRegularExpression, "LIKE regexp compilation failed: %v", err) + return DBoolFalse, pgerror.Wrap( + err, pgcode.InvalidRegularExpression, "LIKE regexp compilation failed") } if like == nil { @@ -3008,8 +3008,8 @@ func ConvertLikeToRegexp( key := likeKey{s: pattern, caseInsensitive: caseInsensitive, escape: escape} re, err := ctx.ReCache.GetRegexp(key) if err != nil { - return nil, pgerror.Newf( - pgcode.InvalidRegularExpression, "LIKE regexp compilation failed: %v", err) + return nil, pgerror.Wrap( + err, pgcode.InvalidRegularExpression, "LIKE regexp compilation failed") } return re, nil } @@ -3033,8 +3033,8 @@ func matchLike(ctx *EvalContext, left, right Datum, caseInsensitive bool) (Datum like, err := optimizedLikeFunc(pattern, caseInsensitive, '\\') if err != nil { - return DBoolFalse, pgerror.Newf( - pgcode.InvalidRegularExpression, "LIKE regexp compilation failed: %v", err) + return DBoolFalse, pgerror.Wrap( + err, pgcode.InvalidRegularExpression, "LIKE regexp compilation failed") } if like == nil { diff --git a/pkg/sql/sem/tree/interval.go b/pkg/sql/sem/tree/interval.go index 4b3bea2a627a..4831930137aa 100644 --- a/pkg/sql/sem/tree/interval.go +++ b/pkg/sql/sem/tree/interval.go @@ -67,8 +67,8 @@ func (l *intervalLexer) consumeNum() (int64, bool, float64) { // Try to convert. value, err := strconv.ParseFloat(l.str[start:l.offset], 64) if err != nil { - l.err = pgerror.Newf( - pgcode.InvalidDatetimeFormat, "interval: %v", err) + l.err = pgerror.Wrap( + err, pgcode.InvalidDatetimeFormat, "interval") return 0, false, 0 } decPart = value @@ -108,8 +108,8 @@ func (l *intervalLexer) consumeInt() int64 { x, err := strconv.ParseInt(l.str[start:l.offset], 10, 64) if err != nil { - l.err = pgerror.Newf( - pgcode.InvalidDatetimeFormat, "interval: %v", err) + l.err = pgerror.Wrap( + err, pgcode.InvalidDatetimeFormat, "interval") return 0 } if start == l.offset { diff --git a/pkg/sql/set_zone_config.go b/pkg/sql/set_zone_config.go index 97c80849886c..10b060d268f8 100644 --- a/pkg/sql/set_zone_config.go +++ b/pkg/sql/set_zone_config.go @@ -641,14 +641,12 @@ func (n *setZoneConfigNode) startExec(params runParams) error { // empty, in which case the unmarshaling will be a no-op. This is // innocuous. if err := yaml.UnmarshalStrict([]byte(yamlConfig), &newZone); err != nil { - return pgerror.Newf(pgcode.CheckViolation, - "could not parse zone config: %v", err) + return pgerror.Wrap(err, pgcode.CheckViolation, "could not parse zone config") } // Load settings from YAML into the partial zone as well. if err := yaml.UnmarshalStrict([]byte(yamlConfig), &finalZone); err != nil { - return pgerror.Newf(pgcode.CheckViolation, - "could not parse zone config: %v", err) + return pgerror.Wrap(err, pgcode.CheckViolation, "could not parse zone config") } // Load settings from var = val assignments. If there were no such @@ -739,8 +737,7 @@ func (n *setZoneConfigNode) startExec(params runParams) error { // Finally revalidate everything. Validate only the completeZone config. if err := completeZone.Validate(); err != nil { - return pgerror.Newf(pgcode.CheckViolation, - "could not validate zone config: %v", err) + return pgerror.Wrap(err, pgcode.CheckViolation, "could not validate zone config") } // Finally check for the extra protection partial zone configs would @@ -1064,8 +1061,7 @@ func prepareZoneConfigWrites( } buf, err := protoutil.Marshal(zone) if err != nil { - return nil, pgerror.Newf(pgcode.CheckViolation, - "could not marshal zone config: %v", err) + return nil, pgerror.Wrap(err, pgcode.CheckViolation, "could not marshal zone config") } return &zoneConfigUpdate{id: targetID, value: buf}, nil } diff --git a/pkg/sql/sqlerrors/errors.go b/pkg/sql/sqlerrors/errors.go index d7d6c1631462..c59838f94442 100644 --- a/pkg/sql/sqlerrors/errors.go +++ b/pkg/sql/sqlerrors/errors.go @@ -217,11 +217,7 @@ func NewDependentObjectErrorf(format string, args ...interface{}) error { // NewRangeUnavailableError creates an unavailable range error. func NewRangeUnavailableError(rangeID roachpb.RangeID, origErr error) error { - // TODO(knz): This could should really use errors.Wrap or - // errors.WithSecondaryError. - return pgerror.Newf(pgcode.RangeUnavailable, - "key range id:%d is unavailable. Original error: %v", - rangeID, origErr) + return pgerror.Wrapf(origErr, pgcode.RangeUnavailable, "key range id:%d is unavailable", rangeID) } // NewWindowInAggError creates an error for the case when a window function is diff --git a/pkg/sql/sqlstats/sslocal/BUILD.bazel b/pkg/sql/sqlstats/sslocal/BUILD.bazel index 749d8100d77c..368e1de8744e 100644 --- a/pkg/sql/sqlstats/sslocal/BUILD.bazel +++ b/pkg/sql/sqlstats/sslocal/BUILD.bazel @@ -29,6 +29,7 @@ go_library( "//pkg/util/stop", "//pkg/util/syncutil", "//pkg/util/timeutil", + "@com_github_cockroachdb_errors//:errors", ], ) diff --git a/pkg/sql/sqlstats/sslocal/sslocal_provider.go b/pkg/sql/sqlstats/sslocal/sslocal_provider.go index 2615a67cadde..691ed72982fc 100644 --- a/pkg/sql/sqlstats/sslocal/sslocal_provider.go +++ b/pkg/sql/sqlstats/sslocal/sslocal_provider.go @@ -12,7 +12,6 @@ package sslocal import ( "context" - "fmt" "sort" "time" @@ -28,6 +27,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/mon" "github.com/cockroachdb/cockroach/pkg/util/stop" "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" ) // New returns an instance of SQLStats. @@ -185,7 +185,7 @@ func (s *SQLStats) IterateAggregatedTransactionStats( err := statsContainer.IterateAggregatedTransactionStats(ctx, options, visitor) if err != nil { - return fmt.Errorf("sql stats iteration abort: %s", err) + return errors.Wrap(err, "sql stats iteration abort") } } diff --git a/pkg/sql/sqlstats/ssmemstorage/ss_mem_storage.go b/pkg/sql/sqlstats/ssmemstorage/ss_mem_storage.go index 7a426e39eea0..30bf14fb08e2 100644 --- a/pkg/sql/sqlstats/ssmemstorage/ss_mem_storage.go +++ b/pkg/sql/sqlstats/ssmemstorage/ss_mem_storage.go @@ -167,7 +167,7 @@ func (s *Container) IterateAggregatedTransactionStats( err := visitor(s.appName, &txnStat) if err != nil { - return fmt.Errorf("sql stats iteration abort: %s", err) + return errors.Wrap(err, "sql stats iteration abort") } return nil diff --git a/pkg/sql/type_change_test.go b/pkg/sql/type_change_test.go index c77bb6f077de..612f95f4c4f8 100644 --- a/pkg/sql/type_change_test.go +++ b/pkg/sql/type_change_test.go @@ -582,7 +582,7 @@ WHERE return errors.New("expected error, found none") } if !testutils.IsError(err, "invalid input value for enum") { - return errors.Newf("expected invalid input for enum error, found %v", err) + return errors.NewAssertionErrorWithWrappedErrf(err, "expected invalid input for enum error") } return nil }) From beb9b729e58877b005d25abbe49e73a4d2cdf41e Mon Sep 17 00:00:00 2001 From: Lidor Carmel Date: Tue, 2 Nov 2021 21:43:51 -0700 Subject: [PATCH 180/205] authors: add Lidor to authors Release note: None --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index edd9a4ef4f80..55fb05665de8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -248,6 +248,7 @@ Lasantha Pambagoda Lauren Hirata Lauren lhirata Lee Reilly Levon Lloyd +Lidor Carmel Lindsey Jin Liv Lobo liyanan From 07cc4a9f539bea7c2ad79c4023b50896fe0ed30b Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Tue, 5 Oct 2021 19:22:38 -0400 Subject: [PATCH 181/205] backupccl: unblock table backups for multi-region tables Table backups for regional-by-row, regional-by-table and global tables were disallowed in #60715. The motivation for that change was that table level restores for MR tables were not supported. With #71178, a user should be able to restore the above three kinds of MR tables with the caveat that the regions in the backup need to be a subset of the regions in restoring database. This change allows table backups of all multi-region tables. Fixes: #71180 Release note (sql change): Table backups of regional-by-row, regional-by-table, and global table are no longer disallowed. --- pkg/ccl/backupccl/backup_planning.go | 4 - pkg/ccl/backupccl/targets.go | 54 ------ .../testdata/logic_test/multi_region_backup | 158 +++++++++++++++++- 3 files changed, 151 insertions(+), 65 deletions(-) diff --git a/pkg/ccl/backupccl/backup_planning.go b/pkg/ccl/backupccl/backup_planning.go index 033a1ed0be65..6e431528a4e1 100644 --- a/pkg/ccl/backupccl/backup_planning.go +++ b/pkg/ccl/backupccl/backup_planning.go @@ -967,10 +967,6 @@ func backupPlanHook( return err } - if err := validateMultiRegionBackup(backupStmt, targetDescs, tables); err != nil { - return err - } - clusterID := p.ExecCfg().ClusterID() for i := range prevBackups { // IDs are how we identify tables, and those are only meaningful in the diff --git a/pkg/ccl/backupccl/targets.go b/pkg/ccl/backupccl/targets.go index 95b54cd3e832..440ea0a121e4 100644 --- a/pkg/ccl/backupccl/targets.go +++ b/pkg/ccl/backupccl/targets.go @@ -228,60 +228,6 @@ func getAllDescChanges( return res, nil } -// validateMultiRegionBackup validates that for all tables included in the -// backup, their parent database is also being backed up. For multi-region -// tables, we require that the parent database is included to ensure that the -// multi-region enum (which is required for multi-region tables) is also -// present. -func validateMultiRegionBackup( - backupStmt *annotatedBackupStatement, - descs []catalog.Descriptor, - tables []catalog.TableDescriptor, -) error { - // We only need to block in the table backup case, so there's nothing to do - // if we're running a cluster backup. - if backupStmt.Coverage() == tree.AllDescriptors { - return nil - } - // We build a map of the target databases here because the supplied list of - // descriptors contains ALL database descriptors for the corresponding - // tables (regardless of whether or not the databases are included in the - // backup targets list). The map helps below so that we're not looping over - // the descriptors slice for every table. - databaseTargetIDs := map[descpb.ID]struct{}{} - databaseTargetNames := map[tree.Name]struct{}{} - for _, name := range backupStmt.Targets.Databases { - databaseTargetNames[name] = struct{}{} - } - - for _, desc := range descs { - switch desc.(type) { - case catalog.DatabaseDescriptor: - // If the database descriptor found is included in the targets list, add - // it to the targetsID map. - if _, ok := databaseTargetNames[tree.Name(desc.GetName())]; ok { - databaseTargetIDs[desc.GetID()] = struct{}{} - } - } - } - - // Look through the list of tables and for every multi-region table, see if - // its parent database is being backed up. - for _, table := range tables { - if table.GetLocalityConfig() != nil { - if _, ok := databaseTargetIDs[table.GetParentID()]; !ok { - // Found a table which is being backed up without its parent database. - return pgerror.Newf(pgcode.FeatureNotSupported, - "cannot backup individual table %d from multi-region database %d", - table.GetID(), - table.GetParentID(), - ) - } - } - } - return nil -} - func ensureInterleavesIncluded(tables []catalog.TableDescriptor) error { inBackup := make(map[descpb.ID]bool, len(tables)) for _, t := range tables { diff --git a/pkg/ccl/logictestccl/testdata/logic_test/multi_region_backup b/pkg/ccl/logictestccl/testdata/logic_test/multi_region_backup index 2120ded5c53e..9b384e5bf0f7 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/multi_region_backup +++ b/pkg/ccl/logictestccl/testdata/logic_test/multi_region_backup @@ -193,6 +193,9 @@ CREATE TABLE regional_by_row_table ( FAMILY (pk, pk2, a, b) ) LOCALITY REGIONAL BY ROW +statement ok +INSERT INTO regional_by_row_table VALUES (1, 1, 1, 1, NULL), (2, 1, 1, 2, NULL) + statement ok CREATE TABLE regional_by_table_in_primary_region ( pk INT PRIMARY KEY, @@ -1016,17 +1019,158 @@ TABLE regional_by_table_in_ca_central_1 ALTER TABLE regional_by_table_in_ca_cen voter_constraints = '[+region=ca-central-1]', lease_preferences = '[[+region=ca-central-1]]' -statement error cannot backup individual table +# Backup and restore individual multi-region tables. +subtest multiregion_table_backup_and_restore + +statement ok BACKUP TABLE regional_by_row_table TO 'nodelocal://1/rbr_table/'; -statement error cannot backup individual table -BACKUP TABLE regional_by_table_in_primary_region TO 'nodelocal://1/rbr_table/'; +statement ok +BACKUP TABLE regional_by_table_in_primary_region TO 'nodelocal://1/rbt_table_in_primary_region/'; + +statement ok +BACKUP TABLE regional_by_table_in_ca_central_1 TO 'nodelocal://1/rbt_table_in_ca_central_1/'; + +statement ok +BACKUP TABLE global_table TO 'nodelocal://1/global_table/'; + +statement ok +DROP TABLE regional_by_row_table; +DROP TABLE regional_by_table_in_primary_region; +DROP TABLE regional_by_table_in_ca_central_1; +DROP TABLE global_table; + +statement ok +RESTORE TABLE regional_by_row_table FROM 'nodelocal://1/rbr_table/'; -statement error cannot backup individual table -BACKUP TABLE regional_by_table_in_ca_central_1 TO 'nodelocal://1/rbr_table/'; +statement ok +RESTORE TABLE regional_by_table_in_primary_region FROM 'nodelocal://1/rbt_table_in_primary_region/'; + +statement ok +RESTORE TABLE regional_by_table_in_ca_central_1 FROM 'nodelocal://1/rbt_table_in_ca_central_1/'; + +statement ok +RESTORE TABLE global_table FROM 'nodelocal://1/global_table/'; + +query IIIIT +SELECT * FROM regional_by_row_table; +---- +1 1 1 1 NULL +2 1 1 2 NULL + +query TTT +SELECT partition_name, index_name, zone_config FROM [SHOW PARTITIONS FROM TABLE regional_by_row_table] +ORDER BY partition_name, index_name +---- +ap-southeast-2 regional_by_row_table@regional_by_row_table_a_idx num_voters = 3, +voter_constraints = '[+region=ap-southeast-2]', +lease_preferences = '[[+region=ap-southeast-2]]' +ap-southeast-2 regional_by_row_table@regional_by_row_table_b_key num_voters = 3, +voter_constraints = '[+region=ap-southeast-2]', +lease_preferences = '[[+region=ap-southeast-2]]' +ap-southeast-2 regional_by_row_table@regional_by_row_table_j_idx num_voters = 3, +voter_constraints = '[+region=ap-southeast-2]', +lease_preferences = '[[+region=ap-southeast-2]]' +ap-southeast-2 regional_by_row_table@regional_by_row_table_pkey num_voters = 3, +voter_constraints = '[+region=ap-southeast-2]', +lease_preferences = '[[+region=ap-southeast-2]]' +ca-central-1 regional_by_row_table@regional_by_row_table_a_idx num_voters = 3, +voter_constraints = '[+region=ca-central-1]', +lease_preferences = '[[+region=ca-central-1]]' +ca-central-1 regional_by_row_table@regional_by_row_table_b_key num_voters = 3, +voter_constraints = '[+region=ca-central-1]', +lease_preferences = '[[+region=ca-central-1]]' +ca-central-1 regional_by_row_table@regional_by_row_table_j_idx num_voters = 3, +voter_constraints = '[+region=ca-central-1]', +lease_preferences = '[[+region=ca-central-1]]' +ca-central-1 regional_by_row_table@regional_by_row_table_pkey num_voters = 3, +voter_constraints = '[+region=ca-central-1]', +lease_preferences = '[[+region=ca-central-1]]' +us-east-1 regional_by_row_table@regional_by_row_table_a_idx num_voters = 3, +voter_constraints = '[+region=us-east-1]', +lease_preferences = '[[+region=us-east-1]]' +us-east-1 regional_by_row_table@regional_by_row_table_b_key num_voters = 3, +voter_constraints = '[+region=us-east-1]', +lease_preferences = '[[+region=us-east-1]]' +us-east-1 regional_by_row_table@regional_by_row_table_j_idx num_voters = 3, +voter_constraints = '[+region=us-east-1]', +lease_preferences = '[[+region=us-east-1]]' +us-east-1 regional_by_row_table@regional_by_row_table_pkey num_voters = 3, +voter_constraints = '[+region=us-east-1]', +lease_preferences = '[[+region=us-east-1]]' + +query TT +SHOW CREATE TABLE regional_by_row_table +---- +regional_by_row_table CREATE TABLE public.regional_by_row_table ( + pk INT8 NOT NULL, + pk2 INT8 NOT NULL, + a INT8 NOT NULL, + b INT8 NOT NULL, + j JSONB NULL, + crdb_region public.crdb_internal_region NOT VISIBLE NOT NULL DEFAULT default_to_database_primary_region(gateway_region())::public.crdb_internal_region, + CONSTRAINT regional_by_row_table_pkey PRIMARY KEY (pk ASC), + INDEX regional_by_row_table_a_idx (a ASC), + UNIQUE INDEX regional_by_row_table_b_key (b ASC), + INVERTED INDEX regional_by_row_table_j_idx (j), + FAMILY fam_0_pk_pk2_a_b_j_crdb_region (pk, pk2, a, b, j, crdb_region) +) LOCALITY REGIONAL BY ROW + +query TTT +SELECT partition_name, index_name, zone_config FROM [SHOW PARTITIONS FROM TABLE regional_by_table_in_primary_region] +ORDER BY partition_name, index_name +---- + +query TT +SHOW ZONE CONFIGURATION FROM TABLE regional_by_table_in_primary_region +---- +DATABASE "mr-backup-2" ALTER DATABASE "mr-backup-2" CONFIGURE ZONE USING + range_min_bytes = 134217728, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 5, + num_voters = 3, + constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', + voter_constraints = '[+region=ap-southeast-2]', + lease_preferences = '[[+region=ap-southeast-2]]' + +query TTT +SELECT partition_name, index_name, zone_config FROM [SHOW PARTITIONS FROM TABLE regional_by_table_in_ca_central_1] +ORDER BY partition_name, index_name +---- + +query TT +SHOW ZONE CONFIGURATION FROM TABLE regional_by_table_in_ca_central_1 +---- +TABLE regional_by_table_in_ca_central_1 ALTER TABLE regional_by_table_in_ca_central_1 CONFIGURE ZONE USING + range_min_bytes = 134217728, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 5, + num_voters = 3, + constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', + voter_constraints = '[+region=ca-central-1]', + lease_preferences = '[[+region=ca-central-1]]' + +query TTT +SELECT partition_name, index_name, zone_config FROM [SHOW PARTITIONS FROM TABLE global_table] +ORDER BY partition_name, index_name +---- + +query TT +SHOW ZONE CONFIGURATION FROM TABLE global_table +---- +TABLE global_table ALTER TABLE global_table CONFIGURE ZONE USING + range_min_bytes = 134217728, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + global_reads = true, + num_replicas = 5, + num_voters = 3, + constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', + voter_constraints = '[+region=ap-southeast-2]', + lease_preferences = '[[+region=ap-southeast-2]]' -statement error cannot backup individual table -BACKUP TABLE global_table TO 'nodelocal://1/rbr_table/'; statement ok CREATE DATABASE non_mr_backup; From 2d67843e9b17383f0b54b2a4267070d5e16beb5b Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Wed, 3 Nov 2021 10:51:53 +0000 Subject: [PATCH 182/205] build: fix bazel invocation of stress in github-pull-request-make Every run of the stress and stressrace bazel CI jobs were failing with: [17:16:00][Run stress tests] /bin/bash: stress: command not found I haven't dug into the Git history enough to know why this was working before. Rather, I've just copied what the `dev` tool does for me and checked that the constructed `bazci` command does in fact work. Release note: None --- pkg/cmd/github-pull-request-make/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/github-pull-request-make/main.go b/pkg/cmd/github-pull-request-make/main.go index 6c8ad8f58159..dffabb1e5b46 100644 --- a/pkg/cmd/github-pull-request-make/main.go +++ b/pkg/cmd/github-pull-request-make/main.go @@ -50,6 +50,8 @@ const targetEnv = "TARGET" // We don't want TesticularCancer. const goTestStr = `func (Test[^a-z]\w*)\(.*\*testing\.TB?\) {$` +const bazelStressTarget = "@com_github_cockroachdb_stress//:stress" + var currentGoTestRE = regexp.MustCompile(`.*` + goTestStr) var newGoTestRE = regexp.MustCompile(`^\+\s*` + goTestStr) @@ -254,13 +256,13 @@ func main() { args = append(args, "--test_arg=-test.timeout", fmt.Sprintf("--test_arg=%s", timeout)) // Give the entire test 1 more minute than the duration to wrap up. args = append(args, fmt.Sprintf("--test_timeout=%d", int((duration+1*time.Minute).Seconds()))) - // NB: stress and bazci are expected to be put in `PATH` by the caller. - args = append(args, "--run_under", fmt.Sprintf("stress -stderr -maxfails 1 -maxtime %s -p %d", duration, parallelism)) + args = append(args, "--run_under", fmt.Sprintf("%s -stderr -maxfails 1 -maxtime %s -p %d", bazelStressTarget, duration, parallelism)) if target == "stressrace" { args = append(args, "--config=race") } else { args = append(args, "--test_sharding_strategy=disabled") } + // NB: bazci is expected to be put in `PATH` by the caller. cmd := exec.Command("bazci", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr From 19402850ad83f1f358c34ae394aa1ce0501cfbf5 Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Wed, 3 Nov 2021 11:30:14 +0000 Subject: [PATCH 183/205] github-pull-request-make: allow testing command generation This adds the ability to feed in package and test names via an environment variable rather than depending on GitHub. It also allows one to force a bazel build. I found this helpful when debugging a problem with this command. Release note: None --- pkg/cmd/github-pull-request-make/main.go | 89 ++++++++++++++++++------ 1 file changed, 68 insertions(+), 21 deletions(-) diff --git a/pkg/cmd/github-pull-request-make/main.go b/pkg/cmd/github-pull-request-make/main.go index dffabb1e5b46..5552dc0440c5 100644 --- a/pkg/cmd/github-pull-request-make/main.go +++ b/pkg/cmd/github-pull-request-make/main.go @@ -31,6 +31,7 @@ import ( "os/exec" "path/filepath" "regexp" + "strconv" "strings" "time" @@ -40,9 +41,16 @@ import ( "golang.org/x/oauth2" ) -const githubAPITokenEnv = "GITHUB_API_TOKEN" -const teamcityVCSNumberEnv = "BUILD_VCS_NUMBER" -const targetEnv = "TARGET" +const ( + githubAPITokenEnv = "GITHUB_API_TOKEN" + teamcityVCSNumberEnv = "BUILD_VCS_NUMBER" + targetEnv = "TARGET" + // The following environment variables are for testing and are + // prefixed with GHM_ to help prevent accidentally triggering + // test code inside the CI pipeline. + packageEnv = "GHM_PACKAGES" + forceBazelEnv = "GHM_FORCE_BAZEL" +) // https://github.com/golang/go/blob/go1.7.3/src/cmd/go/test.go#L1260:L1262 // @@ -59,6 +67,24 @@ type pkg struct { tests []string } +func pkgsFromGithubPRForSHA( + ctx context.Context, org string, repo string, sha string, +) (map[string]pkg, error) { + client := ghClient(ctx) + currentPull := findPullRequest(ctx, client, org, repo, sha) + if currentPull == nil { + log.Printf("SHA %s not found in open pull requests, skipping stress", sha) + return nil, nil + } + + diff, err := getDiff(ctx, client, org, repo, *currentPull.Number) + if err != nil { + return nil, err + } + + return pkgsFromDiff(strings.NewReader(diff)) +} + // pkgsFromDiff parses a git-style diff and returns a mapping from directories // to tests added in those directories in the given diff. func pkgsFromDiff(r io.Reader) (map[string]pkg, error) { @@ -160,6 +186,25 @@ func getDiff( return diff, err } +func parsePackagesFromEnvironment(input string) (map[string]pkg, error) { + const expectedFormat = "PACKAGE_NAME=TEST_NAME[,TEST_NAME...][;PACKAGE_NAME=...]" + pkgTestStrs := strings.Split(input, ";") + pkgs := make(map[string]pkg, len(pkgTestStrs)) + for _, pts := range pkgTestStrs { + ptsParts := strings.Split(pts, "=") + if len(ptsParts) < 2 { + return nil, fmt.Errorf("invalid format for package environment variable: %q (expected format: %s)", + input, expectedFormat) + } + pkgName := ptsParts[0] + tests := ptsParts[1] + pkgs[pkgName] = pkg{ + tests: strings.Split(tests, ","), + } + } + return pkgs, nil +} + func main() { sha, ok := os.LookupEnv(teamcityVCSNumberEnv) if !ok { @@ -174,32 +219,34 @@ func main() { log.Fatalf("environment variable %s is %s; expected 'stress' or 'stressrace'", targetEnv, target) } - const org = "cockroachdb" - const repo = "cockroach" + forceBazel := false + if forceBazelStr, ok := os.LookupEnv(forceBazelEnv); ok { + forceBazel, _ = strconv.ParseBool(forceBazelStr) + } crdb, err := os.Getwd() if err != nil { log.Fatal(err) } - ctx := context.Background() - client := ghClient(ctx) - - currentPull := findPullRequest(ctx, client, org, repo, sha) - if currentPull == nil { - log.Printf("SHA %s not found in open pull requests, skipping stress", sha) - return - } + var pkgs map[string]pkg + if pkgStr, ok := os.LookupEnv(packageEnv); ok { + log.Printf("Using packages from environment variable %s", packageEnv) + pkgs, err = parsePackagesFromEnvironment(pkgStr) + if err != nil { + log.Fatal(err) + } - diff, err := getDiff(ctx, client, org, repo, *currentPull.Number) - if err != nil { - log.Fatal(err) + } else { + ctx := context.Background() + const org = "cockroachdb" + const repo = "cockroach" + pkgs, err = pkgsFromGithubPRForSHA(ctx, org, repo, sha) + if err != nil { + log.Fatal(err) + } } - pkgs, err := pkgsFromDiff(strings.NewReader(diff)) - if err != nil { - log.Fatal(err) - } if len(pkgs) > 0 { for name, pkg := range pkgs { // 20 minutes total seems OK, but at least 2 minutes per test. @@ -224,7 +271,7 @@ func main() { } var args []string - if bazel.BuiltWithBazel() { + if bazel.BuiltWithBazel() || forceBazel { args = append(args, "test") // NB: We use a pretty dumb technique to list the bazel test // targets: we ask bazel query to enumerate all the tests in this From 6a7a878de4e600d7d4c763e783bbfc9c0ee90b3e Mon Sep 17 00:00:00 2001 From: Steven Danna Date: Wed, 3 Nov 2021 12:25:57 +0000 Subject: [PATCH 184/205] Makefile: add proto & log dependencies to roachvet `bin/roachvet` depends on `pkg/testutils/lint/passes/fmtsafe` which depends on `pkg/util/log/logpb` which requires that the protobufs are compiled. This could result in the following failure if you happened to run `make lint` after pulling in a protobuf update (but before any other target that would recompile the protos): ``` pkg/util/log/logpb/severity.go:19:10: undefined: Severity pkg/util/log/logpb/severity.go:20:16: undefined: Severity pkg/util/log/logpb/severity.go:29:15: undefined: Severity pkg/util/log/logpb/severity.go:36:9: undefined: Severity pkg/util/log/logpb/severity.go:36:46: undefined: Severity_UNKNOWN pkg/util/log/logpb/severity.go:39:9: undefined: Severity pkg/util/log/logpb/severity.go:42:10: undefined: Severity pkg/util/log/logpb/severity.go:48:32: undefined: Severity pkg/util/log/logpb/severity.go:63:10: undefined: Severity pkg/util/log/logpb/severity.go:72:9: undefined: Severity pkg/util/log/logpb/severity.go:36:46: too many errors make: *** [Makefile:1788: bin/roachvet] Error 2 make: *** Waiting for unfinished jobs.... ``` Release note: None --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ceb6b152d8da..b35b1464daff 100644 --- a/Makefile +++ b/Makefile @@ -1764,7 +1764,7 @@ logictest-bins := bin/logictest bin/logictestopt bin/logictestccl # Additional dependencies for binaries that depend on generated code. # # TODO(benesch): Derive this automatically. This is getting out of hand. -bin/workload bin/docgen bin/execgen bin/roachtest $(logictest-bins): $(SQLPARSER_TARGETS) $(LOG_TARGETS) $(PROTOBUF_TARGETS) +bin/workload bin/docgen bin/execgen bin/roachtest bin/roachvet $(logictest-bins): $(SQLPARSER_TARGETS) $(LOG_TARGETS) $(PROTOBUF_TARGETS) bin/workload bin/docgen bin/roachtest $(logictest-bins): $(LIBPROJ) $(CGO_FLAGS_FILES) bin/roachtest $(logictest-bins): $(C_LIBS_CCL) $(CGO_FLAGS_FILES) $(OPTGEN_TARGETS) | $(C_LIBS_DYNAMIC) From 5949f80fa47f43b9f9c922872c62ab534cd53c6c Mon Sep 17 00:00:00 2001 From: Aditya Maru Date: Wed, 3 Nov 2021 13:30:51 +0000 Subject: [PATCH 185/205] jobsprotectedts: fixes test failure related to error wrapping Fixes: #72371 Release note: None --- pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go b/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go index e8bfa763a605..72894e687ce0 100644 --- a/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go +++ b/pkg/jobs/jobsprotectedts/jobs_protected_ts_test.go @@ -96,10 +96,13 @@ func TestJobsProtectedTimestamp(t *testing.T) { _, recRemains := mkJobAndRecord() ensureNotExists := func(ctx context.Context, txn *kv.Txn, ptsID uuid.UUID) (err error) { _, err = ptp.GetRecord(ctx, txn, ptsID) + if err == nil { + return errors.New("found pts record, waiting for ErrNotExists") + } if errors.Is(err, protectedts.ErrNotExists) { return nil } - return errors.NewAssertionErrorWithWrappedErrf(err, "waiting for ErrNotExists") + return errors.Wrap(err, "waiting for ErrNotExists") } testutils.SucceedsSoon(t, func() (err error) { return s0.DB().Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { @@ -173,10 +176,13 @@ func TestSchedulesProtectedTimestamp(t *testing.T) { _, recSchedule := mkScheduleAndRecord("do-not-drop") ensureNotExists := func(ctx context.Context, txn *kv.Txn, ptsID uuid.UUID) (err error) { _, err = ptp.GetRecord(ctx, txn, ptsID) + if err == nil { + return errors.New("found pts record, waiting for ErrNotExists") + } if errors.Is(err, protectedts.ErrNotExists) { return nil } - return errors.NewAssertionErrorWithWrappedErrf(err, "waiting for ErrNotExists") + return errors.Wrap(err, "waiting for ErrNotExists") } testutils.SucceedsSoon(t, func() (err error) { return s0.DB().Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { From daa6a7910d935128bb38d28bd183b9e0f4c58908 Mon Sep 17 00:00:00 2001 From: Jane Xing Date: Tue, 2 Nov 2021 19:29:17 -0500 Subject: [PATCH 186/205] roachtest: update ruby-pg blocklist for 22.1 Copied the 21.2's ruby-pg blocklist for 22.1, and deleted the test `"Basic type mapping PG::BasicTypeMapBasedOnResult with usage of result oids for copy encoder selection can type cast #copy_data input with explicit encoder",` from this new 22.1 blocklist. Fixes https://github.com/cockroachdb/cockroach/issues/72316 Release note: None --- pkg/cmd/roachtest/tests/ruby_pg_blocklist.go | 172 ++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/roachtest/tests/ruby_pg_blocklist.go b/pkg/cmd/roachtest/tests/ruby_pg_blocklist.go index c1d667b6427f..ab37708c68b2 100644 --- a/pkg/cmd/roachtest/tests/ruby_pg_blocklist.go +++ b/pkg/cmd/roachtest/tests/ruby_pg_blocklist.go @@ -17,7 +17,177 @@ var rubyPGBlocklist = blocklistsForVersion{ {"v22.1", "rubyPGBlockList22_1", rubyPGBlockList22_1, "rubyPGIgnoreList21_2", rubyPGIgnoreList22_1}, } -var rubyPGBlockList22_1 = rubyPGBlockList21_2 +var rubyPGBlockList22_1 = blocklist{ + "Basic type mapping PG::BasicTypeMapBasedOnResult with usage of result oids for bind params encoder selection can do JSON conversions": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries Record encoding should do array-as-record encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do IPAddr param encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do array of string encoding on unknown classes": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do array-as-json encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do basic Time encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do basic param encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do basic param encoding of various float values": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do bigdecimal param encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do default array-as-array param encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do default array-as-array param encoding with Time objects": "unknown", + "Basic type mapping PG::BasicTypeMapForQueries should do hash-as-json encoding": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 0 timestamps per TimestampLocal": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 0 timestamps per TimestampUtc": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 0 timestamps per TimestampUtcToLocal": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 0 timestamps with time zone": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 1 timestamps per TimestampLocal": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 1 timestamps per TimestampUtc": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 1 timestamps per TimestampUtcToLocal": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should convert format 1 timestamps with time zone": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should do array type conversions": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should do cidr type conversions": "unknown", + "Basic type mapping PG::BasicTypeMapForResults connection wide type mapping should do text datetime without time zone type conversions": "unknown", + "Basic type mapping PG::BasicTypeMapForResults with usage of result oids for copy decoder selection can type cast #copy_data output with explicit decoder": "unknown", + "PG::Connection accepts nil as the timeout in #wait_for_notify ": "unknown", + "PG::Connection allows a query to be cancelled": "unknown", + "PG::Connection automatically rolls back a transaction started with Connection#transaction if an exception is raised": "unknown", + "PG::Connection calls a block for NOTIFY events if one is given": "unknown", + "PG::Connection calls the block supplied to wait_for_notify with the notify payload if it accepts any number of arguments": "unknown", + "PG::Connection calls the block supplied to wait_for_notify with the notify payload if it accepts three arguments": "unknown", + "PG::Connection calls the block supplied to wait_for_notify with the notify payload if it accepts two arguments": "unknown", + "PG::Connection calls the block supplied to wait_for_notify with the notify payload if it doesn't accept arguments": "unknown", + "PG::Connection can handle client errors in #copy_data for input": "unknown", + "PG::Connection can handle client errors in #copy_data for output": "unknown", + "PG::Connection can handle incomplete #copy_data output queries": "unknown", + "PG::Connection can handle server errors in #copy_data for input": "unknown", + "PG::Connection can handle server errors in #copy_data for output": "unknown", + "PG::Connection can process #copy_data input queries": "unknown", + "PG::Connection can process #copy_data output queries": "unknown", + "PG::Connection can receive notices while waiting for NOTIFY without exceeding the timeout": "unknown", + "PG::Connection can wait for NOTIFY events": "unknown", + "PG::Connection correctly finishes COPY queries passed to #async_exec": "unknown", + "PG::Connection deprecated forms of methods should forward exec to exec_params": "unknown", + "PG::Connection deprecated forms of methods should forward send_query to send_query_params": "unknown", + "PG::Connection described_class#block shouldn't block a second thread": "unknown", + "PG::Connection doesn't collapse sequential notifications": "unknown", + "PG::Connection doesn't leave stale server connections after finish": "unknown", + "PG::Connection gracefully handle SQL statements while in #copy_data for input": "unknown", + "PG::Connection gracefully handle SQL statements while in #copy_data for output": "unknown", + "PG::Connection multinationalization support Ruby 1.9.x default_internal encoding allows users of the async interface to set the client_encoding to the default_internal": "unknown", + "PG::Connection multinationalization support Ruby 1.9.x default_internal encoding honors the Encoding.default_internal if it's set and the synchronous interface is used": "unknown", + "PG::Connection multinationalization support encodes exception messages with the connection's encoding (#96)": "unknown", + "PG::Connection multinationalization support handles clearing result in or after set_notice_receiver": "unknown", + "PG::Connection multinationalization support receives properly encoded messages in the notice callbacks": "unknown", + "PG::Connection multinationalization support receives properly encoded text from wait_for_notify": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert error string to #put_copy_end": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert query string and parameters to #exec_params": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert query string and parameters to #send_query_params": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings and parameters to #prepare and #exec_prepared": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings and parameters to #send_prepare and #send_query_prepared": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings to #describe_portal": "unknown", + "PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings to #send_describe_portal": "unknown", + "PG::Connection multinationalization support returns properly encoded text from notifies": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support can use an encoding with high index for client encoding": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support raises appropriate error if set_client_encoding is called with invalid arguments": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support returns the results in the correct encoding even if the client_encoding has changed since the results were fetched": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support should return results in the same encoding as the client (EUC-JP)": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support should return results in the same encoding as the client (iso-8859-1)": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support the connection should return ASCII-8BIT when it's set to SQL_ASCII": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support the connection should use JOHAB dummy encoding when it's set to JOHAB": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for escaped identifier": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for escaped literal": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for escaped string": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for quote_ident": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support uses the previous string encoding for escaped string": "unknown", + "PG::Connection multinationalization support rubyforge #22925: m17n support uses the previous string encoding for quote_ident": "unknown", + "PG::Connection not read past the end of a large object": "unknown", + "PG::Connection password encryption method can encrypt without algorithm": "unknown", + "PG::Connection returns notifications which are already in the queue before wait_for_notify is called without waiting for the socket to become readable": "unknown", + "PG::Connection returns the block result from Connection#transaction": "unknown", + "PG::Connection sends nil as the payload if the notification wasn't given one": "unknown", + "PG::Connection set_single_row_mode should receive rows before entire query fails": "unknown", + "PG::Connection set_single_row_mode should receive rows before entire query is finished": "unknown", + "PG::Connection trace and untrace client-server communication": "unknown", + "PG::Connection type casting can type cast parameters to put_copy_data with explicit encoder": "unknown", + "PG::Connection type casting shouldn't type map params unless requested": "unknown", + "PG::Connection type casting with default query type map can process #copy_data input queries with row encoder and respects character encoding": "unknown", + "PG::Connection type casting with default result type map can process #copy_data output with row decoder and respects character encoding": "unknown", + "PG::Connection type casting with default result type map can type cast #copy_data output with explicit decoder": "unknown", + "PG::Connection type casting with default result type map should respect a type mapping for result": "unknown", + "PG::Connection type casting with default result type map should work with arbitrary number of params in conjunction with type casting": "unknown", + "PG::Result encapsulates PG_DIAG_SEVERITY_NONLOCALIZED error in a PG::Error object": "unknown", + "PG::Result encapsulates database object names for integrity constraint violations": "unknown", + "PG::Result encapsulates errors in a PG::Error object": "unknown", + "PG::Result raises a proper exception for a nonexistant schema": "unknown", + "PG::TypeMapByColumn forwards get_copy_data conversions to another TypeMapByColumn as #default_type_map": "unknown", + "PG::TypeMapByColumn should gracefully handle not initialized state": "unknown", + "PG::TypeMapByColumn will deny copy queries with different column count": "unknown", + "PG::TypeMapByOid should allow building new TypeMapByColumn for a given result": "unknown", + "PG::TypeMapByOid should allow mixed type conversions in binary format": "unknown", + "PG::TypeMapByOid should allow mixed type conversions in text format": "unknown", + "PG::TypeMapByOid should build a TypeMapByColumn when assigned and the number of rows is high enough": "unknown", + "running with sync_* methods PG::Connection accepts nil as the timeout in #wait_for_notify ": "unknown", + "running with sync_* methods PG::Connection allows a query to be cancelled": "unknown", + "running with sync_* methods PG::Connection automatically rolls back a transaction started with Connection#transaction if an exception is raised": "unknown", + "running with sync_* methods PG::Connection calls a block for NOTIFY events if one is given": "unknown", + "running with sync_* methods PG::Connection calls the block supplied to wait_for_notify with the notify payload if it accepts any number of arguments": "unknown", + "running with sync_* methods PG::Connection calls the block supplied to wait_for_notify with the notify payload if it accepts three arguments": "unknown", + "running with sync_* methods PG::Connection calls the block supplied to wait_for_notify with the notify payload if it accepts two arguments": "unknown", + "running with sync_* methods PG::Connection calls the block supplied to wait_for_notify with the notify payload if it doesn't accept arguments": "unknown", + "running with sync_* methods PG::Connection can handle client errors in #copy_data for input": "unknown", + "running with sync_* methods PG::Connection can handle client errors in #copy_data for output": "unknown", + "running with sync_* methods PG::Connection can handle incomplete #copy_data output queries": "unknown", + "running with sync_* methods PG::Connection can handle server errors in #copy_data for input": "unknown", + "running with sync_* methods PG::Connection can handle server errors in #copy_data for output": "unknown", + "running with sync_* methods PG::Connection can process #copy_data input queries": "unknown", + "running with sync_* methods PG::Connection can process #copy_data output queries": "unknown", + "running with sync_* methods PG::Connection can receive notices while waiting for NOTIFY without exceeding the timeout": "unknown", + "running with sync_* methods PG::Connection can wait for NOTIFY events": "unknown", + "running with sync_* methods PG::Connection correctly finishes COPY queries passed to #async_exec": "unknown", + "running with sync_* methods PG::Connection deprecated forms of methods should forward exec to exec_params": "unknown", + "running with sync_* methods PG::Connection deprecated forms of methods should forward send_query to send_query_params": "unknown", + "running with sync_* methods PG::Connection described_class#block shouldn't block a second thread": "unknown", + "running with sync_* methods PG::Connection doesn't collapse sequential notifications": "unknown", + "running with sync_* methods PG::Connection doesn't leave stale server connections after finish": "unknown", + "running with sync_* methods PG::Connection gracefully handle SQL statements while in #copy_data for input": "unknown", + "running with sync_* methods PG::Connection gracefully handle SQL statements while in #copy_data for output": "unknown", + "running with sync_* methods PG::Connection multinationalization support Ruby 1.9.x default_internal encoding allows users of the async interface to set the client_encoding to the default_internal": "unknown", + "running with sync_* methods PG::Connection multinationalization support Ruby 1.9.x default_internal encoding honors the Encoding.default_internal if it's set and the synchronous interface is used": "unknown", + "running with sync_* methods PG::Connection multinationalization support encodes exception messages with the connection's encoding (#96)": "unknown", + "running with sync_* methods PG::Connection multinationalization support handles clearing result in or after set_notice_receiver": "unknown", + "running with sync_* methods PG::Connection multinationalization support receives properly encoded messages in the notice callbacks": "unknown", + "running with sync_* methods PG::Connection multinationalization support receives properly encoded text from wait_for_notify": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert error string to #put_copy_end": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert query string and parameters to #exec_params": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert query string and parameters to #send_query_params": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings and parameters to #prepare and #exec_prepared": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings and parameters to #send_prepare and #send_query_prepared": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings to #describe_portal": "unknown", + "running with sync_* methods PG::Connection multinationalization support respect and convert character encoding of input strings should convert strings to #send_describe_portal": "unknown", + "running with sync_* methods PG::Connection multinationalization support returns properly encoded text from notifies": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support can use an encoding with high index for client encoding": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support raises appropriate error if set_client_encoding is called with invalid arguments": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support returns the results in the correct encoding even if the client_encoding has changed since the results were fetched": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support should return results in the same encoding as the client (EUC-JP)": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support should return results in the same encoding as the client (iso-8859-1)": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support the connection should return ASCII-8BIT when it's set to SQL_ASCII": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support the connection should use JOHAB dummy encoding when it's set to JOHAB": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for escaped identifier": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for escaped literal": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for escaped string": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support uses the client encoding for quote_ident": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support uses the previous string encoding for escaped string": "unknown", + "running with sync_* methods PG::Connection multinationalization support rubyforge #22925: m17n support uses the previous string encoding for quote_ident": "unknown", + "running with sync_* methods PG::Connection not read past the end of a large object": "unknown", + "running with sync_* methods PG::Connection password encryption method can encrypt without algorithm": "unknown", + "running with sync_* methods PG::Connection returns notifications which are already in the queue before wait_for_notify is called without waiting for the socket to become readable": "unknown", + "running with sync_* methods PG::Connection returns the block result from Connection#transaction": "unknown", + "running with sync_* methods PG::Connection sends nil as the payload if the notification wasn't given one": "unknown", + "running with sync_* methods PG::Connection set_single_row_mode should receive rows before entire query fails": "unknown", + "running with sync_* methods PG::Connection set_single_row_mode should receive rows before entire query is finished": "unknown", + "running with sync_* methods PG::Connection trace and untrace client-server communication": "unknown", + "running with sync_* methods PG::Connection type casting can type cast parameters to put_copy_data with explicit encoder": "unknown", + "running with sync_* methods PG::Connection type casting shouldn't type map params unless requested": "unknown", + "running with sync_* methods PG::Connection type casting with default query type map can process #copy_data input queries with row encoder and respects character encoding": "unknown", + "running with sync_* methods PG::Connection type casting with default result type map can process #copy_data output with row decoder and respects character encoding": "unknown", + "running with sync_* methods PG::Connection type casting with default result type map can type cast #copy_data output with explicit decoder": "unknown", + "running with sync_* methods PG::Connection type casting with default result type map should respect a type mapping for result": "unknown", + "running with sync_* methods PG::Connection type casting with default result type map should work with arbitrary number of params in conjunction with type casting": "unknown", +} var rubyPGBlockList21_2 = blocklist{ "Basic type mapping PG::BasicTypeMapBasedOnResult with usage of result oids for bind params encoder selection can do JSON conversions": "unknown", From ec756f3540a9b978d00165c8d4acdac16f5ed04b Mon Sep 17 00:00:00 2001 From: richardjcai Date: Tue, 2 Nov 2021 15:28:21 -0400 Subject: [PATCH 187/205] roachtest: fix gorm roachtest Release note: None --- pkg/cmd/roachtest/tests/gorm.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/roachtest/tests/gorm.go b/pkg/cmd/roachtest/tests/gorm.go index ea6066ed693c..3593d2728954 100644 --- a/pkg/cmd/roachtest/tests/gorm.go +++ b/pkg/cmd/roachtest/tests/gorm.go @@ -22,7 +22,7 @@ import ( ) var gormReleaseTag = regexp.MustCompile(`^v(?P\d+)\.(?P\d+)\.(?P\d+)$`) -var gormSupportedTag = "v1.21.12" +var gormSupportedTag = "v1.22.2" func registerGORM(r registry.Registry) { runGORM := func(ctx context.Context, t test.Test, c cluster.Cluster) { @@ -111,7 +111,7 @@ func registerGORM(r registry.Registry) { err = c.RunE( ctx, node, - fmt.Sprintf(`cd %s && go get -t -u ./... && go mod download && go mod tidy`, gormTestPath), + fmt.Sprintf(`cd %s && go get -u -t ./... && go mod download && go mod tidy `, gormTestPath), ) require.NoError(t, err) @@ -121,8 +121,8 @@ func registerGORM(r registry.Registry) { err = c.RunE( ctx, node, - fmt.Sprintf(`cd %s && GORM_DIALECT="postgres" -PGUSER=root PGPORT=26257 PGSSLMODE=disable go test -v ./... 2>&1 | %s/bin/go-junit-report > %s`, + fmt.Sprintf(`cd %s && GORM_DIALECT="postgres" + PGUSER=root PGPORT=26257 PGSSLMODE=disable go test -v ./... 2>&1 | %s/bin/go-junit-report > %s`, gormTestPath, goPath, resultsPath), ) if err != nil { From bbe3b2ae90ea9222e9092d18d7d07271ccaea48a Mon Sep 17 00:00:00 2001 From: Ahmad Abedalqader Date: Wed, 3 Nov 2021 08:19:51 -0700 Subject: [PATCH 188/205] roachprod: avoid flaky test due to unused functions Merging #71660 trigerred a flaky test due to unused functions. This patch avoids that test by making use of / commenting unused functions. Release note: None --- pkg/cmd/roachprod/main.go | 4 +--- pkg/roachprod/roachprod.go | 3 ++- pkg/roachprod/vm/vm.go | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/roachprod/main.go b/pkg/cmd/roachprod/main.go index 6c5c2b4711d3..0f6d3be38e51 100644 --- a/pkg/cmd/roachprod/main.go +++ b/pkg/cmd/roachprod/main.go @@ -23,7 +23,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/build" "github.com/cockroachdb/cockroach/pkg/roachprod" - // cld "github.com/cockroachdb/cockroach/pkg/roachprod/cloud" "github.com/cockroachdb/cockroach/pkg/roachprod/config" rperrors "github.com/cockroachdb/cockroach/pkg/roachprod/errors" "github.com/cockroachdb/cockroach/pkg/roachprod/install" @@ -94,6 +93,7 @@ var ( quiet = false sig = 9 waitFlag = false + createVMOpts = vm.DefaultCreateOpts() startOpts = install.StartOptsType{} stageOS string stageDir string @@ -134,8 +134,6 @@ func wrap(f func(cmd *cobra.Command, args []string) error) func(cmd *cobra.Comma } } -var createVMOpts vm.CreateOpts - var createCmd = &cobra.Command{ Use: "create ", Short: "create a cluster", diff --git a/pkg/roachprod/roachprod.go b/pkg/roachprod/roachprod.go index 836fff681eda..76b0b839a87f 100644 --- a/pkg/roachprod/roachprod.go +++ b/pkg/roachprod/roachprod.go @@ -124,7 +124,6 @@ func verifyClusterName(clusterName, username string) (string, error) { } // DefaultSyncedCluster returns install.SyncedCluster with default values. -//lint:ignore U1001 unused func DefaultSyncedCluster() install.SyncedCluster { return install.SyncedCluster{ Name: "", @@ -143,6 +142,8 @@ func DefaultSyncedCluster() install.SyncedCluster { } } +var _ = DefaultSyncedCluster() + func sortedClusters() []string { var r []string for n := range install.Clusters { diff --git a/pkg/roachprod/vm/vm.go b/pkg/roachprod/vm/vm.go index 0536f182fcd5..1bd9a198893b 100644 --- a/pkg/roachprod/vm/vm.go +++ b/pkg/roachprod/vm/vm.go @@ -154,7 +154,6 @@ type CreateOpts struct { } // DefaultCreateOpts returns a new vm.CreateOpts with default values set. -//lint:ignore U1001 unused func DefaultCreateOpts() CreateOpts { defaultCreateOpts := CreateOpts{ ClusterName: "", From c4d0b044bc27bc2750e98882cb333ddffca2c134 Mon Sep 17 00:00:00 2001 From: jameswsj10 Date: Wed, 22 Sep 2021 10:54:59 -0700 Subject: [PATCH 189/205] sql: improve historical descriptor look up efficiency Fixes #70692. The existing implementation for looking up old historical descriptors required multiple round trips to storage. This improvement requires only 1, at most 2, KV calls to storage by using a single ExportRequest. Release note (performance improvement): improve efficiency of looking up old historical descriptors. --- pkg/ccl/backupccl/backup_test.go | 19 +- pkg/ccl/changefeedccl/cdctest/BUILD.bazel | 4 +- pkg/ccl/changefeedccl/cdctest/validator.go | 6 +- .../changefeedccl/cdctest/validator_test.go | 6 +- pkg/kv/kvserver/replica_rangefeed_test.go | 4 +- pkg/sql/catalog/lease/BUILD.bazel | 2 + pkg/sql/catalog/lease/lease.go | 202 +++++++++++++----- pkg/sql/catalog/lease/lease_internal_test.go | 152 +++++++++++++ pkg/sql/catalog/lease/lease_test.go | 114 ++++------ pkg/sql/drop_test.go | 3 +- pkg/sql/exec_util.go | 21 -- pkg/sql/revert_test.go | 3 +- pkg/sql/sem/tree/as_of.go | 21 ++ 13 files changed, 389 insertions(+), 168 deletions(-) diff --git a/pkg/ccl/backupccl/backup_test.go b/pkg/ccl/backupccl/backup_test.go index 65e17da5480b..15dfced572bb 100644 --- a/pkg/ccl/backupccl/backup_test.go +++ b/pkg/ccl/backupccl/backup_test.go @@ -6753,12 +6753,19 @@ func TestPaginatedBackupTenant(t *testing.T) { return fmt.Sprintf("%v%s", span.String(), spanStr) } + // Check if export request is from a lease for a descriptor to avoid picking + // up on wrong export requests + isLeasingExportRequest := func(r *roachpb.ExportRequest) bool { + _, tenantID, _ := keys.DecodeTenantPrefix(r.Key) + codec := keys.MakeSQLCodec(tenantID) + return bytes.HasPrefix(r.Key, codec.DescMetadataPrefix()) && + r.EndKey.Equal(r.Key.PrefixEnd()) + } params.ServerArgs.Knobs.Store = &kvserver.StoreTestingKnobs{ TestingRequestFilter: func(ctx context.Context, request roachpb.BatchRequest) *roachpb.Error { for _, ru := range request.Requests { - switch ru.GetInner().(type) { - case *roachpb.ExportRequest: - exportRequest := ru.GetInner().(*roachpb.ExportRequest) + if exportRequest, ok := ru.GetInner().(*roachpb.ExportRequest); ok && + !isLeasingExportRequest(exportRequest) { exportRequestSpans = append( exportRequestSpans, requestSpanStr(roachpb.Span{Key: exportRequest.Key, EndKey: exportRequest.EndKey}, exportRequest.ResumeKeyTS), @@ -6769,9 +6776,9 @@ func TestPaginatedBackupTenant(t *testing.T) { return nil }, TestingResponseFilter: func(ctx context.Context, ba roachpb.BatchRequest, br *roachpb.BatchResponse) *roachpb.Error { - for _, ru := range br.Responses { - switch ru.GetInner().(type) { - case *roachpb.ExportResponse: + for i, ru := range br.Responses { + if exportRequest, ok := ba.Requests[i].GetInner().(*roachpb.ExportRequest); ok && + !isLeasingExportRequest(exportRequest) { exportResponse := ru.GetInner().(*roachpb.ExportResponse) // Every ExportResponse should have a single SST when running backup // within a tenant. diff --git a/pkg/ccl/changefeedccl/cdctest/BUILD.bazel b/pkg/ccl/changefeedccl/cdctest/BUILD.bazel index d654a9e8ab06..e2d4f68cb935 100644 --- a/pkg/ccl/changefeedccl/cdctest/BUILD.bazel +++ b/pkg/ccl/changefeedccl/cdctest/BUILD.bazel @@ -17,7 +17,7 @@ go_library( "//pkg/jobs", "//pkg/jobs/jobspb", "//pkg/roachpb:with-mocks", - "//pkg/sql", + "//pkg/sql/sem/tree", "//pkg/testutils/serverutils", "//pkg/util/fsm", "//pkg/util/hlc", @@ -45,7 +45,7 @@ go_test( "//pkg/security", "//pkg/security/securitytest", "//pkg/server", - "//pkg/sql", + "//pkg/sql/sem/tree", "//pkg/testutils", "//pkg/testutils/serverutils", "//pkg/testutils/sqlutils", diff --git a/pkg/ccl/changefeedccl/cdctest/validator.go b/pkg/ccl/changefeedccl/cdctest/validator.go index 78f360205a74..eb9561e5fbc5 100644 --- a/pkg/ccl/changefeedccl/cdctest/validator.go +++ b/pkg/ccl/changefeedccl/cdctest/validator.go @@ -17,7 +17,7 @@ import ( "strings" "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/sql" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/errors" ) @@ -675,14 +675,14 @@ func ParseJSONValueTimestamps(v []byte) (updated, resolved hlc.Timestamp, err er } if valueRaw.Updated != `` { var err error - updated, err = sql.ParseHLC(valueRaw.Updated) + updated, err = tree.ParseHLC(valueRaw.Updated) if err != nil { return hlc.Timestamp{}, hlc.Timestamp{}, err } } if valueRaw.Resolved != `` { var err error - resolved, err = sql.ParseHLC(valueRaw.Resolved) + resolved, err = tree.ParseHLC(valueRaw.Resolved) if err != nil { return hlc.Timestamp{}, hlc.Timestamp{}, err } diff --git a/pkg/ccl/changefeedccl/cdctest/validator_test.go b/pkg/ccl/changefeedccl/cdctest/validator_test.go index af4786fa110e..3990672bdaa5 100644 --- a/pkg/ccl/changefeedccl/cdctest/validator_test.go +++ b/pkg/ccl/changefeedccl/cdctest/validator_test.go @@ -15,7 +15,7 @@ import ( "testing" "github.com/cockroachdb/cockroach/pkg/base" - "github.com/cockroachdb/cockroach/pkg/sql" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" @@ -127,7 +127,7 @@ func TestBeforeAfterValidator(t *testing.T) { ts := make([]hlc.Timestamp, len(tsRaw)) for i := range tsRaw { var err error - ts[i], err = sql.ParseHLC(tsRaw[i]) + ts[i], err = tree.ParseHLC(tsRaw[i]) if err != nil { t.Fatal(err) } @@ -280,7 +280,7 @@ func TestFingerprintValidator(t *testing.T) { ts := make([]hlc.Timestamp, len(tsRaw)) for i := range tsRaw { var err error - ts[i], err = sql.ParseHLC(tsRaw[i]) + ts[i], err = tree.ParseHLC(tsRaw[i]) if err != nil { t.Fatal(err) } diff --git a/pkg/kv/kvserver/replica_rangefeed_test.go b/pkg/kv/kvserver/replica_rangefeed_test.go index bf87f2a0a1ad..181281781790 100644 --- a/pkg/kv/kvserver/replica_rangefeed_test.go +++ b/pkg/kv/kvserver/replica_rangefeed_test.go @@ -27,7 +27,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/server" "github.com/cockroachdb/cockroach/pkg/settings/cluster" - "github.com/cockroachdb/cockroach/pkg/sql" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" @@ -873,7 +873,7 @@ func TestReplicaRangefeedPushesTransactions(t *testing.T) { // if it ever gets pushed. var ts2Str string require.NoError(t, tx1.QueryRowContext(ctx, "SELECT cluster_logical_timestamp()").Scan(&ts2Str)) - ts2, err := sql.ParseHLC(ts2Str) + ts2, err := tree.ParseHLC(ts2Str) require.NoError(t, err) // Wait for the RangeFeed checkpoint on each RangeFeed to exceed this timestamp. diff --git a/pkg/sql/catalog/lease/BUILD.bazel b/pkg/sql/catalog/lease/BUILD.bazel index f76b46893634..4234d159929d 100644 --- a/pkg/sql/catalog/lease/BUILD.bazel +++ b/pkg/sql/catalog/lease/BUILD.bazel @@ -25,12 +25,14 @@ go_library( "//pkg/settings", "//pkg/settings/cluster", "//pkg/sql/catalog", + "//pkg/sql/catalog/catalogkeys", "//pkg/sql/catalog/catalogkv", "//pkg/sql/catalog/descpb", "//pkg/sql/catalog/nstree", "//pkg/sql/sem/tree", "//pkg/sql/sessiondata", "//pkg/sql/sqlutil", + "//pkg/storage", "//pkg/util/grpcutil", "//pkg/util/hlc", "//pkg/util/log", diff --git a/pkg/sql/catalog/lease/lease.go b/pkg/sql/catalog/lease/lease.go index 7091cbdb08a9..7c5594fa9c5b 100644 --- a/pkg/sql/catalog/lease/lease.go +++ b/pkg/sql/catalog/lease/lease.go @@ -27,11 +27,13 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkeys" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" + kvstorage "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/log/logcrash" @@ -183,76 +185,174 @@ type historicalDescriptor struct { expiration hlc.Timestamp // ModificationTime of the next descriptor } -// Read an older descriptor version for the particular timestamp -// from the store. We unfortunately need to read more than one descriptor -// version just so that we can set the expiration time on the descriptor -// properly. +// Retrieves historical descriptors of given id within the lower and upper bound +// timestamp from the MVCC key range in decreasing modification time order. Any +// descriptor versions that were modified in the range [lower, upper) will be +// retrieved through an export request. A lower bound of an empty timestamp +// hlc.Timestamp{} will result in an error. // -// TODO(vivek): Future work: -// 1. Read multiple versions of a descriptor through one kv call. -// 2. Translate multiple simultaneous calls to this method into a single call +// In the following scenario v4 is our oldest active lease +// [v1@t1 ][v2@t3 ][v3@t5 ][v4@t7 +// [start end] +// getDescriptorsFromStoreForInterval(..., start, end) will get back: +// [v3, v2] (reverse order) +// +// Note that this does not necessarily retrieve a descriptor version that was +// alive at the lower bound timestamp. +func getDescriptorsFromStoreForInterval( + ctx context.Context, + db *kv.DB, + codec keys.SQLCodec, + id descpb.ID, + lowerBound, upperBound hlc.Timestamp, +) ([]historicalDescriptor, error) { + // Ensure lower bound is not an empty timestamp (now). + if lowerBound.Logical == 0 && lowerBound.WallTime == 0 { + return nil, errors.New("Lower bound for export request cannot be 0") + } + + // Create an export request (1 kv call) for all descriptors for given + // descriptor ID written during the interval [timestamp, endTimestamp). + batchRequestHeader := roachpb.Header{} + if upperBound.WallTime != 0 { + batchRequestHeader = roachpb.Header{Timestamp: upperBound.Prev()} + } + descriptorKey := catalogkeys.MakeDescMetadataKey(codec, id) + requestHeader := roachpb.RequestHeader{ + Key: descriptorKey, + EndKey: descriptorKey.PrefixEnd(), + } + req := &roachpb.ExportRequest{ + RequestHeader: requestHeader, + StartTime: lowerBound.Prev(), + MVCCFilter: roachpb.MVCCFilter_All, + ReturnSST: true, + } + + // Export request returns descriptors in decreasing modification time. + res, pErr := kv.SendWrappedWith(ctx, db.NonTransactionalSender(), batchRequestHeader, req) + if pErr != nil { + return nil, errors.Wrapf(pErr.GoError(), "error in retrieving descs between %s, %s", + lowerBound, upperBound) + } + + // Unmarshal key span retrieved from export request to construct historical descs. + var descriptorsRead []historicalDescriptor + // Keep track of the most recently processed descriptor's modification time to + // set as the expiration for the next descriptor to process. Recall we process + // descriptors in decreasing modification time. + subsequentModificationTime := upperBound + for _, file := range res.(*roachpb.ExportResponse).Files { + if err := func() error { + it, err := kvstorage.NewMemSSTIterator(file.SST, false /* verify */) + if err != nil { + return err + } + defer it.Close() + + // Convert each MVCC key value pair corresponding to the specified + // descriptor ID. + for it.SeekGE(kvstorage.NilKey); ; it.Next() { + if ok, err := it.Valid(); err != nil { + return err + } else if !ok { + return nil + } + + // Decode key and value of descriptor. + k := it.UnsafeKey() + descContent := it.UnsafeValue() + if descContent == nil { + return errors.Wrapf(errors.New("unsafe value error"), "error "+ + "extracting raw bytes of descriptor with key %s modified between "+ + "%s, %s", k.String(), k.Timestamp, subsequentModificationTime) + } + + // Construct a plain descriptor. + value := roachpb.Value{RawBytes: descContent} + var desc descpb.Descriptor + if err := value.GetProto(&desc); err != nil { + return err + } + descBuilder := catalogkv.NewBuilderWithMVCCTimestamp(&desc, k.Timestamp) + + // Construct a historical descriptor with expiration. + histDesc := historicalDescriptor{ + desc: descBuilder.BuildImmutable(), + expiration: subsequentModificationTime, + } + descriptorsRead = append(descriptorsRead, histDesc) + + // Update the expiration time for next descriptor. + subsequentModificationTime = k.Timestamp + } + }(); err != nil { + return nil, err + } + } + return descriptorsRead, nil +} + +// Read older descriptor versions for the particular timestamp from store +// through an ExportRequest. The ExportRequest queries the key span for versions +// in range [timestamp, earliest modification time in memory) or [timestamp:] if +// there are no active leases in memory. This is followed by a call to +// getForExpiration in case the ExportRequest doesn't grab the earliest +// descriptor version we are interested in, resulting at most 2 KV calls. +// +// TODO(vivek, james): Future work: +// 1. Translate multiple simultaneous calls to this method into a single call // as is done for acquireNodeLease(). -// 3. Figure out a sane policy on when these descriptors should be purged. +// 2. Figure out a sane policy on when these descriptors should be purged. // They are currently purged in PurgeOldVersions. func (m *Manager) readOlderVersionForTimestamp( ctx context.Context, id descpb.ID, timestamp hlc.Timestamp, ) ([]historicalDescriptor, error) { - expiration, done := func() (hlc.Timestamp, bool) { - t := m.findDescriptorState(id, false /* create */) + // Retrieve the endTimestamp for our query, which will be the modification + // time of the first descriptor in the manager's active set. + t := m.findDescriptorState(id, false /*create*/) + endTimestamp := func() hlc.Timestamp { t.mu.Lock() defer t.mu.Unlock() - afterIdx := 0 - // Walk back the versions to find one that is valid for the timestamp. - for i := len(t.mu.active.data) - 1; i >= 0; i-- { - // Check to see if the ModificationTime is valid. - if desc := t.mu.active.data[i]; desc.GetModificationTime().LessEq(timestamp) { - if expiration := desc.getExpiration(); timestamp.Less(expiration) { - // Existing valid descriptor version. - return expiration, true - } - // We need a version after data[i], but before data[i+1]. - // We could very well use the timestamp to read the - // descriptor, but unfortunately we will not be able to assign - // it a proper expiration time. Therefore, we read - // descriptor versions one by one from afterIdx back into the - // past until we find a valid one. - afterIdx = i + 1 - break - } + if len(t.mu.active.data) == 0 { + return hlc.Timestamp{} } + return t.mu.active.data[0].GetModificationTime() + }() - if afterIdx == len(t.mu.active.data) { - return hlc.Timestamp{}, true - } + // Retrieve descriptors in range [timestamp, endTimestamp) in decreasing + // modification time order. + descs, err := getDescriptorsFromStoreForInterval(ctx, m.DB(), m.Codec(), id, timestamp, endTimestamp) + if err != nil { + return nil, err + } - // Read descriptor versions one by one into the past until we - // find a valid one. Every version is assigned an expiration time that - // is the ModificationTime of the previous one read. - return t.mu.active.data[afterIdx].GetModificationTime(), false - }() - if done { - return nil, nil + // In the case where the descriptor we're looking for is modified before the + // input timestamp, we get the descriptor before the earliest descriptor we + // have from either in memory or from the call to + // getDescriptorsFromStoreForInterval. + var earliestModificationTime hlc.Timestamp + if len(descs) == 0 { + earliestModificationTime = endTimestamp + } else { + earliestModificationTime = descs[len(descs)-1].desc.GetModificationTime() } - // Read descriptors from the store. - var versions []historicalDescriptor - for { - desc, err := m.storage.getForExpiration(ctx, expiration, id) + // Unless the timestamp is exactly at the earliest modification time from + // ExportRequest, we'll invoke another call to retrieve the descriptor with + // modification time prior to the timestamp. + if timestamp.Less(earliestModificationTime) { + desc, err := m.storage.getForExpiration(ctx, earliestModificationTime, id) if err != nil { return nil, err } - versions = append(versions, historicalDescriptor{ + descs = append(descs, historicalDescriptor{ desc: desc, - expiration: expiration, + expiration: earliestModificationTime, }) - if desc.GetModificationTime().LessEq(timestamp) { - break - } - // Set the expiration time for the next descriptor. - expiration = desc.GetModificationTime() } - return versions, nil + return descs, nil } // Insert descriptor versions. The versions provided are not in @@ -773,7 +873,7 @@ type LeasedDescriptor interface { } // Acquire acquires a read lease for the specified descriptor ID valid for -// the timestamp. It returns the descriptor and a expiration time. +// the timestamp. It returns the descriptor and an expiration time. // A transaction using this descriptor must ensure that its // commit-timestamp < expiration-time. Care must be taken to not modify // the returned descriptor. diff --git a/pkg/sql/catalog/lease/lease_internal_test.go b/pkg/sql/catalog/lease/lease_internal_test.go index 834d82b95105..036d74c2c3ab 100644 --- a/pkg/sql/catalog/lease/lease_internal_test.go +++ b/pkg/sql/catalog/lease/lease_internal_test.go @@ -29,10 +29,13 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/skip" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/stop" "github.com/cockroachdb/errors" "github.com/cockroachdb/logtags" + "github.com/stretchr/testify/require" ) func TestTableSet(t *testing.T) { @@ -1051,3 +1054,152 @@ func TestLeaseAcquireAndReleaseConcurrently(t *testing.T) { }) } } + +// Tests retrieving older versions within a given start and end timestamp of a +// table descriptor from store through an ExportRequest. +func TestReadOlderVersionForTimestamp(t *testing.T) { + defer leaktest.AfterTest(t)() + serverParams := base.TestServerArgs{ + Knobs: base.TestingKnobs{ + SQLLeaseManager: &ManagerTestingKnobs{ + TestingDescriptorUpdateEvent: func(_ *descpb.Descriptor) error { + return errors.New("Caught race between resetting state and refreshing leases") + }, + }, + }, + } + var stopper *stop.Stopper + s, sqlDB, _ := serverutils.StartServer(t, serverParams) + stopper = s.Stopper() + ctx := context.Background() + defer stopper.Stop(ctx) + + tdb := sqlutils.MakeSQLRunner(sqlDB) + // Prevent non-explicit Acquire to leases for testing purposes. + tdb.Exec(t, "SET CLUSTER SETTING sql.tablecache.lease.refresh_limit = 0") + tdb.Exec(t, "CREATE TABLE foo (i INT PRIMARY KEY)") + var tableID descpb.ID + tdb.QueryRow(t, "SELECT id FROM system.namespace WHERE name = 'foo'").Scan(&tableID) + + manager := s.LeaseManager().(*Manager) + const N = 5 + descs := make([]catalog.Descriptor, N+1) + + // Create N versions of table descriptor + for i := 0; i < N; i++ { + _, err := manager.Publish(ctx, tableID, func(desc catalog.MutableDescriptor) error { + descs[i] = desc.ImmutableCopy() + return nil + }, nil) + require.NoError(t, err) + } + { + last, err := manager.Acquire(ctx, s.Clock().Now(), tableID) + require.NoError(t, err) + descs[N] = last.Underlying() + last.Release(ctx) + } + + type version int + type testCase struct { + before []version + ts hlc.Timestamp + tsStr string + expected []version + } + versionTS := func(v version) hlc.Timestamp { + return descs[v-1].GetModificationTime() + } + versionDesc := func(v version) catalog.Descriptor { + return descs[v-1] + } + resetDescriptorState := func( + manager *Manager, tableID descpb.ID, tc testCase, + ) { + manager.mu.Lock() + defer manager.mu.Unlock() + descStates := manager.mu.descriptors + descStates[tableID] = &descriptorState{m: manager, id: tableID} + for _, v := range tc.before { + addedDescVState := &descriptorVersionState{ + t: descStates[tableID], + Descriptor: versionDesc(v), + } + addedDescVState.mu.Lock() + addedDescVState.mu.expiration = hlc.MaxTimestamp + addedDescVState.mu.Unlock() + descStates[tableID].mu.active.insert(addedDescVState) + } + } + + // Test historical read for descriptors as of specific timestamps and confirm + // expected data. + // [v1 ---)[v2 --)[v3 ---)[v4 ----)[v5 -----)[v6 ------) + for _, tc := range []testCase{ + { + before: []version{}, + ts: versionTS(1), + tsStr: "ts1", + expected: []version{1, 2, 3, 4, 5, 6}, + }, + { + before: []version{}, + ts: versionTS(4), + tsStr: "ts4", + expected: []version{4, 5, 6}, + }, + { + before: []version{}, + ts: versionTS(6), + tsStr: "ts6", + expected: []version{6}, + }, + { + before: []version{}, + ts: versionTS(6).Prev(), + tsStr: "ts6.Prev", + expected: []version{5, 6}, + }, + { + before: []version{6}, + ts: versionTS(4).Prev(), + tsStr: "ts4.Prev", + expected: []version{3, 4, 5}, + }, + { + before: []version{6}, + ts: versionTS(5), + tsStr: "ts5", + expected: []version{5}, + }, + { + before: []version{5, 6}, + ts: versionTS(3).Prev(), + tsStr: "ts3.Prev", + expected: []version{2, 3, 4}, + }, + { + before: []version{1, 2, 3, 4, 5, 6}, + ts: versionTS(4), + tsStr: "ts4", + expected: []version{}, + }, + } { + t.Run(fmt.Sprintf("%v@%v->%v", tc.before, tc.tsStr, tc.expected), func(t *testing.T) { + // Reset the descriptor state to before versions. + resetDescriptorState(manager, tableID, tc) + + // Retrieve historicalDescriptors modification times. + retrieved, err := manager.readOlderVersionForTimestamp(ctx, tableID, tc.ts) + require.NoError(t, err) + + // Validate retrieved descriptors match expected versions. + retrievedVersions := make([]version, 0) + for _, desc := range retrieved { + ver := version(desc.desc.GetVersion()) + retrievedVersions = append([]version{ver}, retrievedVersions...) + } + require.Equal(t, tc.expected, retrievedVersions) + }) + } +} diff --git a/pkg/sql/catalog/lease/lease_test.go b/pkg/sql/catalog/lease/lease_test.go index 1116755621e3..c7e05520debf 100644 --- a/pkg/sql/catalog/lease/lease_test.go +++ b/pkg/sql/catalog/lease/lease_test.go @@ -52,7 +52,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" "github.com/cockroachdb/cockroach/pkg/util" - "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/stop" @@ -2505,99 +2504,58 @@ func TestHistoricalAcquireDroppedDescriptor(t *testing.T) { tdb.CheckQueryResults(t, `WITH a AS (SELECT 'a'::`+typeName+`) SELECT * FROM a AS OF SYSTEM TIME `+now, [][]string{{"a"}}) } -// Test that attempts to use a descriptor at a timestamp that precedes when -// a descriptor is dropped but follows the notification that that descriptor -// was dropped will successfully acquire the lease. -func TestLeaseAcquireAfterDropWithEarlierTimestamp(t *testing.T) { +// Tests acquiring read leases on previous versions of a table descriptor from +// store. +func TestHistoricalDescriptorAcquire(t *testing.T) { defer leaktest.AfterTest(t)() - - // descID is the ID of the table we're dropping. - var descID atomic.Value - descID.Store(descpb.ID(0)) - type refreshEvent struct { - unblock chan struct{} - ts hlc.Timestamp - } - refreshed := make(chan refreshEvent) var stopper *stop.Stopper tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{ - ServerArgs: base.TestServerArgs{ - Knobs: base.TestingKnobs{ - SQLLeaseManager: &lease.ManagerTestingKnobs{ - TestingDescriptorRefreshedEvent: func(descriptor *descpb.Descriptor) { - if descpb.GetDescriptorID(descriptor) != descID.Load().(descpb.ID) { - return - } - unblock := make(chan struct{}) - select { - case refreshed <- refreshEvent{ - unblock: unblock, - ts: descpb.GetDescriptorModificationTime(descriptor), - }: - case <-stopper.ShouldQuiesce(): - } - select { - case <-unblock: - case <-stopper.ShouldQuiesce(): - } - }, - }, - }, - }, + ServerArgs: base.TestServerArgs{}, }) stopper = tc.Stopper() ctx := context.Background() defer stopper.Stop(ctx) tdb := sqlutils.MakeSQLRunner(tc.ServerConn(0)) - // Create a schema, create a table in that schema, insert into it, drop it, - // detect the drop has made its way to the lease manager and thus the lease - // has been removed, and note the timestamp at which the drop occurred, then - // ensure that the descriptors can be read at the previous timestamp. + // Create a schema, create table, alter table a few times to get some history + // of tables while keeping timestamp checkpoints for acquire query tdb.Exec(t, "CREATE SCHEMA sc") tdb.Exec(t, "CREATE TABLE sc.foo (i INT PRIMARY KEY)") tdb.Exec(t, "INSERT INTO sc.foo VALUES (1)") - { + + var ts1Str string + tdb.QueryRow(t, "SELECT cluster_logical_timestamp()").Scan(&ts1Str) + ts1, err := tree.ParseHLC(ts1Str) + require.NoError(t, err) + + tdb.Exec(t, "ALTER TABLE sc.foo ADD COLUMN id UUID NOT NULL DEFAULT gen_random_uuid()") + tdb.Exec(t, "ALTER TABLE sc.foo RENAME COLUMN i TO former_id") + tdb.Exec(t, "ALTER TABLE sc.foo RENAME COLUMN id TO current_id") + + // Store table descriptor ID + var tableID atomic.Value + storeID := func(val *atomic.Value, name string) { var id descpb.ID - tdb.QueryRow(t, `SELECT id FROM system.namespace WHERE name = $1`, "sc").Scan(&id) + tdb.QueryRow(t, `SELECT id FROM system.namespace WHERE name = $1`, name).Scan(&id) require.NotEqual(t, descpb.ID(0), id) - descID.Store(id) + val.Store(id) } - dropErr := make(chan error, 1) - go func() { - _, err := tc.ServerConn(0).Exec("DROP SCHEMA sc CASCADE") - dropErr <- err - }() + storeID(&tableID, "foo") + + // Acquire descriptor version valid at timestamp ts1. Waits for the most + // recent version with the name column before doing so. + _, err = tc.Server(0).LeaseManager().(*lease.Manager).WaitForOneVersion(ctx, tableID.Load().(descpb.ID), base.DefaultRetryOptions()) + require.NoError(t, err, "Failed to wait for one version of descriptor: %s", err) + acquiredDescriptor, err := + tc.Server(0).LeaseManager().(*lease.Manager).Acquire(ctx, ts1, tableID.Load().(descpb.ID)) + assert.NoError(t, err) - // Observe that the lease manager has now marked the descriptor as dropped. - ev := <-refreshed - - // Ensure that reads at the previous timestamp will succeed. Before the - // commit that introduced this test, they would fail because the fallback - // used to read the table descriptor from the store did not exist for the - // schema. After this commit, there is no fallback and the lease manager - // properly serves the right version for both. - tdb.CheckQueryResults(t, - "SELECT * FROM sc.foo AS OF SYSTEM TIME "+ev.ts.Prev().AsOfSystemTime(), - [][]string{{"1"}}) - - // Test that using a timestamp equal to the timestamp at which the descriptor - // is dropped results in the proper error. - tdb.ExpectErr(t, `relation "sc.foo" does not exist`, - "SELECT * FROM sc.foo AS OF SYSTEM TIME "+ev.ts.AsOfSystemTime()) - - // Also ensure that the subsequent timestamp gets the same error. - tdb.ExpectErr(t, `relation "sc.foo" does not exist`, - "SELECT * FROM sc.foo AS OF SYSTEM TIME "+ev.ts.Next().AsOfSystemTime()) - - // Allow everything to continue. - close(ev.unblock) - require.NoError(t, <-dropErr) - - // Test again, after the namespace entry has been fully removed, that the - // query returns the exact same error. - tdb.ExpectErr(t, `relation "sc.foo" does not exist`, - "SELECT * FROM sc.foo AS OF SYSTEM TIME "+ev.ts.AsOfSystemTime()) + // Ensure the modificationTime <= timestamp < expirationTime + modificationTime := acquiredDescriptor.Underlying().GetModificationTime() + assert.Truef(t, modificationTime.LessEq(ts1) && + ts1.Less(acquiredDescriptor.Expiration()), "modification: %s, ts1: %s, "+ + "expiration: %s", modificationTime.String(), ts1.String(), + acquiredDescriptor.Expiration().String()) } func TestDropDescriptorRacesWithAcquisition(t *testing.T) { diff --git a/pkg/sql/drop_test.go b/pkg/sql/drop_test.go index d6943fcd1d45..70475a0640ea 100644 --- a/pkg/sql/drop_test.go +++ b/pkg/sql/drop_test.go @@ -37,6 +37,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/execinfra" "github.com/cockroachdb/cockroach/pkg/sql/gcjob" "github.com/cockroachdb/cockroach/pkg/sql/row" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/sql/sqltestutils" "github.com/cockroachdb/cockroach/pkg/sql/tests" "github.com/cockroachdb/cockroach/pkg/startupmigrations" @@ -1206,7 +1207,7 @@ WHERE tdb.Exec(t, "INSERT INTO foo VALUES (1)") var afterInsertStr string tdb.QueryRow(t, "SELECT cluster_logical_timestamp()").Scan(&afterInsertStr) - afterInsert, err := sql.ParseHLC(afterInsertStr) + afterInsert, err := tree.ParseHLC(afterInsertStr) require.NoError(t, err) // Now set up a filter to detect when the DROP INDEX execution will begin and diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index 0688229d3a19..e2eb4483ee0a 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -1605,27 +1605,6 @@ func (p *planner) EvalAsOfTimestamp( return asOf, nil } -// ParseHLC parses a string representation of an `hlc.Timestamp`. -// This differs from hlc.ParseTimestamp in that it parses the decimal -// serialization of an hlc timestamp as opposed to the string serialization -// performed by hlc.Timestamp.String(). -// -// This function is used to parse: -// -// 1580361670629466905.0000000001 -// -// hlc.ParseTimestamp() would be used to parse: -// -// 1580361670.629466905,1 -// -func ParseHLC(s string) (hlc.Timestamp, error) { - dec, _, err := apd.NewFromString(s) - if err != nil { - return hlc.Timestamp{}, err - } - return tree.DecimalToHLC(dec) -} - // isAsOf analyzes a statement to bypass the logic in newPlan(), since // that requires the transaction to be started already. If the returned // timestamp is not nil, it is the timestamp to which a transaction diff --git a/pkg/sql/revert_test.go b/pkg/sql/revert_test.go index 15d2e1599938..df2f6e59591b 100644 --- a/pkg/sql/revert_test.go +++ b/pkg/sql/revert_test.go @@ -23,6 +23,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog" "github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" @@ -56,7 +57,7 @@ func TestRevertTable(t *testing.T) { var ts string var before int db.QueryRow(t, `SELECT cluster_logical_timestamp(), xor_agg(k # rev) FROM test`).Scan(&ts, &before) - targetTime, err := sql.ParseHLC(ts) + targetTime, err := tree.ParseHLC(ts) require.NoError(t, err) const ignoreGC = false diff --git a/pkg/sql/sem/tree/as_of.go b/pkg/sql/sem/tree/as_of.go index 641fd61e5eb5..55ee64aa7dbe 100644 --- a/pkg/sql/sem/tree/as_of.go +++ b/pkg/sql/sem/tree/as_of.go @@ -313,3 +313,24 @@ func DecimalToHLC(d *apd.Decimal) (hlc.Timestamp, error) { Logical: int32(logical), }, nil } + +// ParseHLC parses a string representation of an `hlc.Timestamp`. +// This differs from hlc.ParseTimestamp in that it parses the decimal +// serialization of an hlc timestamp as opposed to the string serialization +// performed by hlc.Timestamp.String(). +// +// This function is used to parse: +// +// 1580361670629466905.0000000001 +// +// hlc.ParseTimestamp() would be used to parse: +// +// 1580361670.629466905,1 +// +func ParseHLC(s string) (hlc.Timestamp, error) { + dec, _, err := apd.NewFromString(s) + if err != nil { + return hlc.Timestamp{}, err + } + return DecimalToHLC(dec) +} From 045c0aad442ce1e8926550c6e8de72c064ba8f07 Mon Sep 17 00:00:00 2001 From: Tobias Grieger Date: Wed, 3 Nov 2021 14:19:51 +0100 Subject: [PATCH 190/205] kvserver: use correct `t` in TestReplicaRangefeedRetryErrors Ran into this when running with ad-hoc code that would execute `t.Skip` in `NewTestCluster`. This test would fail since the `t.Skip` call would be occurring on the wrong `t`. Release note: None --- pkg/kv/kvserver/replica_rangefeed_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/kv/kvserver/replica_rangefeed_test.go b/pkg/kv/kvserver/replica_rangefeed_test.go index bf87f2a0a1ad..1bd0b38f9fc1 100644 --- a/pkg/kv/kvserver/replica_rangefeed_test.go +++ b/pkg/kv/kvserver/replica_rangefeed_test.go @@ -386,9 +386,9 @@ func TestReplicaRangefeedRetryErrors(t *testing.T) { ctx := context.Background() startKey := []byte("a") - setup := func(subT *testing.T) ( + setup := func(t *testing.T) ( *testcluster.TestCluster, roachpb.RangeID) { - subT.Helper() + t.Helper() tc := testcluster.StartTestCluster(t, 3, base.TestClusterArgs{ @@ -416,9 +416,9 @@ func TestReplicaRangefeedRetryErrors(t *testing.T) { } waitForInitialCheckpointAcrossSpan := func( - subT *testing.T, stream *testStream, streamErrC <-chan *roachpb.Error, span roachpb.Span, + t *testing.T, stream *testStream, streamErrC <-chan *roachpb.Error, span roachpb.Span, ) { - subT.Helper() + t.Helper() noResolveTimestampEvent := roachpb.RangeFeedEvent{ Checkpoint: &roachpb.RangeFeedCheckpoint{ Span: span, @@ -443,7 +443,7 @@ func TestReplicaRangefeedRetryErrors(t *testing.T) { return nil }) if len(streamErrC) > 0 { - subT.Fatalf("unexpected error from stream: %v", <-streamErrC) + t.Fatalf("unexpected error from stream: %v", <-streamErrC) } expEvents := []*roachpb.RangeFeedEvent{&noResolveTimestampEvent} if len(events) > 1 { @@ -457,25 +457,25 @@ func TestReplicaRangefeedRetryErrors(t *testing.T) { } } if !reflect.DeepEqual(events, expEvents) { - subT.Fatalf("incorrect events on stream, found %v, want %v", events, expEvents) + t.Fatalf("incorrect events on stream, found %v, want %v", events, expEvents) } } assertRangefeedRetryErr := func( - subT *testing.T, pErr *roachpb.Error, expReason roachpb.RangeFeedRetryError_Reason, + t *testing.T, pErr *roachpb.Error, expReason roachpb.RangeFeedRetryError_Reason, ) { - subT.Helper() + t.Helper() expErr := roachpb.NewRangeFeedRetryError(expReason) if pErr == nil { - subT.Fatalf("got nil error for RangeFeed: expecting %v", expErr) + t.Fatalf("got nil error for RangeFeed: expecting %v", expErr) } rfErr, ok := pErr.GetDetail().(*roachpb.RangeFeedRetryError) if !ok { - subT.Fatalf("got incorrect error for RangeFeed: %v; expecting %v", pErr, expErr) + t.Fatalf("got incorrect error for RangeFeed: %v; expecting %v", pErr, expErr) } if rfErr.Reason != expReason { - subT.Fatalf("got incorrect RangeFeedRetryError reason for RangeFeed: %v; expecting %v", + t.Fatalf("got incorrect RangeFeedRetryError reason for RangeFeed: %v; expecting %v", rfErr.Reason, expReason) } } From 0cc76c5346e7790f13421bbf5488db704a123901 Mon Sep 17 00:00:00 2001 From: Darin Peshev Date: Wed, 27 Oct 2021 16:16:21 -0700 Subject: [PATCH 191/205] server: add current time to instant metrics We currently have two metrics - user and system cpu times that reflect the time spend in userspace and system space by the process. We want to use these to compute CPU utilization for the tenant processes. To do that however, we need to also know the absolute amount of time that passed between two consecutive measurements. While this is possible to be done by the caller, this isn't ideal as any delays in the metrics retrieval will skew the result. Retrieval retries may be performed automatically and be invisible for the caller. To ensure that these delays don't affect the calculation, this PR adds an additional metric to the two already avilable instant metrics - the current time. That allows a more precise comutation of the CPU loads, unaffected by the retrieval delays. Release note: None --- pkg/ccl/serverccl/tenant_vars_test.go | 12 +++++++++++- pkg/server/status/runtime.go | 9 +++++++++ pkg/server/tenant.go | 4 ++++ pkg/ts/catalog/chart_catalog.go | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/ccl/serverccl/tenant_vars_test.go b/pkg/ccl/serverccl/tenant_vars_test.go index f59058721492..c56d41700a8a 100644 --- a/pkg/ccl/serverccl/tenant_vars_test.go +++ b/pkg/ccl/serverccl/tenant_vars_test.go @@ -22,6 +22,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/elastic/gosigar" io_prometheus_client "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" @@ -46,6 +47,7 @@ func TestTenantVars(t *testing.T) { TenantID: roachpb.MakeTenantID(10 /* id */), }) + startNowNanos := timeutil.Now().UnixNano() url := "https://" + tenant.HTTPAddr() + "/_status/load" client := http.Client{ Transport: &http.Transport{ @@ -70,18 +72,26 @@ func TestTenantVars(t *testing.T) { sysCPU, found := metrics["sys_cpu_sys_ns"] require.True(t, found) - require.True(t, found) require.Len(t, sysCPU.GetMetric(), 1) require.Equal(t, io_prometheus_client.MetricType_GAUGE, sysCPU.GetType()) cpuSysNanos := sysCPU.Metric[0].GetGauge().GetValue() + now, found := metrics["sys_cpu_now_ns"] + require.True(t, found) + require.Len(t, now.GetMetric(), 1) + require.Equal(t, io_prometheus_client.MetricType_GAUGE, now.GetType()) + nowNanos := now.Metric[0].GetGauge().GetValue() + // The values are between zero and whatever User/Sys time is observed after the get. require.Positive(t, cpuUserNanos) require.Positive(t, cpuSysNanos) + require.Positive(t, nowNanos) cpuTime := gosigar.ProcTime{} require.NoError(t, cpuTime.Get(os.Getpid())) require.LessOrEqual(t, cpuUserNanos, float64(cpuTime.User)*1e6) require.LessOrEqual(t, cpuSysNanos, float64(cpuTime.Sys)*1e6) + require.GreaterOrEqual(t, nowNanos, float64(startNowNanos)) + require.LessOrEqual(t, nowNanos, float64(timeutil.Now().UnixNano())) resp, err = client.Get(url) require.NoError(t, err) diff --git a/pkg/server/status/runtime.go b/pkg/server/status/runtime.go index b26c4017677f..a9118d7f8c31 100644 --- a/pkg/server/status/runtime.go +++ b/pkg/server/status/runtime.go @@ -120,6 +120,12 @@ var ( Measurement: "CPU Time", Unit: metric.Unit_PERCENT, } + metaCPUNowNS = metric.Metadata{ + Name: "sys.cpu.now.ns", + Help: "Number of nanoseconds elapsed since January 1, 1970 UTC", + Measurement: "CPU Time", + Unit: metric.Unit_NANOSECONDS, + } metaRSSBytes = metric.Metadata{ Name: "sys.rss", Help: "Current process RSS", @@ -283,6 +289,7 @@ type RuntimeStatSampler struct { CPUSysNS *metric.Gauge CPUSysPercent *metric.GaugeFloat64 CPUCombinedPercentNorm *metric.GaugeFloat64 + CPUNowNS *metric.Gauge // Memory stats. RSSBytes *metric.Gauge // File descriptor stats. @@ -360,6 +367,7 @@ func NewRuntimeStatSampler(ctx context.Context, clock *hlc.Clock) *RuntimeStatSa CPUSysNS: metric.NewGauge(metaCPUSysNS), CPUSysPercent: metric.NewGaugeFloat64(metaCPUSysPercent), CPUCombinedPercentNorm: metric.NewGaugeFloat64(metaCPUCombinedPercentNorm), + CPUNowNS: metric.NewGauge(metaCPUNowNS), RSSBytes: metric.NewGauge(metaRSSBytes), HostDiskReadBytes: metric.NewGauge(metaHostDiskReadBytes), HostDiskReadCount: metric.NewGauge(metaHostDiskReadCount), @@ -570,6 +578,7 @@ func (rsr *RuntimeStatSampler) SampleEnvironment( rsr.CPUSysNS.Update(stime) rsr.CPUSysPercent.Update(srate) rsr.CPUCombinedPercentNorm.Update(combinedNormalizedPerc) + rsr.CPUNowNS.Update(now) rsr.FDOpen.Update(int64(fds.Open)) rsr.FDSoftLimit.Update(int64(fds.SoftLimit)) rsr.RSSBytes.Update(int64(mem.Resident)) diff --git a/pkg/server/tenant.go b/pkg/server/tenant.go index 7c8a74b83a7a..e51f07885495 100644 --- a/pkg/server/tenant.go +++ b/pkg/server/tenant.go @@ -324,9 +324,11 @@ func loadVarsHandler( ) func(http.ResponseWriter, *http.Request) { cpuUserNanos := metric.NewGauge(rsr.CPUUserNS.GetMetadata()) cpuSysNanos := metric.NewGauge(rsr.CPUSysNS.GetMetadata()) + cpuNowNanos := metric.NewGauge(rsr.CPUNowNS.GetMetadata()) registry := metric.NewRegistry() registry.AddMetric(cpuUserNanos) registry.AddMetric(cpuSysNanos) + registry.AddMetric(cpuNowNanos) return func(w http.ResponseWriter, r *http.Request) { userTimeMillis, sysTimeMillis, err := status.GetCPUTime(ctx) @@ -334,11 +336,13 @@ func loadVarsHandler( // Just log but don't return an error to match the _status/vars metrics handler. log.Ops.Errorf(ctx, "unable to get cpu usage: %v", err) } + // cpuTime.{User,Sys} are in milliseconds, convert to nanoseconds. utime := userTimeMillis * 1e6 stime := sysTimeMillis * 1e6 cpuUserNanos.Update(utime) cpuSysNanos.Update(stime) + cpuNowNanos.Update(timeutil.Now().UnixNano()) exporter := metric.MakePrometheusExporter() exporter.ScrapeRegistry(registry, true) diff --git a/pkg/ts/catalog/chart_catalog.go b/pkg/ts/catalog/chart_catalog.go index 5b57da403df8..5909bb0c9b8c 100644 --- a/pkg/ts/catalog/chart_catalog.go +++ b/pkg/ts/catalog/chart_catalog.go @@ -119,6 +119,7 @@ var charts = []sectionDescription{ Metrics: []string{ "sys.cpu.sys.ns", "sys.cpu.user.ns", + "sys.cpu.now.ns", }, }, }, From 89f8aba84346fcae27c55dc253a3f574c9eb8e2e Mon Sep 17 00:00:00 2001 From: irfan sharif Date: Thu, 28 Oct 2021 16:13:44 -0400 Subject: [PATCH 192/205] spanconfig: introduce the spanconfig.KVSubscriber KVSubscriber presents a consistent[^1] snapshot of a spanconfig.StoreReader that's incrementally maintained with changes made to the global span configurations state. The maintenance happens transparently; callers can subscribe to learn about what key spans may have seen a configuration change. After learning about a span update, consulting the embedded StoreReader would retrieve an up-to-date[^2] config for it. When a callback is first installed, it's invoked with the [min,max) span -- a shorthand to indicate that subscribers should consult the StoreReader for all spans of interest. Subsequent updates are of the more incremental kind. It's possible that the span updates received are no-ops, i.e. consulting the StoreReader for the given span would retrieve the last config observed for the span[^2]. type KVSubscriber interface { StoreReader Subscribe(func(updated roachpb.Span)) } It's expected to Start-ed once, after which one or many subscribers can listen in for updates. Internally we maintain a rangefeed over the global store of span configurations (system.span_configurations), applying updates from it into an embedded spanconfig.Store. A read-only view of this data structure (spanconfig.StoreReader) is exposed as part of the KVSubscriber interface. Rangefeeds used as is don't offer any ordering guarantees with respect to updates made over non-overlapping keys, which is something we care about[^4]. For that reason we make use of a rangefeed buffer, accumulating raw rangefeed updates and flushing them out en-masse in timestamp order when the rangefeed frontier is bumped[^5]. If the buffer overflows (as dictated by the memory limit the KVSubscriber is instantiated with), the old rangefeed is wound down and a new one re-established. When running into the internal errors described above, it's safe for us to re-establish the underlying rangefeeds. When re-establishing a new rangefeed and populating a spanconfig.Store using the contents of the initial scan[3], we wish to preserve the existing spanconfig.StoreReader. Discarding it would entail either blocking all external readers until a new spanconfig.StoreReader was fully populated, or presenting an inconsistent view of the spanconfig.Store that's currently being populated. For new rangefeeds what we do then is route all updates from the initial scan to a fresh spanconfig.Store, and once the initial scan is done, swap at the source for the exported spanconfig.StoreReader. During the initial scan, concurrent readers would continue to observe the last spanconfig.StoreReader if any. After the swap, it would observe the more up-to-date source instead. Future incremental updates will also target the new source. When this source swap occurs, we inform the handler of the need to possibly refresh its view of all configs. This commit also wires up the KVSubscriber into KV stores, replacing the use of the gossiped system config span (possible given the StoreReader interface, only happens if a testing flag/env var is set). [^1]: The contents of the StoreReader at t1 corresponds exactly to the contents of the global span configuration state at t0 where t0 <= t1. If the StoreReader is read from at t2 where t2 > t1, it's guaranteed to observe a view of the global state at t >= t0. [^2]: For the canonical KVSubscriber implementation, this is typically the closed timestamp target duration. [^3]: The canonical KVSubscriber implementation internally re-establishes feeds when errors occur, possibly re-transmitting earlier updates (usually through a lazy [min,max) span) despite possibly not needing to. We could do a bit better and diff the two data structures, emitting only targeted updates. [^4]: For a given key k, it's config may be stored as part of a larger span S (where S.start <= k < S.end). It's possible for S to get deleted and replaced with sub-spans S1...SN in the same transaction if the span is getting split. When applying these updates, we need to make sure to process the deletion event for S before processing S1...SN. [^5]: In our example above deleting the config for S and adding configs for S1...SN, we want to make sure that we apply the full set of updates all at once -- lest we expose the intermediate state where the config for S was deleted but the configs for S1...SN were not yet applied. [^6]: When tearing down the subscriber due to underlying errors, we could also surface a checkpoint to use the next time the subscriber is established. That way we can avoid the full initial scan over the span configuration state and simply pick up where we left off with our existing spanconfig.Store. Release note: None --- pkg/BUILD.bazel | 1 + pkg/keys/constants.go | 9 +- pkg/keys/spans.go | 3 + pkg/kv/kvclient/rangefeed/rangefeed.go | 6 +- pkg/kv/kvserver/BUILD.bazel | 5 + pkg/kv/kvserver/client_spanconfigs_test.go | 119 +++++ pkg/kv/kvserver/store.go | 160 ++++++- pkg/kv/kvserver/testing_knobs.go | 4 + pkg/server/BUILD.bazel | 1 + pkg/server/server.go | 27 ++ pkg/spanconfig/spanconfig.go | 72 ++- pkg/spanconfig/spanconfigjob/job.go | 4 +- .../spanconfigkvsubscriber/BUILD.bazel | 76 +++ .../spanconfigkvsubscriber/datadriven_test.go | 312 ++++++++++++ .../spanconfigkvsubscriber/kvsubscriber.go | 446 ++++++++++++++++++ .../kvsubscriber_test.go | 18 + .../spanconfigkvsubscriber/main_test.go | 31 ++ .../span_config_decoder.go | 118 +++++ .../span_config_decoder_test.go | 128 +++++ .../spanconfigkvsubscriber/testdata/basic | 48 ++ .../testdata/buffer_overflow | 83 ++++ .../testdata/initial_state | 27 ++ pkg/spanconfig/spanconfigstore/BUILD.bazel | 1 + pkg/spanconfig/spanconfigstore/shadow.go | 4 + pkg/spanconfig/spanconfigstore/store.go | 14 +- pkg/spanconfig/testing_knobs.go | 24 +- pkg/sql/catalog/systemschema/system.go | 9 +- 27 files changed, 1708 insertions(+), 42 deletions(-) create mode 100644 pkg/kv/kvserver/client_spanconfigs_test.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/BUILD.bazel create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/datadriven_test.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber_test.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/main_test.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder_test.go create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/testdata/basic create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/testdata/buffer_overflow create mode 100644 pkg/spanconfig/spanconfigkvsubscriber/testdata/initial_state diff --git a/pkg/BUILD.bazel b/pkg/BUILD.bazel index 38d9147867af..c04272934912 100644 --- a/pkg/BUILD.bazel +++ b/pkg/BUILD.bazel @@ -181,6 +181,7 @@ ALL_TESTS = [ "//pkg/server:server_test", "//pkg/settings:settings_test", "//pkg/spanconfig/spanconfigkvaccessor:spanconfigkvaccessor_test", + "//pkg/spanconfig/spanconfigkvsubscriber:spanconfigkvsubscriber_test", "//pkg/spanconfig/spanconfigmanager:spanconfigmanager_test", "//pkg/spanconfig/spanconfigsqltranslator:spanconfigsqltranslator_test", "//pkg/spanconfig/spanconfigstore:spanconfigstore_test", diff --git a/pkg/keys/constants.go b/pkg/keys/constants.go index eaeda96e7a98..35def95cd9e7 100644 --- a/pkg/keys/constants.go +++ b/pkg/keys/constants.go @@ -357,10 +357,11 @@ const ( ZonesTableConfigColumnID = 2 ZonesTableConfigColFamID = 2 - DescriptorTablePrimaryKeyIndexID = 1 - DescriptorTableDescriptorColID = 2 - DescriptorTableDescriptorColFamID = 2 - TenantsTablePrimaryKeyIndexID = 1 + DescriptorTablePrimaryKeyIndexID = 1 + DescriptorTableDescriptorColID = 2 + DescriptorTableDescriptorColFamID = 2 + TenantsTablePrimaryKeyIndexID = 1 + SpanConfigurationsTablePrimaryKeyIndexID = 1 // Reserved IDs for other system tables. Note that some of these IDs refer // to "Ranges" instead of a Table - these IDs are needed to store custom diff --git a/pkg/keys/spans.go b/pkg/keys/spans.go index d611b2ee8a02..02e513ada545 100644 --- a/pkg/keys/spans.go +++ b/pkg/keys/spans.go @@ -13,6 +13,9 @@ package keys import "github.com/cockroachdb/cockroach/pkg/roachpb" var ( + // EverythingSpan is a span that covers everything. + EverythingSpan = roachpb.Span{Key: roachpb.KeyMin, EndKey: roachpb.KeyMax} + // Meta1Span holds all first level addressing records. Meta1Span = roachpb.Span{Key: roachpb.KeyMin, EndKey: Meta2Prefix} diff --git a/pkg/kv/kvclient/rangefeed/rangefeed.go b/pkg/kv/kvclient/rangefeed/rangefeed.go index 0ef98e9281ef..ed2e74ac109e 100644 --- a/pkg/kv/kvclient/rangefeed/rangefeed.go +++ b/pkg/kv/kvclient/rangefeed/rangefeed.go @@ -193,9 +193,9 @@ func (f *RangeFeed) Start(ctx context.Context) error { return nil } -// Close closes the RangeFeed and waits for it to shut down; it does -// idempotently. It's guaranteed that no future handlers will be invoked after -// this point. +// Close closes the RangeFeed and waits for it to shut down; it does so +// idempotently. It waits for the currently running handler, if any, to complete +// and guarantees that no future handlers will be invoked after this point. func (f *RangeFeed) Close() { f.closeOnce.Do(func() { f.cancel() diff --git a/pkg/kv/kvserver/BUILD.bazel b/pkg/kv/kvserver/BUILD.bazel index 96aa6e48f6de..5a8171940b37 100644 --- a/pkg/kv/kvserver/BUILD.bazel +++ b/pkg/kv/kvserver/BUILD.bazel @@ -151,6 +151,7 @@ go_library( "//pkg/settings", "//pkg/settings/cluster", "//pkg/spanconfig", + "//pkg/spanconfig/spanconfigstore", "//pkg/sql/sessiondata", "//pkg/sql/sqlutil", "//pkg/storage", @@ -218,6 +219,7 @@ go_test( "client_replica_backpressure_test.go", "client_replica_gc_test.go", "client_replica_test.go", + "client_spanconfigs_test.go", "client_split_burst_test.go", "client_split_test.go", "client_status_test.go", @@ -340,6 +342,7 @@ go_test( "//pkg/server/telemetry", "//pkg/settings/cluster", "//pkg/spanconfig", + "//pkg/spanconfig/spanconfigstore", "//pkg/sql", "//pkg/sql/catalog/bootstrap", "//pkg/sql/catalog/catalogkeys", @@ -349,6 +352,8 @@ go_test( "//pkg/sql/catalog/tabledesc", "//pkg/sql/rowenc", "//pkg/sql/sem/tree", + "//pkg/sql/sessiondata", + "//pkg/sql/sqlutil", "//pkg/storage", "//pkg/storage/enginepb", "//pkg/storage/fs", diff --git a/pkg/kv/kvserver/client_spanconfigs_test.go b/pkg/kv/kvserver/client_spanconfigs_test.go new file mode 100644 index 000000000000..74efc495d655 --- /dev/null +++ b/pkg/kv/kvserver/client_spanconfigs_test.go @@ -0,0 +1,119 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package kvserver_test + +import ( + "context" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv/kvserver" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/spanconfig" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigstore" + "github.com/cockroachdb/cockroach/pkg/sql/sessiondata" + "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/errors" + "github.com/stretchr/testify/require" +) + +// TestSpanConfigUpdateAppliedToReplica ensures that when a store learns of a +// span config update, it installs the corresponding config on the right +// replica. +func TestSpanConfigUpdateAppliedToReplica(t *testing.T) { + defer leaktest.AfterTest(t)() + + spanConfigStore := spanconfigstore.New(roachpb.TestingDefaultSpanConfig()) + mockSubscriber := newMockSpanConfigSubscriber(spanConfigStore) + + ctx := context.Background() + + args := base.TestServerArgs{ + EnableSpanConfigs: true, + Knobs: base.TestingKnobs{ + Store: &kvserver.StoreTestingKnobs{ + DisableMergeQueue: true, + DisableSplitQueue: true, + }, + SpanConfig: &spanconfig.TestingKnobs{ + StoreKVSubscriberOverride: mockSubscriber, + }, + }, + } + s, _, _ := serverutils.StartServer(t, args) + defer s.Stopper().Stop(context.Background()) + + _, err := s.InternalExecutor().(sqlutil.InternalExecutor).ExecEx(ctx, "inline-exec", nil, + sessiondata.InternalExecutorOverride{User: security.RootUserName()}, + `SET CLUSTER SETTING spanconfig.experimental_store.enabled = true`) + require.NoError(t, err) + + key, err := s.ScratchRange() + require.NoError(t, err) + store, err := s.GetStores().(*kvserver.Stores).GetStore(s.GetFirstStoreID()) + require.NoError(t, err) + repl := store.LookupReplica(keys.MustAddr(key)) + span := repl.Desc().RSpan().AsRawSpanWithNoLocals() + conf := roachpb.SpanConfig{NumReplicas: 5, NumVoters: 3} + + deleted, added := spanConfigStore.Apply(ctx, spanconfig.Update{Span: span, Config: conf}, false /* dryrun */) + require.Empty(t, deleted) + require.Len(t, added, 1) + require.True(t, added[0].Span.Equal(span)) + require.True(t, added[0].Config.Equal(conf)) + + require.NotNil(t, mockSubscriber.callback) + mockSubscriber.callback(span) // invoke the callback + testutils.SucceedsSoon(t, func() error { + repl := store.LookupReplica(keys.MustAddr(key)) + gotConfig := repl.SpanConfig() + if !gotConfig.Equal(conf) { + return errors.Newf("expected config=%s, got config=%s", conf.String(), gotConfig.String()) + } + return nil + }) +} + +func newMockSpanConfigSubscriber(store spanconfig.Store) *mockSpanConfigSubscriber { + return &mockSpanConfigSubscriber{Store: store} +} + +type mockSpanConfigSubscriber struct { + callback func(config roachpb.Span) + spanconfig.Store +} + +func (m *mockSpanConfigSubscriber) NeedsSplit(ctx context.Context, start, end roachpb.RKey) bool { + return m.Store.NeedsSplit(ctx, start, end) +} + +func (m *mockSpanConfigSubscriber) ComputeSplitKey( + ctx context.Context, start, end roachpb.RKey, +) roachpb.RKey { + return m.Store.ComputeSplitKey(ctx, start, end) +} + +func (m *mockSpanConfigSubscriber) GetSpanConfigForKey( + ctx context.Context, key roachpb.RKey, +) (roachpb.SpanConfig, error) { + return m.Store.GetSpanConfigForKey(ctx, key) +} + +func (m *mockSpanConfigSubscriber) Subscribe(callback func(roachpb.Span)) { + m.callback = callback +} + +var _ spanconfig.KVSubscriber = &mockSpanConfigSubscriber{} diff --git a/pkg/kv/kvserver/store.go b/pkg/kv/kvserver/store.go index c3019eb04ab1..7c80312f6930 100644 --- a/pkg/kv/kvserver/store.go +++ b/pkg/kv/kvserver/store.go @@ -53,6 +53,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/spanconfig" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigstore" "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/storage/enginepb" @@ -217,7 +218,6 @@ func testStoreConfig(clock *hlc.Clock, version roachpb.Version) StoreConfig { tracer := tracing.NewTracerWithOpt(context.TODO(), tracing.WithClusterSettings(&st.SV)) sc := StoreConfig{ DefaultSpanConfig: zonepb.DefaultZoneConfigRef().AsSpanConfig(), - DefaultSystemSpanConfig: zonepb.DefaultSystemZoneConfigRef().AsSpanConfig(), Settings: st, AmbientCtx: log.AmbientContext{Tracer: tracer}, Clock: clock, @@ -651,6 +651,7 @@ type Store struct { computeInitialMetrics sync.Once systemConfigUpdateQueueRateLimiter *quotapool.RateLimiter + spanConfigUpdateQueueRateLimiter *quotapool.RateLimiter } var _ kv.Sender = &Store{} @@ -663,18 +664,17 @@ type StoreConfig struct { AmbientCtx log.AmbientContext base.RaftConfig - DefaultSpanConfig roachpb.SpanConfig - DefaultSystemSpanConfig roachpb.SpanConfig - Settings *cluster.Settings - Clock *hlc.Clock - DB *kv.DB - Gossip *gossip.Gossip - NodeLiveness *liveness.NodeLiveness - StorePool *StorePool - Transport *RaftTransport - NodeDialer *nodedialer.Dialer - RPCContext *rpc.Context - RangeDescriptorCache *rangecache.RangeCache + DefaultSpanConfig roachpb.SpanConfig + Settings *cluster.Settings + Clock *hlc.Clock + DB *kv.DB + Gossip *gossip.Gossip + NodeLiveness *liveness.NodeLiveness + StorePool *StorePool + Transport *RaftTransport + NodeDialer *nodedialer.Dialer + RPCContext *rpc.Context + RangeDescriptorCache *rangecache.RangeCache ClosedTimestampSender *sidetransport.Sender ClosedTimestampReceiver sidetransportReceiver @@ -759,6 +759,10 @@ type StoreConfig struct { // SpanConfigsEnabled determines whether we're able to use the span configs // infrastructure. SpanConfigsEnabled bool + // Used to subscribe to span configuration changes, keeping up-to-date a + // data structure useful for retrieving span configs. Only available if + // SpanConfigsEnabled. + SpanConfigSubscriber spanconfig.KVSubscriber // KVAdmissionController is an optional field used for admission control. KVAdmissionController KVAdmissionController @@ -1635,6 +1639,26 @@ func (s *Store) Start(ctx context.Context, stopper *stop.Stopper) error { }) } + if s.cfg.SpanConfigsEnabled { + s.cfg.SpanConfigSubscriber.Subscribe(func(update roachpb.Span) { + s.onSpanConfigUpdate(ctx, update) + }) + + // When toggling between the system config span and the span configs + // infrastructure, we want to re-apply configs on all replicas from + // whatever the new source is. + spanconfigstore.EnabledSetting.SetOnChange(&s.ClusterSettings().SV, func(ctx context.Context) { + enabled := spanconfigstore.EnabledSetting.Get(&s.ClusterSettings().SV) + if enabled { + s.applyAllFromSpanConfigStore(ctx) + } else { + if s.cfg.Gossip != nil && s.cfg.Gossip.GetSystemConfig() != nil { + s.systemGossipUpdate(s.cfg.Gossip.GetSystemConfig()) + } + } + }) + } + if !s.cfg.TestingKnobs.DisableAutomaticLeaseRenewal { s.startLeaseRenewer(ctx) } @@ -1776,6 +1800,10 @@ func (s *Store) GetConfReader() (spanconfig.StoreReader, error) { return nil, errSysCfgUnavailable } + if s.cfg.SpanConfigsEnabled && spanconfigstore.EnabledSetting.Get(&s.ClusterSettings().SV) { + return spanconfigstore.NewShadowReader(s.cfg.SpanConfigSubscriber, sysCfg), nil + } + return sysCfg, nil } @@ -1930,11 +1958,11 @@ func (s *Store) systemGossipUpdate(sysCfg *config.SystemConfig) { // We'll want to offer all replicas to the split and merge queues. Be a little // careful about not spawning too many individual goroutines. + shouldQueue := s.systemConfigUpdateQueueRateLimiter.AdmitN(1) // For every range, update its zone config and check if it needs to // be split or merged. now := s.cfg.Clock.NowAsClockTimestamp() - shouldQueue := s.systemConfigUpdateQueueRateLimiter.AdmitN(1) newStoreReplicaVisitor(s).Visit(func(repl *Replica) bool { key := repl.Desc().StartKey conf, err := sysCfg.GetSpanConfigForKey(ctx, key) @@ -1957,6 +1985,110 @@ func (s *Store) systemGossipUpdate(sysCfg *config.SystemConfig) { }) } +// onSpanConfigUpdate is the callback invoked whenever this store learns of a +// span config update. +func (s *Store) onSpanConfigUpdate(ctx context.Context, updated roachpb.Span) { + if !spanconfigstore.EnabledSetting.Get(&s.ClusterSettings().SV) { + return + } + + sp, err := keys.SpanAddr(updated) + if err != nil { + log.Errorf(ctx, "skipped applying update (%s), unexpected error resolving span address: %v", + updated, err) + return + } + + now := s.cfg.Clock.NowAsClockTimestamp() + if err := s.mu.replicasByKey.VisitKeyRange(ctx, sp.Key, sp.EndKey, AscendingKeyOrder, + func(ctx context.Context, it replicaOrPlaceholder) error { + repl := it.repl + if repl == nil { + return nil // placeholder; ignore + } + + startKey := repl.Desc().StartKey + if !sp.ContainsKey(startKey) { + // It's possible that the update we're receiving here is the + // right-hand side of a span config getting split. Think of + // installing a zone config on some partition of an index where + // previously there was none on any of the partitions. The range + // spanning the entire index would have to split on the + // partition boundary, and before it does so, it's possible that + // it would receive a span config update for just the partition. + // + // To avoid clobbering the pre-split range's embedded span + // config with the partition's config, we'll ensure that the + // range's start key is part of the update. We don't have to + // enqueue the range in the split queue here, that takes place + // when processing the left-hand side span config update. + + return nil // ignore + } + + // TODO(irfansharif): It's possible for a config to be applied over an + // entire range when it only pertains to the first half of the range. + // This will be corrected shortly -- we enqueue the range for a split + // below where we then apply the right config on each half. But still, + // it's surprising behavior and gets in the way of a desirable + // consistency guarantee: a key's config at any point in time is one + // that was explicitly declared over it, or the default config. + // + // We can do better, we can skip applying the config entirely and + // enqueue the split, then relying on the split trigger to install + // the right configs on each half. The current structure is as it is + // to maintain parity with the system config span variant. + + replCtx := repl.AnnotateCtx(ctx) + conf, err := s.cfg.SpanConfigSubscriber.GetSpanConfigForKey(replCtx, startKey) + if err != nil { + log.Errorf(ctx, "skipped applying update, unexpected error reading from subscriber: %v", err) + return err + } + repl.SetSpanConfig(conf) + + // TODO(irfansharif): For symmetry with the system config span variant, + // we queue blindly; we could instead only queue it if we knew the + // range's keyspans has a split in there somewhere, or was now part of a + // larger range and eligible for a merge. + s.splitQueue.Async(replCtx, "span config update", true /* wait */, func(ctx context.Context, h queueHelper) { + h.MaybeAdd(ctx, repl, now) + }) + s.mergeQueue.Async(replCtx, "span config update", true /* wait */, func(ctx context.Context, h queueHelper) { + h.MaybeAdd(ctx, repl, now) + }) + return nil // more + }, + ); err != nil { + // Errors here should not be possible, but if there is one, log loudly. + log.Errorf(ctx, "unexpected error visiting replicas: %v", err) + } +} + +// applyAllFromSpanConfigStore applies, on each replica, span configs from the +// embedded span config store. +func (s *Store) applyAllFromSpanConfigStore(ctx context.Context) { + now := s.cfg.Clock.NowAsClockTimestamp() + newStoreReplicaVisitor(s).Visit(func(repl *Replica) bool { + replCtx := repl.AnnotateCtx(ctx) + key := repl.Desc().StartKey + conf, err := s.cfg.SpanConfigSubscriber.GetSpanConfigForKey(replCtx, key) + if err != nil { + log.Errorf(ctx, "skipped applying config update, unexpected error reading from subscriber: %v", err) + return true // more + } + + repl.SetSpanConfig(conf) + s.splitQueue.Async(replCtx, "span config update", true /* wait */, func(ctx context.Context, h queueHelper) { + h.MaybeAdd(ctx, repl, now) + }) + s.mergeQueue.Async(replCtx, "span config update", true /* wait */, func(ctx context.Context, h queueHelper) { + h.MaybeAdd(ctx, repl, now) + }) + return true // more + }) +} + func (s *Store) asyncGossipStore(ctx context.Context, reason string, useCached bool) { if err := s.stopper.RunAsyncTask( ctx, fmt.Sprintf("storage.Store: gossip on %s", reason), diff --git a/pkg/kv/kvserver/testing_knobs.go b/pkg/kv/kvserver/testing_knobs.go index 786b01a86c98..eba3f2c6028d 100644 --- a/pkg/kv/kvserver/testing_knobs.go +++ b/pkg/kv/kvserver/testing_knobs.go @@ -18,6 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/kv/kvserver/tenantrate" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/txnwait" "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/spanconfig" "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/syncutil" @@ -327,6 +328,9 @@ type StoreTestingKnobs struct { // PurgeOutdatedReplicasInterceptor intercepts attempts to purge outdated // replicas in the store. PurgeOutdatedReplicasInterceptor func() + // SpanConfigUpdateInterceptor is called after the store hears about a span + // config update. + SpanConfigUpdateInterceptor func(spanconfig.Update) // If set, use the given version as the initial replica version when // bootstrapping ranges. This is used for testing the migration // infrastructure. diff --git a/pkg/server/BUILD.bazel b/pkg/server/BUILD.bazel index 867dce5b597f..80ac4fd11f22 100644 --- a/pkg/server/BUILD.bazel +++ b/pkg/server/BUILD.bazel @@ -113,6 +113,7 @@ go_library( "//pkg/spanconfig", "//pkg/spanconfig/spanconfigjob", "//pkg/spanconfig/spanconfigkvaccessor", + "//pkg/spanconfig/spanconfigkvsubscriber", "//pkg/spanconfig/spanconfigmanager", "//pkg/spanconfig/spanconfigsqltranslator", "//pkg/sql", diff --git a/pkg/server/server.go b/pkg/server/server.go index d805f5e54517..480db62f0864 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -71,6 +71,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/spanconfig" _ "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigjob" // register jobs declared outside of pkg/sql "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvaccessor" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvsubscriber" "github.com/cockroachdb/cockroach/pkg/sql" "github.com/cockroachdb/cockroach/pkg/sql/catalog/systemschema" "github.com/cockroachdb/cockroach/pkg/sql/contention" @@ -177,6 +178,8 @@ type Server struct { protectedtsProvider protectedts.Provider protectedtsReconciler *ptreconcile.Reconciler + spanConfigSubscriber *spanconfigkvsubscriber.KVSubscriber + sqlServer *SQLServer drainSleepFn func(time.Duration) @@ -612,8 +615,26 @@ func NewServer(cfg Config, stopper *stop.Stopper) (*Server, error) { } var spanConfigAccessor spanconfig.KVAccessor + var spanConfigSubscriber *spanconfigkvsubscriber.KVSubscriber if cfg.SpanConfigsEnabled { storeCfg.SpanConfigsEnabled = true + spanConfigKnobs, _ := cfg.TestingKnobs.SpanConfig.(*spanconfig.TestingKnobs) + if spanConfigKnobs != nil && spanConfigKnobs.StoreKVSubscriberOverride != nil { + storeCfg.SpanConfigSubscriber = spanConfigKnobs.StoreKVSubscriberOverride + } else { + spanConfigSubscriber = spanconfigkvsubscriber.New( + stopper, + db, + clock, + rangeFeedFactory, + keys.SpanConfigurationsTableID, + 1<<20, /* 1 MB */ + storeCfg.DefaultSpanConfig, + spanConfigKnobs, + ) + storeCfg.SpanConfigSubscriber = spanConfigSubscriber + } + spanConfigAccessor = spanconfigkvaccessor.New( db, internalExecutor, cfg.Settings, systemschema.SpanConfigurationsTableName.FQString(), @@ -808,6 +829,7 @@ func NewServer(cfg Config, stopper *stop.Stopper) (*Server, error) { replicationReporter: replicationReporter, protectedtsProvider: protectedtsProvider, protectedtsReconciler: protectedtsReconciler, + spanConfigSubscriber: spanConfigSubscriber, sqlServer: sqlServer, drainSleepFn: drainSleepFn, externalStorageBuilder: externalStorageBuilder, @@ -1726,6 +1748,11 @@ func (s *Server) PreStart(ctx context.Context) error { return err } + if s.cfg.SpanConfigsEnabled && s.spanConfigSubscriber != nil { + if err := s.spanConfigSubscriber.Start(ctx); err != nil { + return err + } + } // Start garbage collecting system events. // // NB: As written, this falls awkwardly between SQL and KV. KV is used only diff --git a/pkg/spanconfig/spanconfig.go b/pkg/spanconfig/spanconfig.go index 06cb134ef1cd..97e4c6dc7f72 100644 --- a/pkg/spanconfig/spanconfig.go +++ b/pkg/spanconfig/spanconfig.go @@ -24,7 +24,10 @@ import ( type KVAccessor interface { // GetSpanConfigEntriesFor returns the span configurations that overlap with // the given spans. - GetSpanConfigEntriesFor(ctx context.Context, spans []roachpb.Span) ([]roachpb.SpanConfigEntry, error) + GetSpanConfigEntriesFor( + ctx context.Context, + spans []roachpb.Span, + ) ([]roachpb.SpanConfigEntry, error) // UpdateSpanConfigEntries updates configurations for the given spans. This // is a "targeted" API: the spans being deleted are expected to have been @@ -33,35 +36,72 @@ type KVAccessor interface { // divvying up an existing span into multiple others with distinct configs, // callers are to issue a delete for the previous span and upserts for the // new ones. - UpdateSpanConfigEntries(ctx context.Context, toDelete []roachpb.Span, toUpsert []roachpb.SpanConfigEntry) error + UpdateSpanConfigEntries( + ctx context.Context, + toDelete []roachpb.Span, + toUpsert []roachpb.SpanConfigEntry, + ) error +} + +// KVSubscriber presents a consistent[1] snapshot of a StoreReader that's +// incrementally maintained with changes made to the global span configurations +// state (system.span_configurations). The maintenance happens transparently; +// callers can subscribe to learn about what key spans may have seen a +// configuration change. After learning about a span update through a callback +// invocation, subscribers can consult the embedded StoreReader to retrieve an +// up-to-date[2] config for the updated span. The callback is called in a single +// goroutine; it should avoid doing any long-running or blocking work. +// +// When a callback is first installed, it's invoked with the [min,max) span -- +// a shorthand to indicate that subscribers should consult the StoreReader for all +// spans of interest. Subsequent updates are of the more incremental kind. It's +// possible that the span updates received are no-ops, i.e. consulting the +// StoreReader for the given span would still retrieve the last config observed +// for the span[3]. +// +// [1]: The contents of the StoreReader at t1 corresponds exactly to the +// contents of the global span configuration state at t0 where t0 <= t1. If +// the StoreReader is read from at t2 where t2 > t1, it's guaranteed to +// observe a view of the global state at t >= t0. +// [2]: For the canonical KVSubscriber implementation, this is typically lagging +// by the closed timestamp target duration. +// [3]: The canonical KVSubscriber implementation is bounced whenever errors +// occur, which may result in the re-transmission of earlier updates +// (typically through a coarsely targeted [min,max) span). +type KVSubscriber interface { + StoreReader + Subscribe(func(updated roachpb.Span)) } // SQLTranslator translates SQL descriptors and their corresponding zone // configurations to constituent spans and span configurations. // // Concretely, for the following zone configuration hierarchy: +// // CREATE DATABASE db; // CREATE TABLE db.t1(); // ALTER DATABASE db CONFIGURE ZONE USING num_replicas=7; // ALTER TABLE db.t1 CONFIGURE ZONE USING num_voters=5; +// // The SQLTranslator produces the following translation (represented as a diff // against RANGE DEFAULT for brevity): +// // Table/5{3-4} num_replicas=7 num_voters=5 type SQLTranslator interface { - // Translate generates the implied span configuration state given a list of // Translate generates the span configuration state given a list of - // {descriptor, named zone} IDs. No entry is returned for an ID if it doesn't - // exist or has been dropped. The timestamp at which the translation is valid - // is also returned. + // {descriptor, named zone} IDs. No entry is returned for an ID if it + // doesn't exist or if it's dropped. The timestamp at which the translation + // is valid is also returned. // - // For every ID we first descend the zone configuration hierarchy with the ID - // as the root to accumulate IDs of all leaf objects. Leaf objects are tables - // and named zones (other than RANGE DEFAULT) which have actual span - // configurations associated with them (as opposed to non-leaf nodes that only - // serve to hold zone configurations for inheritance purposes). Then, for - // for every one of these accumulated IDs, we generate - // tuples by following up the inheritance chain to fully hydrate the span - // configuration. Translate also accounts for and negotiates subzone spans. + // For every ID we first descend the zone configuration hierarchy with the + // ID as the root to accumulate IDs of all leaf objects. Leaf objects are + // tables and named zones (other than RANGE DEFAULT) which have actual span + // configurations associated with them (as opposed to non-leaf nodes that + // only serve to hold zone configurations for inheritance purposes). Then, + // for each one of these accumulated IDs, we generate tuples by following up the inheritance chain to fully hydrate the + // span configuration. Translate also accounts for and negotiates subzone + // spans. Translate(ctx context.Context, ids descpb.IDs) ([]roachpb.SpanConfigEntry, hlc.Timestamp, error) } @@ -178,8 +218,8 @@ type StoreReader interface { GetSpanConfigForKey(ctx context.Context, key roachpb.RKey) (roachpb.SpanConfig, error) } -// Update captures what span has seen a config change. It will be the unit of -// what a {SQL,KV}Watcher emits, and what can be applied to a StoreWriter. +// Update captures a span and the corresponding config change. It's the unit of +// what can be applied to a StoreWriter. type Update struct { // Span captures the key span being updated. Span roachpb.Span diff --git a/pkg/spanconfig/spanconfigjob/job.go b/pkg/spanconfig/spanconfigjob/job.go index c748b70554c4..fa461a0d11cb 100644 --- a/pkg/spanconfig/spanconfigjob/job.go +++ b/pkg/spanconfig/spanconfigjob/job.go @@ -30,8 +30,8 @@ var _ jobs.Resumer = (*resumer)(nil) func (r *resumer) Resume(ctx context.Context, execCtxI interface{}) error { execCtx := execCtxI.(sql.JobExecContext) rc := execCtx.SpanConfigReconciliationJobDeps() - // TODO(zcfg-pod): Upcoming PRs will actually make use of these reconciliation - // dependencies. + + // TODO(irfansharif): Actually make use of these dependencies. _ = rc <-ctx.Done() diff --git a/pkg/spanconfig/spanconfigkvsubscriber/BUILD.bazel b/pkg/spanconfig/spanconfigkvsubscriber/BUILD.bazel new file mode 100644 index 000000000000..2790e0aba9fe --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/BUILD.bazel @@ -0,0 +1,76 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "spanconfigkvsubscriber", + srcs = [ + "kvsubscriber.go", + "span_config_decoder.go", + ], + importpath = "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvsubscriber", + visibility = ["//visibility:public"], + deps = [ + "//pkg/base", + "//pkg/keys", + "//pkg/kv", + "//pkg/kv/kvclient/rangefeed:with-mocks", + "//pkg/kv/kvclient/rangefeed/rangefeedbuffer", + "//pkg/roachpb:with-mocks", + "//pkg/spanconfig", + "//pkg/spanconfig/spanconfigstore", + "//pkg/sql/catalog", + "//pkg/sql/catalog/descpb", + "//pkg/sql/catalog/systemschema", + "//pkg/sql/row", + "//pkg/sql/rowenc", + "//pkg/sql/sem/tree", + "//pkg/sql/types", + "//pkg/util/encoding", + "//pkg/util/grpcutil", + "//pkg/util/hlc", + "//pkg/util/log", + "//pkg/util/protoutil", + "//pkg/util/retry", + "//pkg/util/stop", + "//pkg/util/syncutil", + "//pkg/util/timeutil", + "@com_github_cockroachdb_errors//:errors", + ], +) + +go_test( + name = "spanconfigkvsubscriber_test", + srcs = [ + "datadriven_test.go", + "kvsubscriber_test.go", + "main_test.go", + "span_config_decoder_test.go", + ], + data = glob(["testdata/**"]), + embed = [":spanconfigkvsubscriber"], + deps = [ + "//pkg/base", + "//pkg/keys", + "//pkg/kv/kvclient/rangefeed:with-mocks", + "//pkg/kv/kvclient/rangefeed/rangefeedbuffer", + "//pkg/roachpb:with-mocks", + "//pkg/security", + "//pkg/security/securitytest", + "//pkg/server", + "//pkg/spanconfig", + "//pkg/spanconfig/spanconfigkvaccessor", + "//pkg/spanconfig/spanconfigtestutils", + "//pkg/sql/sqlutil", + "//pkg/testutils", + "//pkg/testutils/serverutils", + "//pkg/testutils/sqlutils", + "//pkg/testutils/testcluster", + "//pkg/util/hlc", + "//pkg/util/leaktest", + "//pkg/util/log", + "//pkg/util/protoutil", + "//pkg/util/syncutil", + "@com_github_cockroachdb_datadriven//:datadriven", + "@com_github_cockroachdb_errors//:errors", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/spanconfig/spanconfigkvsubscriber/datadriven_test.go b/pkg/spanconfig/spanconfigkvsubscriber/datadriven_test.go new file mode 100644 index 000000000000..5ec6d0376ed8 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/datadriven_test.go @@ -0,0 +1,312 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package spanconfigkvsubscriber_test + +import ( + "context" + "fmt" + "sort" + "strings" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed/rangefeedbuffer" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/spanconfig" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvaccessor" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvsubscriber" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigtestutils" + "github.com/cockroachdb/cockroach/pkg/sql/sqlutil" + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" + "github.com/cockroachdb/datadriven" + "github.com/cockroachdb/errors" + "github.com/stretchr/testify/require" +) + +// TestDataDriven runs datadriven tests against the KVSubscriber interface. +// The syntax is as follows: +// +// update +// delete [c,e) +// upsert [c,d):C +// upsert [d,e):D +// ---- +// ok +// +// get +// span [a,b) +// span [b,c) +// ---- +// [a,b):A +// [b,d):B +// +// start +// ---- +// +// updates +// ---- +// [a,b) +// [b,d) +// [e,f) +// +// store-reader key=b +// ---- +// [b,d):B +// +// store-reader compute-split=[a,c) +// ---- +// b +// +// store-reader needs-split=[b,h) +// ---- +// true +// +// inject-buffer-overflow +// ---- +// ok +// +// - update and get tie into GetSpanConfigEntriesFor and +// UpdateSpanConfigEntries respectively on the KVAccessor interface, and are a +// convenient shorthand to populate the system table that the KVSubscriber +// subscribes to. The input is processed in a single batch. +// - start starts the subscription process. It can also be used to verify +// behavior when re-establishing subscriptions after hard errors. +// - updates lists the span updates the KVSubscriber receives, in the listed +// order. Updates in a batch are de-duped. +// - store-reader {key,compute-split,needs-split} relate to GetSpanConfigForKey, +// ComputeSplitKey and NeedsSplit respectively on the StoreReader subset of the +// KVSubscriber interface. +// - inject-buffer-overflow can be used to inject rangefeed buffer overflow +// errors within the kvsubscriber. It pokes into the internals of the +// kvsubscriber and is useful to test teardown and recovery behavior. +// +// Text of the form [a,b) and [a,b):C correspond to spans and span config +// entries; see spanconfigtestutils.Parse{Span,Config,SpanConfigEntry} for more +// details. +func TestDataDriven(t *testing.T) { + defer leaktest.AfterTest(t)() + + datadriven.Walk(t, testutils.TestDataPath(t), func(t *testing.T, path string) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{ + ServerArgs: base.TestServerArgs{ + EnableSpanConfigs: true, + }, + }) + defer cancel() + defer tc.Stopper().Stop(ctx) + + ts := tc.Server(0) + tdb := sqlutils.MakeSQLRunner(tc.ServerConn(0)) + tdb.Exec(t, `SET CLUSTER SETTING kv.rangefeed.enabled = true`) + tdb.Exec(t, `SET CLUSTER SETTING spanconfig.experimental_kvaccessor.enabled = true`) + tdb.Exec(t, `SET CLUSTER SETTING kv.closed_timestamp.target_duration = '100ms'`) + + const dummyTableName = "dummy_span_configurations" + tdb.Exec(t, fmt.Sprintf("CREATE TABLE %s (LIKE system.span_configurations INCLUDING ALL)", dummyTableName)) + + var dummyTableID uint32 + tdb.QueryRow(t, fmt.Sprintf( + `SELECT table_id from crdb_internal.tables WHERE name = '%s'`, dummyTableName), + ).Scan(&dummyTableID) + + kvAccessor := spanconfigkvaccessor.New( + tc.Server(0).DB(), + tc.Server(0).InternalExecutor().(sqlutil.InternalExecutor), + tc.Server(0).ClusterSettings(), + fmt.Sprintf("defaultdb.public.%s", dummyTableName), + ) + + mu := struct { + syncutil.Mutex + lastFrontierTS hlc.Timestamp // serializes updates and update + subscriberRunning bool // serializes updates, subscribe, and inject-buffer-overflow + receivedUpdates roachpb.Spans + }{} + injectedErrCh := make(chan error) + + kvSubscriber := spanconfigkvsubscriber.New( + ts.Stopper(), + ts.DB(), + ts.Clock(), + ts.RangeFeedFactory().(*rangefeed.Factory), + dummyTableID, + 10<<20, /* 10 MB */ + spanconfigtestutils.ParseConfig(t, "MISSING"), + &spanconfig.TestingKnobs{ + KVSubscriberOnTimestampAdvanceInterceptor: func(ts hlc.Timestamp) { + mu.Lock() + defer mu.Unlock() + mu.lastFrontierTS = ts + }, + KVSubscriberPostRangefeedStartInterceptor: func() { + mu.Lock() + defer mu.Unlock() + mu.subscriberRunning = true + }, + KVSubscriberPreExitInterceptor: func() { + mu.Lock() + defer mu.Unlock() + mu.subscriberRunning = false + }, + KVSubscriberErrorInjectionCh: injectedErrCh, + }, + ) + + kvSubscriber.Subscribe(func(span roachpb.Span) { + mu.Lock() + defer mu.Unlock() + mu.receivedUpdates = append(mu.receivedUpdates, span) + }) + + var lastUpdateTS hlc.Timestamp + datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { + switch d.Cmd { + case "get": + spans := spanconfigtestutils.ParseKVAccessorGetArguments(t, d.Input) + entries, err := kvAccessor.GetSpanConfigEntriesFor(ctx, spans) + require.NoError(t, err) + + var output strings.Builder + for _, entry := range entries { + output.WriteString(fmt.Sprintf("%s\n", spanconfigtestutils.PrintSpanConfigEntry(entry))) + } + return output.String() + + case "update": + toDelete, toUpsert := spanconfigtestutils.ParseKVAccessorUpdateArguments(t, d.Input) + require.NoError(t, kvAccessor.UpdateSpanConfigEntries(ctx, toDelete, toUpsert)) + lastUpdateTS = ts.Clock().Now() + + case "start": + mu.Lock() + require.False(t, mu.subscriberRunning, "subscriber already running") + mu.Unlock() + + go func() { + _ = kvSubscriber.TestingRunInner(ctx) + }() + testutils.SucceedsSoon(t, func() error { + mu.Lock() + defer mu.Unlock() + if !mu.subscriberRunning { + return errors.New("expected subscriber to have started") + } + return nil + }) + + case "updates": + testutils.SucceedsSoon(t, func() error { + mu.Lock() + defer mu.Unlock() + + if !mu.subscriberRunning { + // The subscriber isn't running, we're not expecting any + // frontier bumps. + return nil + } + + // The subscriber is running -- we should be observing + // frontier bumps. In order to serialize after the last + // kvaccessor-update, lets wait until the frontier timestamp + // is past it. + if lastUpdateTS.LessEq(mu.lastFrontierTS) { + return nil + } + + return errors.Newf("frontier timestamp (%s) lagging last update (%s)", + mu.lastFrontierTS.String(), lastUpdateTS.String()) + }) // TODO(irfansharif): We could use a tighter bound here, but it's unreliable under stress. + + mu.Lock() + receivedUpdates := mu.receivedUpdates + mu.receivedUpdates = mu.receivedUpdates[:0] // clear out buffer + mu.Unlock() + + var output strings.Builder + sort.Sort(receivedUpdates) + for i, update := range receivedUpdates { + if i != 0 && receivedUpdates[i].Equal(receivedUpdates[i-1]) { + continue // de-dup updates + } + + var spanStr string + if update.Equal(keys.EverythingSpan) { + spanStr = update.String() + } else { + spanStr = spanconfigtestutils.PrintSpan(update) + } + output.WriteString(fmt.Sprintf("%s\n", spanStr)) + } + + return output.String() + + case "inject-buffer-overflow": + injectedErrCh <- rangefeedbuffer.ErrBufferLimitExceeded + testutils.SucceedsSoon(t, func() error { + mu.Lock() + defer mu.Unlock() + if mu.subscriberRunning { + return errors.New("expected subscriber to have stopped") + } + return nil + + }) + + case "store-reader": + if len(d.CmdArgs) != 1 { + d.Fatalf(t, "unexpected number of args (%d), expected 1", len(d.CmdArgs)) + } + cmdArg := d.CmdArgs[0] + + switch cmdArg.Key { + case "key": + var keyStr string + d.ScanArgs(t, cmdArg.Key, &keyStr) + config, err := kvSubscriber.GetSpanConfigForKey(ctx, roachpb.RKey(keyStr)) + require.NoError(t, err) + return fmt.Sprintf("conf=%s", spanconfigtestutils.PrintSpanConfig(config)) + + case "compute-split": + var spanStr string + d.ScanArgs(t, cmdArg.Key, &spanStr) + span := spanconfigtestutils.ParseSpan(t, spanStr) + start, end := roachpb.RKey(span.Key), roachpb.RKey(span.EndKey) + splitKey := kvSubscriber.ComputeSplitKey(ctx, start, end) + return string(splitKey) + + case "needs-split": + var spanStr string + d.ScanArgs(t, cmdArg.Key, &spanStr) + span := spanconfigtestutils.ParseSpan(t, spanStr) + start, end := roachpb.RKey(span.Key), roachpb.RKey(span.EndKey) + result := kvSubscriber.NeedsSplit(ctx, start, end) + return fmt.Sprintf("%t", result) + + default: + d.Fatalf(t, "unknown argument: %s", cmdArg.Key) + } + + default: + d.Fatalf(t, "unknown command: %s", d.Cmd) + } + return "" + }) + }) +} diff --git a/pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber.go b/pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber.go new file mode 100644 index 000000000000..77fca41a25c1 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber.go @@ -0,0 +1,446 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package spanconfigkvsubscriber + +import ( + "context" + "strings" + "sync/atomic" + "time" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed" + "github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed/rangefeedbuffer" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/spanconfig" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigstore" + "github.com/cockroachdb/cockroach/pkg/util/grpcutil" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/retry" + "github.com/cockroachdb/cockroach/pkg/util/stop" + "github.com/cockroachdb/cockroach/pkg/util/syncutil" + "github.com/cockroachdb/cockroach/pkg/util/timeutil" + "github.com/cockroachdb/errors" +) + +// KVSubscriber is used to subscribe to global span configuration changes. It's +// a concrete implementation of the spanconfig.KVSubscriber interface. +// +// It's expected to Start-ed once, after which one or many subscribers can +// listen in for updates. Internally we maintain a rangefeed over the global +// store of span configurations (system.span_configurations), applying updates +// from it into an internal spanconfig.Store. A read-only view of this data +// structure (spanconfig.StoreReader) is exposed as part of the KVSubscriber +// interface. Rangefeeds used as is don't offer any ordering guarantees with +// respect to updates made over non-overlapping keys, which is something we care +// about[1]. For that reason we make use of a rangefeed buffer, accumulating raw +// rangefeed updates and flushing them out en-masse in timestamp order when the +// rangefeed frontier is bumped[2]. If the buffer overflows (as dictated by the +// memory limit the KVSubscriber is instantiated with), the old rangefeed is +// wound down and a new one re-established. +// +// When running into the internal errors described above, it's safe for us to +// re-establish the underlying rangefeeds. When re-establishing a new rangefeed +// and populating a spanconfig.Store using the contents of the initial scan[3], +// we wish to preserve the existing spanconfig.StoreReader. Discarding it would +// entail either blocking all external readers until a new +// spanconfig.StoreReader was fully populated, or presenting an inconsistent +// view of the spanconfig.Store that's currently being populated. For new +// rangefeeds what we do then is route all updates from the initial scan to a +// fresh spanconfig.Store, and once the initial scan is done, swap at the source +// for the exported spanconfig.StoreReader. During the initial scan, concurrent +// readers would continue to observe the last spanconfig.StoreReader if any. +// After the swap, it would observe the more up-to-date source instead. Future +// incremental updates will also target the new source. When this source swap +// occurs, we inform the handler of the need to possibly refresh its view of all +// configs. +// +// TODO(irfansharif): When swapping the old spanconfig.StoreReader for the new, +// instead of informing registered handlers with an everything [min,max) span, +// we could diff the two data structures and only emit targeted updates. +// +// [1]: For a given key k, it's config may be stored as part of a larger span S +// (where S.start <= k < S.end). It's possible for S to get deleted and +// replaced with sub-spans S1...SN in the same transaction if the span is +// getting split. When applying these updates, we need to make sure to +// process the deletion event for S before processing S1...SN. +// [2]: In our example above deleting the config for S and adding configs for +// S1...SN we want to make sure that we apply the full set of updates all +// at once -- lest we expose the intermediate state where the config for S +// was deleted but the configs for S1...SN were not yet applied. +// [3]: TODO(irfansharif): When tearing down the subscriber due to underlying +// errors, we could also capture a checkpoint to use the next time the +// subscriber is established. That way we can avoid the full initial scan +// over the span configuration state and simply pick up where we left off +// with our existing spanconfig.Store. +type KVSubscriber struct { + stopper *stop.Stopper + db *kv.DB + clock *hlc.Clock + rangefeedFactory *rangefeed.Factory + decoder *spanConfigDecoder + spanConfigTableSpan roachpb.Span // typically system.span_configurations, but overridable for tests + bufferMemLimit int64 + fallback roachpb.SpanConfig + knobs *spanconfig.TestingKnobs + + started int32 // accessed atomically + mu struct { // serializes between Start and external threads + syncutil.RWMutex + // internal is the internal spanconfig.Store maintained by the + // KVSubscriber. A read-only view over this store is exposed as part of + // the interface. When re-subscribing, a fresh spanconfig.Store is + // populated while the exposed spanconfig.StoreReader appears static. + // Once sufficiently caught up, the fresh spanconfig.Store is swapped in + // and the old discarded. See type-level comment for more details. + internal spanconfig.Store + handlers []handler + } + + lastFrontierTS hlc.Timestamp // used to assert monotonicity across subscription attempts +} + +var _ spanconfig.KVSubscriber = &KVSubscriber{} + +// spanConfigurationsTableRowSize is an estimate of the size of a single row in +// the system.span_configurations table (size of start/end key, and size of a +// marshaled span config proto). The value used here was pulled out of thin air +// -- it only serves to coarsely limit how large the KVSubscriber's underlying +// rangefeed buffer can get. +const spanConfigurationsTableRowSize = 5 << 10 // 5 KB + +// New instantiates a KVSubscriber. +func New( + stopper *stop.Stopper, + db *kv.DB, + clock *hlc.Clock, + rangeFeedFactory *rangefeed.Factory, + spanConfigurationsTableID uint32, + bufferMemLimit int64, + fallback roachpb.SpanConfig, + knobs *spanconfig.TestingKnobs, +) *KVSubscriber { + spanConfigTableStart := keys.SystemSQLCodec.IndexPrefix( + spanConfigurationsTableID, + keys.SpanConfigurationsTablePrimaryKeyIndexID, + ) + spanConfigTableSpan := roachpb.Span{ + Key: spanConfigTableStart, + EndKey: spanConfigTableStart.PrefixEnd(), + } + spanConfigStore := spanconfigstore.New(fallback) + if knobs == nil { + knobs = &spanconfig.TestingKnobs{} + } + s := &KVSubscriber{ + stopper: stopper, + db: db, + clock: clock, + bufferMemLimit: bufferMemLimit, + rangefeedFactory: rangeFeedFactory, + spanConfigTableSpan: spanConfigTableSpan, + fallback: fallback, + knobs: knobs, + decoder: newSpanConfigDecoder(), + } + s.mu.internal = spanConfigStore + return s +} + +// Start establishes a subscription (internally: rangefeed) over the global +// store of span configs. It fires off an async task to do so, re-establishing +// internally when retryable errors[1] occur and stopping only when the surround +// stopper is quiescing or the context canceled. All installed handlers are +// invoked in the single async task thread. +// +// [1]: It's possible for retryable errors to occur internally, at which point +// we tear down the existing subscription and re-establish another. When +// unsubscribed, the exposed spanconfig.StoreReader continues to be +// readable (though no longer incrementally maintained -- the view gets +// progressively staler overtime). Existing handlers are kept intact and +// notified when the subscription is re-established. After re-subscribing, +// the exported StoreReader will be up-to-date and continue to be +// incrementally maintained. +func (s *KVSubscriber) Start(ctx context.Context) error { + return s.stopper.RunAsyncTask(ctx, "spanconfig-kvsubscriber", func(ctx context.Context) { + ctx, cancel := s.stopper.WithCancelOnQuiesce(ctx) + defer cancel() + + const aWhile = 5 * time.Minute // arbitrary but much longer than a retry + for r := retry.StartWithCtx(ctx, base.DefaultRetryOptions()); r.Next(); { + + started := timeutil.Now() + if err := s.run(ctx); err != nil { + if errors.Is(err, context.Canceled) { + return // we're done here + } + + if timeutil.Since(started) > aWhile { + r.Reset() + } + + log.Warningf(ctx, "spanconfig-kvsubscriber failed with %v, retrying...", err) + continue + } + + return // we're done here (the stopper was stopped, run exited cleanly) + } + }) +} + +// run establishes a rangefeed over the global store of span configs. +// This is a blocking operation, returning (and unsubscribing) only when the +// surrounding stopper is stopped, the context canceled, or when a retryable +// error occurs. For the latter, it's expected that callers will re-run the +// subscriber. +func (s *KVSubscriber) run(ctx context.Context) error { + if !atomic.CompareAndSwapInt32(&s.started, 0, 1) { + log.Fatal(ctx, "currently started: only allowed once at any point in time") + } + if fn := s.knobs.KVSubscriberPreExitInterceptor; fn != nil { + defer fn() + } + defer func() { atomic.StoreInt32(&s.started, 0) }() + + buffer := rangefeedbuffer.New(int(s.bufferMemLimit / spanConfigurationsTableRowSize)) + frontierBumpedCh, initialScanDoneCh, errCh := make(chan struct{}), make(chan struct{}), make(chan error) + mu := struct { // serializes access between the rangefeed and the main thread here + syncutil.Mutex + frontierTS hlc.Timestamp + }{} + + defer func() { + mu.Lock() + s.lastFrontierTS.Forward(mu.frontierTS) + mu.Unlock() + }() + + onValue := func(ctx context.Context, ev *roachpb.RangeFeedValue) { + deleted := !ev.Value.IsPresent() + var value roachpb.Value + if deleted { + if !ev.PrevValue.IsPresent() { + // It's possible to write a KV tombstone on top of another KV + // tombstone -- both the new and old value will be empty. We simply + // ignore these events. + return + } + + // Since the end key is not part of the primary key, we need to + // decode the previous value in order to determine what it is. + value = ev.PrevValue + } else { + value = ev.Value + } + entry, err := s.decoder.decode(roachpb.KeyValue{ + Key: ev.Key, + Value: value, + }) + if err != nil { + log.Fatalf(ctx, "failed to decode row: %v", err) // non-retryable error; just fatal + } + + if log.ExpensiveLogEnabled(ctx, 1) { + log.Infof(ctx, "received span configuration update for %s (deleted=%t)", entry.Span, deleted) + } + + update := spanconfig.Update{Span: entry.Span} + if !deleted { + update.Config = entry.Config + } + + if err := buffer.Add(ctx, &bufferEvent{update, ev.Value.Timestamp}); err != nil { + select { + case <-ctx.Done(): + // The context is canceled when the rangefeed is closed by the + // main handler goroutine. It's closed after we stop listening + // to errCh. + case errCh <- err: + } + } + } + + initialScanTS := s.clock.Now() + if initialScanTS.Less(s.lastFrontierTS) { + log.Fatalf(ctx, "initial scan timestamp (%s) regressed from last recorded frontier (%s)", initialScanTS, s.lastFrontierTS) + } + + rangeFeed := s.rangefeedFactory.New("spanconfig-rangefeed", s.spanConfigTableSpan, initialScanTS, + onValue, + rangefeed.WithInitialScan(func(ctx context.Context) { + select { + case <-ctx.Done(): + // The context is canceled when the rangefeed is closed by the + // main handler goroutine. It's closed after we stop listening + // to initialScanDoneCh. + case initialScanDoneCh <- struct{}{}: + } + }), + rangefeed.WithOnFrontierAdvance(func(ctx context.Context, frontierTS hlc.Timestamp) { + mu.Lock() + mu.frontierTS = frontierTS + mu.Unlock() + + select { + case <-ctx.Done(): + case frontierBumpedCh <- struct{}{}: + } + }), + rangefeed.WithDiff(), + rangefeed.WithOnInitialScanError(func(ctx context.Context, err error) (shouldFail bool) { + // TODO(irfansharif): Consider if there are other errors which we + // want to treat as permanent. This was cargo culted from the + // settings watcher. + if grpcutil.IsAuthError(err) || + strings.Contains(err.Error(), "rpc error: code = Unauthenticated") { + return true + } + return false + }), + ) + if err := rangeFeed.Start(ctx); err != nil { + return err + } + defer rangeFeed.Close() + if fn := s.knobs.KVSubscriberPostRangefeedStartInterceptor; fn != nil { + fn() + } + + log.Info(ctx, "established range feed over span configurations table") + + injectedErrCh := s.knobs.KVSubscriberErrorInjectionCh + + for { + select { + case <-s.stopper.ShouldQuiesce(): + return nil + case <-ctx.Done(): + return ctx.Err() + case <-frontierBumpedCh: + mu.Lock() + frontierTS := mu.frontierTS + mu.Unlock() + + events := buffer.Flush(ctx, frontierTS) + s.mu.Lock() + for _, ev := range events { + s.mu.internal.Apply(ctx, ev.(*bufferEvent).Update, false /* dryrun */) + } + handlers := s.mu.handlers + s.mu.Unlock() + + for _, h := range handlers { + for _, ev := range events { + h.invoke(ev.(*bufferEvent).Update.Span) + } + } + + if fn := s.knobs.KVSubscriberOnTimestampAdvanceInterceptor; fn != nil { + fn(frontierTS) + } + case <-initialScanDoneCh: + events := buffer.Flush(ctx, initialScanTS) + freshStore := spanconfigstore.New(s.fallback) + for _, ev := range events { + freshStore.Apply(ctx, ev.(*bufferEvent).Update, false /* dryrun */) + } + + s.mu.Lock() + s.mu.internal = freshStore + handlers := s.mu.handlers + s.mu.Unlock() + + for _, h := range handlers { + // When re-establishing a rangefeed, it's possible we have a + // spanconfig.Store with arbitrary updates from what was + // exported last. Let's inform the handler than everything needs + // to be checked again. + h.invoke(keys.EverythingSpan) + } + + if fn := s.knobs.KVSubscriberOnTimestampAdvanceInterceptor; fn != nil { + fn(initialScanTS) + } + case err := <-errCh: + return err + case err := <-injectedErrCh: + return err + } + } +} + +// Subscribe installs a callback that's invoked with whatever span may have seen +// a config update. +func (s *KVSubscriber) Subscribe(fn func(roachpb.Span)) { + s.mu.Lock() + defer s.mu.Unlock() + + s.mu.handlers = append(s.mu.handlers, handler{fn: fn}) +} + +// NeedsSplit is part of the spanconfig.KVSubscriber interface. +func (s *KVSubscriber) NeedsSplit(ctx context.Context, start, end roachpb.RKey) bool { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.mu.internal.NeedsSplit(ctx, start, end) +} + +// ComputeSplitKey is part of the spanconfig.KVSubscriber interface. +func (s *KVSubscriber) ComputeSplitKey(ctx context.Context, start, end roachpb.RKey) roachpb.RKey { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.mu.internal.ComputeSplitKey(ctx, start, end) +} + +// GetSpanConfigForKey is part of the spanconfig.KVSubscriber interface. +func (s *KVSubscriber) GetSpanConfigForKey( + ctx context.Context, key roachpb.RKey, +) (roachpb.SpanConfig, error) { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.mu.internal.GetSpanConfigForKey(ctx, key) +} + +type handler struct { + initialized bool // tracks whether we need to invoke with a [min,max) span first + fn func(update roachpb.Span) +} + +func (h handler) invoke(update roachpb.Span) { + if !h.initialized { + h.fn(keys.EverythingSpan) + h.initialized = true + + if update.Equal(keys.EverythingSpan) { + return // we can opportunistically avoid re-invoking with the same update + } + } + + h.fn(update) +} + +type bufferEvent struct { + spanconfig.Update + ts hlc.Timestamp +} + +// Timestamp implements the rangefeedbuffer.Event interface. +func (w *bufferEvent) Timestamp() hlc.Timestamp { + return w.ts +} + +var _ rangefeedbuffer.Event = &bufferEvent{} diff --git a/pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber_test.go b/pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber_test.go new file mode 100644 index 000000000000..d5ba0fe16ab2 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/kvsubscriber_test.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package spanconfigkvsubscriber + +import "context" + +// TestingRunInner exports the inner run method for testing purposes. +func (s *KVSubscriber) TestingRunInner(ctx context.Context) error { + return s.run(ctx) +} diff --git a/pkg/spanconfig/spanconfigkvsubscriber/main_test.go b/pkg/spanconfig/spanconfigkvsubscriber/main_test.go new file mode 100644 index 000000000000..177f2e83fa44 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/main_test.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package spanconfigkvsubscriber_test + +import ( + "os" + "testing" + + "github.com/cockroachdb/cockroach/pkg/security" + "github.com/cockroachdb/cockroach/pkg/security/securitytest" + "github.com/cockroachdb/cockroach/pkg/server" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" +) + +func TestMain(m *testing.M) { + security.SetAssetLoader(securitytest.EmbeddedAssets) + serverutils.InitTestServerFactory(server.TestServerFactory) + serverutils.InitTestClusterFactory(testcluster.TestClusterFactory) + os.Exit(m.Run()) +} + +//go:generate ../../util/leaktest/add-leaktest.sh *_test.go diff --git a/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go b/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go new file mode 100644 index 000000000000..76fa92e1f7a8 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder.go @@ -0,0 +1,118 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package spanconfigkvsubscriber + +import ( + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/systemschema" + "github.com/cockroachdb/cockroach/pkg/sql/row" + "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/encoding" + "github.com/cockroachdb/cockroach/pkg/util/protoutil" + "github.com/cockroachdb/errors" +) + +// spanConfigDecoder decodes rows from system.span_configurations. It's not +// safe for concurrent use. +type spanConfigDecoder struct { + alloc rowenc.DatumAlloc + colIdxMap catalog.TableColMap +} + +// newSpanConfigDecoder instantiates a spanConfigDecoder. +func newSpanConfigDecoder() *spanConfigDecoder { + return &spanConfigDecoder{ + colIdxMap: row.ColIDtoRowIndexFromCols( + systemschema.SpanConfigurationsTable.PublicColumns(), + ), + } +} + +// decode a span config entry given a KV from the +// system.span_configurations table. +func (sd *spanConfigDecoder) decode(kv roachpb.KeyValue) (entry roachpb.SpanConfigEntry, _ error) { + tbl := systemschema.SpanConfigurationsTable + // First we need to decode the start_key field from the index key. + { + types := []*types.T{tbl.PublicColumns()[0].GetType()} + startKeyRow := make([]rowenc.EncDatum, 1) + _, matches, _, err := rowenc.DecodeIndexKey( + keys.SystemSQLCodec, tbl, tbl.GetPrimaryIndex(), + types, startKeyRow, nil, kv.Key, + ) + if err != nil { + return roachpb.SpanConfigEntry{}, errors.Wrapf(err, "failed to decode key: %v", kv.Key) + } + if !matches { + return roachpb.SpanConfigEntry{}, + errors.AssertionFailedf( + "system.span_configurations descriptor does not match key: %v", kv.Key, + ) + } + if err := startKeyRow[0].EnsureDecoded(types[0], &sd.alloc); err != nil { + return roachpb.SpanConfigEntry{}, err + } + entry.Span.Key = []byte(tree.MustBeDBytes(startKeyRow[0].Datum)) + } + if !kv.Value.IsPresent() { + return roachpb.SpanConfigEntry{}, + errors.AssertionFailedf("missing value for start key: %s", entry.Span.Key) + } + + // The remaining columns are stored as a family, packed with diff-encoded + // column IDs followed by their values. + { + bytes, err := kv.Value.GetTuple() + if err != nil { + return roachpb.SpanConfigEntry{}, err + } + var colIDDiff uint32 + var lastColID descpb.ColumnID + var res tree.Datum + for len(bytes) > 0 { + _, _, colIDDiff, _, err = encoding.DecodeValueTag(bytes) + if err != nil { + return roachpb.SpanConfigEntry{}, err + } + colID := lastColID + descpb.ColumnID(colIDDiff) + lastColID = colID + if idx, ok := sd.colIdxMap.Get(colID); ok { + res, bytes, err = rowenc.DecodeTableValue(&sd.alloc, tbl.PublicColumns()[idx].GetType(), bytes) + if err != nil { + return roachpb.SpanConfigEntry{}, err + } + + switch colID { + case tbl.PublicColumns()[1].GetID(): // end_key + entry.Span.EndKey = []byte(tree.MustBeDBytes(res)) + case tbl.PublicColumns()[2].GetID(): // config + if err := protoutil.Unmarshal([]byte(tree.MustBeDBytes(res)), &entry.Config); err != nil { + return roachpb.SpanConfigEntry{}, err + } + default: + return roachpb.SpanConfigEntry{}, errors.AssertionFailedf("unknown column: %v", colID) + } + } + } + } + + return entry, nil +} + +// TestingDecoderFn exports the decoding routine for testing purposes. +func TestingDecoderFn() func(roachpb.KeyValue) (roachpb.SpanConfigEntry, error) { + return newSpanConfigDecoder().decode +} diff --git a/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder_test.go b/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder_test.go new file mode 100644 index 000000000000..18e56e4b704a --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/span_config_decoder_test.go @@ -0,0 +1,128 @@ +// Copyright 2021 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package spanconfigkvsubscriber_test + +import ( + "context" + "fmt" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigkvsubscriber" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/testutils/testcluster" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/cockroach/pkg/util/protoutil" + "github.com/stretchr/testify/require" +) + +// TestSpanConfigDecoder verifies that we can decode rows stored in the +// system.span_configurations table. +func TestSpanConfigDecoder(t *testing.T) { + defer leaktest.AfterTest(t)() + + ctx := context.Background() + tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{}) + defer tc.Stopper().Stop(ctx) + + const dummyTableName = "dummy_span_configurations" + tdb := sqlutils.MakeSQLRunner(tc.ServerConn(0)) + tdb.Exec(t, fmt.Sprintf("CREATE TABLE %s (LIKE system.span_configurations INCLUDING ALL)", dummyTableName)) + + var dummyTableID uint32 + tdb.QueryRow(t, fmt.Sprintf( + `SELECT table_id FROM crdb_internal.tables WHERE name = '%s'`, dummyTableName), + ).Scan(&dummyTableID) + + getCount := func() int { + q := tdb.Query(t, fmt.Sprintf(`SELECT count(*) FROM %s`, dummyTableName)) + q.Next() + var c int + require.Nil(t, q.Scan(&c)) + require.Nil(t, q.Close()) + return c + } + initialCount := getCount() + + key := tc.ScratchRange(t) + rng := tc.GetFirstStoreFromServer(t, 0).LookupReplica(keys.MustAddr(key)) + span := rng.Desc().RSpan().AsRawSpanWithNoLocals() + conf := roachpb.SpanConfig{NumReplicas: 5, NumVoters: 3} + + buf, err := protoutil.Marshal(&conf) + require.NoError(t, err) + tdb.Exec(t, fmt.Sprintf(`UPSERT INTO %s (start_key, end_key, config) VALUES ($1, $2, $3)`, + dummyTableName), span.Key, span.EndKey, buf) + require.Equal(t, initialCount+1, getCount()) + + k := keys.SystemSQLCodec.IndexPrefix(dummyTableID, keys.SpanConfigurationsTablePrimaryKeyIndexID) + rows, err := tc.Server(0).DB().Scan(ctx, k, k.PrefixEnd(), 0 /* maxRows */) + require.NoError(t, err) + require.Len(t, rows, initialCount+1) + + last := rows[len(rows)-1] + got, err := spanconfigkvsubscriber.TestingDecoderFn()( + roachpb.KeyValue{ + Key: last.Key, + Value: *last.Value, + }, + ) + require.NoError(t, err) + require.Truef(t, span.Equal(got.Span), + "expected span=%s, got span=%s", span, got.Span) + require.Truef(t, conf.Equal(got.Config), + "expected config=%s, got config=%s", conf, got.Config) +} + +func BenchmarkSpanConfigDecoder(b *testing.B) { + defer log.Scope(b).Close(b) + + s, db, _ := serverutils.StartServer( + b, base.TestServerArgs{UseDatabase: "bench"}) + defer s.Stopper().Stop(context.Background()) + + ctx := context.Background() + const dummyTableName = "dummy_span_configurations" + tdb := sqlutils.MakeSQLRunner(db) + + tdb.Exec(b, `CREATE DATABASE bench`) + tdb.Exec(b, fmt.Sprintf("CREATE TABLE %s (LIKE system.span_configurations INCLUDING ALL)", dummyTableName)) + + var dummyTableID uint32 + tdb.QueryRow(b, fmt.Sprintf( + `SELECT table_id from crdb_internal.tables WHERE name = '%s'`, dummyTableName), + ).Scan(&dummyTableID) + + conf := roachpb.SpanConfig{NumReplicas: 5, NumVoters: 3} + buf, err := protoutil.Marshal(&conf) + require.NoError(b, err) + + tdb.Exec(b, fmt.Sprintf(`UPSERT INTO %s (start_key, end_key, config) VALUES ($1, $2, $3)`, + dummyTableName), roachpb.Key("a"), roachpb.Key("b"), buf) + + k := keys.SystemSQLCodec.IndexPrefix(dummyTableID, keys.SpanConfigurationsTablePrimaryKeyIndexID) + rows, err := s.DB().Scan(ctx, k, k.PrefixEnd(), 0 /* maxRows */) + require.NoError(b, err) + last := rows[len(rows)-1] + decoderFn := spanconfigkvsubscriber.TestingDecoderFn() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = decoderFn(roachpb.KeyValue{ + Key: last.Key, + Value: *last.Value, + }) + } +} diff --git a/pkg/spanconfig/spanconfigkvsubscriber/testdata/basic b/pkg/spanconfig/spanconfigkvsubscriber/testdata/basic new file mode 100644 index 000000000000..676a8d5aa502 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/testdata/basic @@ -0,0 +1,48 @@ +# Test the basic control flow: writes to the span configurations table should +# appear to the subscriber. Incremental updates should as well, and the exposed +# store reader reflecting said updates appropriately. + +start +---- + +update +upsert [a,c):A +upsert [d,f):D +---- + +get +span [a,f) +---- +[a,c):A +[d,f):D + +updates +---- +/M{in-ax} +[a,c) +[d,f) + +store-reader key=a +---- +conf=A + +store-reader key=d +---- +conf=D + +store-reader compute-split=[a,e) +---- +d + +update +delete [d,f) +---- + +updates +---- +/M{in-ax} +[d,f) + +store-reader key=d +---- +conf=MISSING diff --git a/pkg/spanconfig/spanconfigkvsubscriber/testdata/buffer_overflow b/pkg/spanconfig/spanconfigkvsubscriber/testdata/buffer_overflow new file mode 100644 index 000000000000..fa7787f911e7 --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/testdata/buffer_overflow @@ -0,0 +1,83 @@ +# Test the behavior of the kvsubscriber in the presence of internal +# subscription errors. During errors, the store-reader should present a snapshot +# view of the state before the error occurred. It should also be safe to +# bounce the same subscriber and have the handlers observe a [max,min) update +# indicating their view of all span configs needs to be refreshed. When +# consulting the store-reader after, it should observe a more up-to-date +# snapshot than earlier. We should also continue to observe incremental updates +# there-on-forth. + +start +---- + +update +upsert [a,c):A +upsert [d,f):D +---- + +updates +---- +/M{in-ax} +[a,c) +[d,f) + +store-reader key=a +---- +conf=A + +store-reader key=d +---- +conf=D + +# Inject a hard error. Subsequent updates aren't observed by the subscriber. The +# store-reader should also still be readable and present a snapshot of the state +# pre-error. +inject-buffer-overflow +---- + +update +upsert [a,c):B +delete [d,f) +---- + +updates +---- + +store-reader key=a +---- +conf=A + +store-reader key=d +---- +conf=D + +# Bounce the kvsubscriber. We should observe a catch-all update as a result, and +# observe a more up-to-date snapshot of the span configuration state. We should +# also receive incremental updates. +start +---- + +updates +---- +/M{in-ax} + +store-reader key=a +---- +conf=B + +store-reader key=d +---- +conf=MISSING + +update +upsert [a,c):C +---- + +updates +---- +/M{in-ax} +[a,c) + +store-reader key=a +---- +conf=C diff --git a/pkg/spanconfig/spanconfigkvsubscriber/testdata/initial_state b/pkg/spanconfig/spanconfigkvsubscriber/testdata/initial_state new file mode 100644 index 000000000000..5437421e235c --- /dev/null +++ b/pkg/spanconfig/spanconfigkvsubscriber/testdata/initial_state @@ -0,0 +1,27 @@ +# Ensure that subscribers started after certain span configs have been +# deleted/overwritten never observe earlier state. + +update +upsert [a,c):A +upsert [d,f):D +---- + +update +delete [d,f) +upsert [a,c):B +---- + +start +---- + +updates +---- +/M{in-ax} + +store-reader key=a +---- +conf=B + +store-reader key=d +---- +conf=MISSING diff --git a/pkg/spanconfig/spanconfigstore/BUILD.bazel b/pkg/spanconfig/spanconfigstore/BUILD.bazel index 5d6524640d87..a17a28eade92 100644 --- a/pkg/spanconfig/spanconfigstore/BUILD.bazel +++ b/pkg/spanconfig/spanconfigstore/BUILD.bazel @@ -11,6 +11,7 @@ go_library( deps = [ "//pkg/keys", "//pkg/roachpb:with-mocks", + "//pkg/settings", "//pkg/spanconfig", "//pkg/util/interval", "//pkg/util/log", diff --git a/pkg/spanconfig/spanconfigstore/shadow.go b/pkg/spanconfig/spanconfigstore/shadow.go index c9625f19fdeb..98a0b042318c 100644 --- a/pkg/spanconfig/spanconfigstore/shadow.go +++ b/pkg/spanconfig/spanconfigstore/shadow.go @@ -21,6 +21,10 @@ import ( // ShadowReader wraps around two spanconfig.StoreReaders and logs warnings (if // expensive logging is enabled) when there are divergent results from the two. +// +// TODO(irfansharif): This was added as a convenient way to diagnose the +// differences between span configs infrastructure and system config span. +// Remove it when we actually start issuing RPCs (#71994) have better tests. type ShadowReader struct { new, old spanconfig.StoreReader } diff --git a/pkg/spanconfig/spanconfigstore/store.go b/pkg/spanconfig/spanconfigstore/store.go index 0f3d357fd7c3..b5eb8bd50c5b 100644 --- a/pkg/spanconfig/spanconfigstore/store.go +++ b/pkg/spanconfig/spanconfigstore/store.go @@ -15,15 +15,27 @@ import ( "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/spanconfig" "github.com/cockroachdb/cockroach/pkg/util/interval" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/syncutil" ) +// EnabledSetting is a hidden cluster setting to enable the use of the span +// configs infrastructure in KV. It switches each store in the cluster from +// using the gossip backed system config span to instead using the span configs +// infrastructure. It has no effect unless COCKROACH_EXPERIMENTAL_SPAN_CONFIGS +// is set. +var EnabledSetting = settings.RegisterBoolSetting( + "spanconfig.experimental_store.enabled", + `use the span config infrastructure in KV instead of the system config span`, + false, +).WithSystemOnly() + // Store is an in-memory data structure to store and retrieve span configs. // Internally it makes use of an interval tree to store non-overlapping span -// configs. +// configs. It's safe for concurrent use. type Store struct { mu struct { syncutil.RWMutex diff --git a/pkg/spanconfig/testing_knobs.go b/pkg/spanconfig/testing_knobs.go index bf498332d19e..acde9d9f34b6 100644 --- a/pkg/spanconfig/testing_knobs.go +++ b/pkg/spanconfig/testing_knobs.go @@ -10,7 +10,10 @@ package spanconfig -import "github.com/cockroachdb/cockroach/pkg/base" +import ( + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/util/hlc" +) // TestingKnobs provide fine-grained control over the various span config // components for testing. @@ -31,6 +34,25 @@ type TestingKnobs struct { // manager has checked if the auto span config reconciliation job exists or // not. ManagerAfterCheckedReconciliationJobExistsInterceptor func(exists bool) + + // KVSubscriberPostRangefeedStartInterceptor is invoked after the rangefeed is started. + KVSubscriberPostRangefeedStartInterceptor func() + + // KVSubscriberPreExitInterceptor is invoked right before returning from + // subscribeInner, after tearing down internal components. + KVSubscriberPreExitInterceptor func() + + // KVSubscriberOnTimestampAdvanceInterceptor is invoked each time the + // KVSubscriber has process all updates before the provided timestamp. + KVSubscriberOnTimestampAdvanceInterceptor func(hlc.Timestamp) + + // KVSubscriberErrorInjectionCh is a way for tests to conveniently inject + // buffer overflow errors into the subscriber in order to test recovery. + KVSubscriberErrorInjectionCh chan error + + // StoreKVSubscriberOverride is used to override the KVSubscriber used when + // setting up a new store. + StoreKVSubscriberOverride KVSubscriber } // ModuleTestingKnobs is part of the base.ModuleTestingKnobs interface. diff --git a/pkg/sql/catalog/systemschema/system.go b/pkg/sql/catalog/systemschema/system.go index 3fd5dc5fdbb1..5ddafb6fd02b 100644 --- a/pkg/sql/catalog/systemschema/system.go +++ b/pkg/sql/catalog/systemschema/system.go @@ -2245,7 +2245,14 @@ var ( ColumnIDs: []descpb.ColumnID{1, 2, 3}, }, }, - pk("start_key"), + descpb.IndexDescriptor{ + Name: "primary", + ID: keys.SpanConfigurationsTablePrimaryKeyIndexID, + Unique: true, + KeyColumnNames: []string{"start_key"}, + KeyColumnDirections: singleASC, + KeyColumnIDs: singleID1, + }, ), func(tbl *descpb.TableDescriptor) { tbl.Checks = []*descpb.TableDescriptor_CheckConstraint{{ From 2407d3f586d6abe132c3e7f3807678eb9d2154b1 Mon Sep 17 00:00:00 2001 From: Rafi Shamim Date: Tue, 2 Nov 2021 16:53:16 -0400 Subject: [PATCH 193/205] server,cli: fix improperly wrapped errors I'm working on a linter that detects errors that are not wrapped correctly, and it discovered these. Release note: None --- pkg/base/store_spec.go | 4 ++-- pkg/base/store_spec_test.go | 2 +- pkg/cli/clisqlclient/conn_test.go | 6 +++--- pkg/cli/clisqlshell/sql.go | 2 +- pkg/server/node.go | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/base/store_spec.go b/pkg/base/store_spec.go index 6ce9e9c89724..a8d6c77a9a2e 100644 --- a/pkg/base/store_spec.go +++ b/pkg/base/store_spec.go @@ -94,7 +94,7 @@ func NewSizeSpec( size.Percent, err = strconv.ParseFloat(factorValue, 64) size.Percent *= percentFactor if err != nil { - return SizeSpec{}, errors.Newf("could not parse %s size (%s) %s", field, value, err) + return SizeSpec{}, errors.Wrapf(err, "could not parse %s size (%s)", field, value) } if percentRange != nil { if (percentRange.min != nil && size.Percent < *percentRange.min) || @@ -112,7 +112,7 @@ func NewSizeSpec( var err error size.InBytes, err = humanizeutil.ParseBytes(value) if err != nil { - return SizeSpec{}, errors.Newf("could not parse %s size (%s) %s", field, value, err) + return SizeSpec{}, errors.Wrapf(err, "could not parse %s size (%s)", field, value) } if bytesRange != nil { if bytesRange.min != nil && size.InBytes < *bytesRange.min { diff --git a/pkg/base/store_spec_test.go b/pkg/base/store_spec_test.go index 046a5d2cb7b1..f7fd65984222 100644 --- a/pkg/base/store_spec_test.go +++ b/pkg/base/store_spec_test.go @@ -123,7 +123,7 @@ target_file_size=2097152` {"path=/mnt/hda1,size=.009999", "store size (.009999) must be between 1.000000% and 100.000000%", StoreSpec{}}, // errors {"path=/mnt/hda1,size=0", "store size (0) must be larger than 640 MiB", StoreSpec{}}, - {"path=/mnt/hda1,size=abc", "could not parse store size (abc) strconv.ParseFloat: parsing \"\": invalid syntax", StoreSpec{}}, + {"path=/mnt/hda1,size=abc", "could not parse store size (abc): strconv.ParseFloat: parsing \"\": invalid syntax", StoreSpec{}}, {"path=/mnt/hda1,size=", "no value specified for size", StoreSpec{}}, {"size=20GiB,path=/mnt/hda1,size=20GiB", "size field was used twice in store definition", StoreSpec{}}, {"size=123TB", "no path specified", StoreSpec{}}, diff --git a/pkg/cli/clisqlclient/conn_test.go b/pkg/cli/clisqlclient/conn_test.go index b385017b82bf..5fe07d75c7ad 100644 --- a/pkg/cli/clisqlclient/conn_test.go +++ b/pkg/cli/clisqlclient/conn_test.go @@ -64,12 +64,12 @@ func TestConnRecover(t *testing.T) { // and starts delivering ErrBadConn. We don't know the timing of // this however. testutils.SucceedsSoon(t, func() error { - if sqlRows, err := conn.Query(`SELECT 1`, nil); !errors.Is(err, driver.ErrBadConn) { - return errors.Newf("expected ErrBadConn, got %v", err) - } else if err == nil { + if sqlRows, err := conn.Query(`SELECT 1`, nil); err == nil { if closeErr := sqlRows.Close(); closeErr != nil { t.Fatal(closeErr) } + } else if !errors.Is(err, driver.ErrBadConn) { + return errors.Newf("expected ErrBadConn, got %v", err) } return nil }) diff --git a/pkg/cli/clisqlshell/sql.go b/pkg/cli/clisqlshell/sql.go index 7c5d974e23e8..6f29c2fdc003 100644 --- a/pkg/cli/clisqlshell/sql.go +++ b/pkg/cli/clisqlshell/sql.go @@ -682,7 +682,7 @@ func (c *cliState) execSyscmd(command string) (string, error) { cmd.Stderr = c.iCtx.stderr if err := cmd.Run(); err != nil { - return "", fmt.Errorf("error in external command: %s", err) + return "", errors.Wrap(err, "error in external command") } return out.String(), nil diff --git a/pkg/server/node.go b/pkg/server/node.go index fcc05e71e5f8..2208ebd1f992 100644 --- a/pkg/server/node.go +++ b/pkg/server/node.go @@ -449,7 +449,7 @@ func (n *Node) start( // gossip can bootstrap using the most recently persisted set of // node addresses. if err := n.storeCfg.Gossip.SetStorage(n.stores); err != nil { - return fmt.Errorf("failed to initialize the gossip interface: %s", err) + return errors.Wrap(err, "failed to initialize the gossip interface") } // Initialize remaining stores/engines, if any. From 39c4656b352a659bf3a12dbb4f17e454e486048b Mon Sep 17 00:00:00 2001 From: rimadeodhar Date: Wed, 3 Nov 2021 12:09:51 -0700 Subject: [PATCH 194/205] [serverccl] Fix flaky tenant status test. The Statements method on the tenant status server relies on the sqlinstance system to retrieve SQL pod information. The sqlinstance system was updated recently to cache SQL pod data backed by a rangefeed. This caused the tenant status to fail occassionally with make stress as there could be a race between when the cache is populated and the statements are queried. This PR updates the test to ensure this race condition is eliminated. Release note: None --- pkg/ccl/serverccl/BUILD.bazel | 2 ++ pkg/ccl/serverccl/tenant_status_test.go | 32 ++++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/pkg/ccl/serverccl/BUILD.bazel b/pkg/ccl/serverccl/BUILD.bazel index bba8971b9057..50323a416b59 100644 --- a/pkg/ccl/serverccl/BUILD.bazel +++ b/pkg/ccl/serverccl/BUILD.bazel @@ -57,6 +57,7 @@ go_test( "//pkg/sql/pgwire/pgcode", "//pkg/sql/sqlstats", "//pkg/sql/tests", + "//pkg/testutils", "//pkg/testutils/serverutils", "//pkg/testutils/skip", "//pkg/testutils/sqlutils", @@ -67,6 +68,7 @@ go_test( "//pkg/util/log", "//pkg/util/randutil", "//pkg/util/timeutil", + "@com_github_cockroachdb_errors//:errors", "@com_github_elastic_gosigar//:gosigar", "@com_github_lib_pq//:pq", "@com_github_prometheus_client_model//go", diff --git a/pkg/ccl/serverccl/tenant_status_test.go b/pkg/ccl/serverccl/tenant_status_test.go index 70b9fb566268..c9e6f05f350e 100644 --- a/pkg/ccl/serverccl/tenant_status_test.go +++ b/pkg/ccl/serverccl/tenant_status_test.go @@ -31,11 +31,13 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/idxusage" "github.com/cockroachdb/cockroach/pkg/sql/sqlstats" "github.com/cockroachdb/cockroach/pkg/sql/tests" + "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" "github.com/cockroachdb/cockroach/pkg/testutils/skip" "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/errors" "github.com/stretchr/testify/require" ) @@ -115,13 +117,31 @@ func TestTenantCannotSeeNonTenantStats(t *testing.T) { require.NoError(t, err) request := &serverpb.StatementsRequest{} - - tenantStats, err := tenantStatusServer.Statements(ctx, request) - require.NoError(t, err) - combinedStatsRequest := &serverpb.CombinedStatementsStatsRequest{} - tenantCombinedStats, err := tenantStatusServer.CombinedStatementStats(ctx, combinedStatsRequest) - require.NoError(t, err) + var tenantStats *serverpb.StatementsResponse + var tenantCombinedStats *serverpb.StatementsResponse + + // Populate `tenantStats` and `tenantCombinedStats`. The tenant server + // `Statements` and `CombinedStatements` methods are backed by the + // sqlinstance system which uses a cache populated through rangefeed + // for keeping track of SQL pod data. We use `SucceedsSoon` to eliminate + // race condition with the sqlinstance cache population such as during + // a stress test. + testutils.SucceedsSoon(t, func() error { + tenantStats, err = tenantStatusServer.Statements(ctx, request) + if err != nil { + return err + } + if tenantStats == nil || len(tenantStats.Statements) == 0 { + return errors.New("tenant statements are unexpectedly empty") + } + + tenantCombinedStats, err = tenantStatusServer.CombinedStatementStats(ctx, combinedStatsRequest) + if tenantCombinedStats == nil || len(tenantCombinedStats.Statements) == 0 { + return errors.New("tenant combined statements are unexpectedly empty") + } + return nil + }) path := "/_status/statements" var nonTenantStats serverpb.StatementsResponse From e800d749e5384d52b008029fdc26ce89bff97db9 Mon Sep 17 00:00:00 2001 From: Marius Posta Date: Thu, 23 Sep 2021 09:48:26 -0400 Subject: [PATCH 195/205] sql: remove dead interleave code In the previous release, we disabled interleaved tables and indexes. Therefore, at this point in time, all code pertaining to this feature is effectively dead. This commit removes a lot of it by removing the catalog methods and their callers. The remaining dead code is now located in the SQL grammar definition and the plan nodes, and is removed in the subsequent commit. Release note: None --- .../settings/settings-for-tenants.txt | 1 - docs/generated/settings/settings.html | 1 - docs/generated/sql/bnf/stmt_block.bnf | 2 - pkg/bench/bench_test.go | 84 - pkg/ccl/backupccl/BUILD.bazel | 1 - pkg/ccl/backupccl/backup_planning.go | 31 +- pkg/ccl/backupccl/backup_test.go | 42 - pkg/ccl/backupccl/backupresolver/targets.go | 7 +- pkg/ccl/backupccl/create_scheduled_backup.go | 5 +- .../backupccl/create_scheduled_backup_test.go | 13 - pkg/ccl/backupccl/key_rewriter.go | 115 +- pkg/ccl/backupccl/key_rewriter_test.go | 10 +- pkg/ccl/backupccl/restore_data_processor.go | 2 +- pkg/ccl/backupccl/restore_job.go | 2 +- pkg/ccl/backupccl/restore_planning.go | 34 +- pkg/ccl/backupccl/targets.go | 34 - .../create_interleaved.sql | 18 - .../interleaved/0/.DS_Store | Bin 6148 -> 0 bytes ...KUP-CHECKPOINT-684192901900697601-CHECKSUM | 2 - .../interleaved/0/BACKUP-CHECKPOINT-CHECKSUM | 1 - .../interleaved/0/BACKUP-STATISTICS | Bin 435 -> 0 bytes .../interleaved/0/BACKUP_MANIFEST | Bin 787 -> 0 bytes .../interleaved/0/BACKUP_MANIFEST-CHECKSUM | 1 - .../interleaved/0/data/684192909531021315.sst | Bin 1009 -> 0 bytes pkg/ccl/importccl/csv_internal_test.go | 4 - pkg/ccl/importccl/import_planning.go | 6 - pkg/ccl/importccl/read_import_mysql_test.go | 4 +- .../testdata/logic_test/partitioning_index | 11 - .../testdata/explain-bundle/bundle/env.sql | 2 - pkg/clusterversion/cockroach_versions.go | 14 - pkg/clusterversion/key_string.go | 42 +- pkg/jobs/jobspb/jobs.pb.go | 948 ++--- pkg/jobs/jobspb/jobs.proto | 9 +- pkg/migration/migrations/BUILD.bazel | 2 - .../migrations/interleaved_tables.go | 48 - .../interleaved_tables_external_test.go | 119 - pkg/migration/migrations/migrations.go | 6 - pkg/server/settingswatcher/row_decoder.go | 2 +- pkg/settings/registry.go | 13 +- .../span_config_decoder.go | 5 +- pkg/sql/alter_primary_key.go | 184 +- pkg/sql/alter_table_locality.go | 4 - pkg/sql/backfill.go | 168 +- pkg/sql/catalog/catalogkeys/keys.go | 33 +- pkg/sql/catalog/catconstants/constants.go | 1 - pkg/sql/catalog/catformat/index.go | 5 +- pkg/sql/catalog/catformat/index_test.go | 40 +- pkg/sql/catalog/descpb/index.go | 5 - pkg/sql/catalog/descpb/structured.pb.go | 713 ++-- pkg/sql/catalog/descpb/structured.proto | 7 +- pkg/sql/catalog/descriptor.go | 3 - pkg/sql/catalog/table_elements.go | 7 - pkg/sql/catalog/tabledesc/index.go | 28 - pkg/sql/catalog/tabledesc/index_test.go | 6 - pkg/sql/catalog/tabledesc/safe_format.go | 22 - pkg/sql/catalog/tabledesc/structured.go | 17 - pkg/sql/catalog/tabledesc/validate.go | 100 +- pkg/sql/catalog/tabledesc/validate_test.go | 134 +- pkg/sql/colfetcher/cfetcher.go | 6 +- pkg/sql/crdb_internal.go | 202 +- pkg/sql/create_index.go | 45 +- pkg/sql/create_table.go | 199 +- pkg/sql/doctor/doctor_test.go | 5 - pkg/sql/drop_index.go | 11 - pkg/sql/drop_table.go | 124 +- pkg/sql/exec_util.go | 12 - pkg/sql/gcjob/BUILD.bazel | 1 - pkg/sql/gcjob/gc_job.go | 24 - pkg/sql/gcjob/table_garbage_collection.go | 5 +- .../testdata/logic_test/crdb_internal | 1 - .../testdata/logic_test/crdb_internal_tenant | 1 - .../testdata/logic_test/create_statements | 17 - .../testdata/logic_test/create_table | 3 - .../logictest/testdata/logic_test/grant_table | 1 - .../testdata/logic_test/information_schema | 6 - .../logictest/testdata/logic_test/pg_builtins | 72 +- .../logictest/testdata/logic_test/pg_catalog | 3403 ++++++++--------- .../logictest/testdata/logic_test/show_source | 1 - pkg/sql/logictest/testdata/logic_test/table | 1 - pkg/sql/opt/cat/BUILD.bazel | 1 - pkg/sql/opt/cat/index.go | 43 - pkg/sql/opt/cat/utils.go | 18 - .../opttester/testfixtures/tpcc_schema | 8 +- pkg/sql/opt/testutils/testcat/create_table.go | 6 - pkg/sql/opt/testutils/testcat/test_catalog.go | 24 - pkg/sql/opt/xform/testdata/external/customer | 2 +- pkg/sql/opt/xform/testdata/rules/join | 154 - pkg/sql/opt_catalog.go | 42 - pkg/sql/parser/sql.y | 8 +- pkg/sql/parser/testdata/backup_restore | 8 - pkg/sql/pg_catalog.go | 33 +- pkg/sql/pgwire/testdata/pgtest/notice | 2 +- pkg/sql/randgen/schema.go | 153 +- pkg/sql/resolver.go | 31 - pkg/sql/revert.go | 17 +- pkg/sql/row/deleter.go | 30 - pkg/sql/row/fetcher.go | 8 +- pkg/sql/rowenc/index_encoding.go | 395 +- pkg/sql/rowenc/index_encoding_test.go | 341 +- pkg/sql/schema_changer.go | 94 +- pkg/sql/schemachanger/scbuild/table.go | 10 - pkg/sql/sem/tree/backup.go | 22 +- .../local_only_session_data.pb.go | 288 +- .../local_only_session_data.proto | 6 +- pkg/sql/show_create.go | 83 +- pkg/sql/show_create_clauses.go | 40 - pkg/sql/span/span_builder.go | 19 +- .../sqlinstance/instancestorage/row_codec.go | 2 +- pkg/sql/stats/stats_cache.go | 6 +- pkg/sql/tablewriter_delete.go | 181 +- pkg/sql/truncate.go | 149 +- pkg/sql/vars.go | 18 - pkg/testutils/sqlutils/table_gen.go | 33 +- pkg/workload/ledger/ledger.go | 6 - pkg/workload/tpcc/ddls.go | 35 +- pkg/workload/tpcc/tpcc.go | 25 - 116 files changed, 2899 insertions(+), 6700 deletions(-) delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/create_interleaved.sql delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/.DS_Store delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP-CHECKPOINT-684192901900697601-CHECKSUM delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP-CHECKPOINT-CHECKSUM delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP-STATISTICS delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/BACKUP_MANIFEST-CHECKSUM delete mode 100644 pkg/ccl/backupccl/testdata/restore_old_versions/interleaved/0/data/684192909531021315.sst delete mode 100644 pkg/migration/migrations/interleaved_tables.go delete mode 100644 pkg/migration/migrations/interleaved_tables_external_test.go diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index 7c24361ae39e..2574ca609b7d 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -79,7 +79,6 @@ sql.cross_db_fks.enabled boolean false if true, creating foreign key references sql.cross_db_sequence_owners.enabled boolean false if true, creating sequences owned by tables from other databases is allowed sql.cross_db_sequence_references.enabled boolean false if true, sequences referenced by tables from other databases are allowed sql.cross_db_views.enabled boolean false if true, creating views that refer to other databases is allowed -sql.defaults.copy_partitioning_when_deinterleaving_table.enabled boolean false default value for enable_copying_partitioning_when_deinterleaving_table session variable sql.defaults.datestyle enumeration iso, mdy default value for DateStyle session setting [iso, mdy = 0, iso, dmy = 1, iso, ymd = 2] sql.defaults.datestyle.enabled boolean false default value for datestyle_enabled session setting sql.defaults.default_int_size integer 8 the size, in bytes, of an INT type diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 8924c5c2b7f6..f79e16ccfbf4 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -84,7 +84,6 @@
sql.cross_db_sequence_owners.enabledbooleanfalseif true, creating sequences owned by tables from other databases is allowed
sql.cross_db_sequence_references.enabledbooleanfalseif true, sequences referenced by tables from other databases are allowed
sql.cross_db_views.enabledbooleanfalseif true, creating views that refer to other databases is allowed
sql.defaults.copy_partitioning_when_deinterleaving_table.enabledbooleanfalsedefault value for enable_copying_partitioning_when_deinterleaving_table session variable
sql.defaults.datestyleenumerationiso, mdydefault value for DateStyle session setting [iso, mdy = 0, iso, dmy = 1, iso, ymd = 2]
sql.defaults.datestyle.enabledbooleanfalsedefault value for datestyle_enabled session setting
sql.defaults.default_int_sizeinteger8the size, in bytes, of an INT type