diff --git a/pkg/cmd/roachprod/main.go b/pkg/cmd/roachprod/main.go index 8b30d422545f..9863a1074264 100644 --- a/pkg/cmd/roachprod/main.go +++ b/pkg/cmd/roachprod/main.go @@ -1857,6 +1857,10 @@ func main() { `Ignored if --local-ssd=false is specified.`) createCmd.Flags().IntVarP(&numNodes, "nodes", "n", 4, "Total number of nodes, distributed across all clouds") + + createCmd.Flags().IntVarP(&createVMOpts.OsVolumeSize, + "os-volume-size", "", 10, "OS disk volume size in GB") + createCmd.Flags().StringSliceVarP(&createVMOpts.VMProviders, "clouds", "c", []string{gce.ProviderName}, fmt.Sprintf("The cloud provider(s) to use when creating new vm instances: %s", vm.AllProviderNames())) diff --git a/pkg/cmd/roachprod/vm/aws/aws.go b/pkg/cmd/roachprod/vm/aws/aws.go index d74bceee6b52..8fee83f437f6 100644 --- a/pkg/cmd/roachprod/vm/aws/aws.go +++ b/pkg/cmd/roachprod/vm/aws/aws.go @@ -859,25 +859,37 @@ func (p *Provider) runInstance(name string, zone string, opts vm.CreateOpts) err v.Disk = p.opts.DefaultEBSVolume.Disk p.opts.EBSVolumes = append(p.opts.EBSVolumes, v) } + } - mapping, err := json.Marshal(p.opts.EBSVolumes) - if err != nil { - return err - } + osDiskVolume := &ebsVolume{ + DeviceName: "/dev/sda1", + Disk: ebsDisk{ + VolumeType: defaultEBSVolumeType, + VolumeSize: opts.OsVolumeSize, + DeleteOnTermination: true, + }, + } - deviceMapping, err := ioutil.TempFile("", "aws-block-device-mapping") - if err != nil { - return err - } - defer deviceMapping.Close() - if _, err := deviceMapping.Write(mapping); err != nil { - return err - } - args = append(args, - "--block-device-mapping", - "file://"+deviceMapping.Name(), - ) + p.opts.EBSVolumes = append(p.opts.EBSVolumes, osDiskVolume) + + mapping, err := json.Marshal(p.opts.EBSVolumes) + if err != nil { + return err + } + + deviceMapping, err := ioutil.TempFile("", "aws-block-device-mapping") + if err != nil { + return err } + defer deviceMapping.Close() + if _, err := deviceMapping.Write(mapping); err != nil { + return err + } + args = append(args, + "--block-device-mapping", + "file://"+deviceMapping.Name(), + ) + return p.runJSONCommand(args, &data) } diff --git a/pkg/cmd/roachprod/vm/azure/azure.go b/pkg/cmd/roachprod/vm/azure/azure.go index 91455307b383..60e609aab734 100644 --- a/pkg/cmd/roachprod/vm/azure/azure.go +++ b/pkg/cmd/roachprod/vm/azure/azure.go @@ -498,6 +498,11 @@ func (p *Provider) createVM( tags[tagLifetime] = to.StringPtr(opts.Lifetime.String()) tags[tagRoachprod] = to.StringPtr("true") + osVolumeSize := int32(opts.OsVolumeSize) + if osVolumeSize < 32 { + log.Print("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{ @@ -520,6 +525,7 @@ func (p *Provider) createVM( ManagedDisk: &compute.ManagedDiskParameters{ StorageAccountType: compute.StorageAccountTypesStandardSSDLRS, }, + DiskSizeGB: to.Int32Ptr(osVolumeSize), }, }, OsProfile: &compute.OSProfile{ diff --git a/pkg/cmd/roachprod/vm/gce/gcloud.go b/pkg/cmd/roachprod/vm/gce/gcloud.go index 6b836f611d6a..ea8d65cf0998 100644 --- a/pkg/cmd/roachprod/vm/gce/gcloud.go +++ b/pkg/cmd/roachprod/vm/gce/gcloud.go @@ -277,7 +277,7 @@ func (o *providerOpts) ConfigureCreateFlags(flags *pflag.FlagSet) { "Image to use to create the vm, "+ "use `gcloud compute images list --filter=\"family=ubuntu-2004-lts\"` to list available images") - flags.IntVar(&o.SSDCount, ProviderName+"-local-ssd-count", 1, + flags.IntVar(&o.SSDCount, ProviderName+"-local-ssd-count", 0, "Number of local SSDs to create, only used if local-ssd=true") flags.StringVar(&o.PDVolumeType, ProviderName+"-pd-volume-type", "pd-ssd", "Type of the persistent disk volume, only used if local-ssd=false") @@ -385,7 +385,6 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { "--scopes", "default,storage-rw", "--image", p.opts.Image, "--image-project", "ubuntu-os-cloud", - "--boot-disk-size", "10", "--boot-disk-type", "pd-ssd", } @@ -405,7 +404,7 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { // come in different sizes. // See: https://cloud.google.com/compute/docs/disks/ n2MachineTypes := regexp.MustCompile("^[cn]2-.+-16") - if n2MachineTypes.MatchString(p.opts.MachineType) && p.opts.SSDCount < 2 { + if n2MachineTypes.MatchString(p.opts.MachineType) && p.opts.SSDCount == 1 { fmt.Fprint(os.Stderr, "WARNING: SSD count must be at least 2 for n2 and c2 machine types with 16vCPU. Setting --gce-local-ssd-count to 2.\n") p.opts.SSDCount = 2 } @@ -444,7 +443,7 @@ func (p *Provider) Create(names []string, opts vm.CreateOpts) error { args = append(args, "--metadata-from-file", fmt.Sprintf("startup-script=%s", filename)) args = append(args, "--project", project) - + args = append(args, fmt.Sprintf("--boot-disk-size=%dGB", opts.OsVolumeSize)) var g errgroup.Group nodeZones := vm.ZonePlacement(len(zones), len(names)) diff --git a/pkg/cmd/roachprod/vm/vm.go b/pkg/cmd/roachprod/vm/vm.go index 89cf8a5eaf8e..8f367d80b625 100644 --- a/pkg/cmd/roachprod/vm/vm.go +++ b/pkg/cmd/roachprod/vm/vm.go @@ -140,6 +140,7 @@ type CreateOpts struct { // mounting the SSD. Ignored if UseLocalSSD is not set. NoExt4Barrier bool } + OsVolumeSize int } // MultipleProjectsOption is used to specify whether a command accepts multiple diff --git a/pkg/cmd/roachtest/sqlalchemy.go b/pkg/cmd/roachtest/sqlalchemy.go index 67c8cdd26d08..f7e8392e4bc2 100644 --- a/pkg/cmd/roachtest/sqlalchemy.go +++ b/pkg/cmd/roachtest/sqlalchemy.go @@ -22,7 +22,7 @@ import ( var sqlAlchemyResultRegex = regexp.MustCompile(`^(?Ptest.*::.*::[^ \[\]]*(?:\[.*])?) (?P\w+)\s+\[.+]$`) var sqlAlchemyReleaseTagRegex = regexp.MustCompile(`^rel_(?P\d+)_(?P\d+)_(?P\d+)$`) -var supportedSQLAlchemyTag = "rel_1_3_24" +var supportedSQLAlchemyTag = "rel_1_4_17" // This test runs the SQLAlchemy dialect test suite against a single Cockroach // node. @@ -155,7 +155,7 @@ func runSQLAlchemy(ctx context.Context, t *test, c *cluster) { // will fail. And it is safe to swallow it here. rawResults, _ := c.RunWithBuffer(ctx, t.l, node, `cd /mnt/data1/sqlalchemy/ && pytest --maxfail=0 \ - --requirements=cockroachdb.sqlalchemy.test_requirements:Requirements \ + --requirements=sqlalchemy_cockroachdb.requirements:Requirements \ --dburi=cockroachdb://root@localhost:26257/defaultdb?sslmode=disable \ test/dialect/test_suite.py `) diff --git a/pkg/sql/schemachanger/scbuild/builder_test.go b/pkg/sql/schemachanger/scbuild/builder_test.go index f66aa07b55ce..1500c0037cba 100644 --- a/pkg/sql/schemachanger/scbuild/builder_test.go +++ b/pkg/sql/schemachanger/scbuild/builder_test.go @@ -11,11 +11,13 @@ package scbuild_test import ( + "bufio" "bytes" "context" gojson "encoding/json" "fmt" "path/filepath" + "strings" "testing" "github.com/cockroachdb/cockroach/pkg/base" @@ -117,29 +119,46 @@ func TestBuilderAlterTable(t *testing.T) { }) } +// indentText indents text for formatting out marshaled data. +func indentText(input string, tab string) string { + result := strings.Builder{} + scanner := bufio.NewScanner(strings.NewReader(input)) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line := scanner.Text() + if len(line) == 0 { + continue + } + result.WriteString(tab) + result.WriteString(line) + result.WriteString("\n") + } + return result.String() +} + // marshalNodes marshals a []*scpb.Node to YAML. func marshalNodes(t *testing.T, nodes []*scpb.Node) string { - type mapNode struct { - Target map[string]interface{} - State string - } - mapNodes := make([]mapNode, 0, len(nodes)) + result := strings.Builder{} for _, node := range nodes { var buf bytes.Buffer - require.NoError(t, (&jsonpb.Marshaler{}).Marshal(&buf, node.Target)) + require.NoError(t, (&jsonpb.Marshaler{}).Marshal(&buf, node.Target.Element())) target := make(map[string]interface{}) require.NoError(t, gojson.Unmarshal(buf.Bytes(), &target)) - mapNodes = append(mapNodes, mapNode{ - Target: target, - State: node.State.String(), - }) + result.WriteString("- ") + result.WriteString(node.Target.Direction.String()) + result.WriteString(" ") + result.WriteString(node.Element().GetAttributes().String()) + result.WriteString("\n") + result.WriteString(indentText(fmt.Sprintf("state: %s\n", node.State.String()), " ")) + result.WriteString(indentText("details:\n", " ")) + out, err := yaml.Marshal(target) + require.NoError(t, err) + result.WriteString(indentText(string(out), " ")) } - out, err := yaml.Marshal(mapNodes) - require.NoError(t, err) - return string(out) + return result.String() } func newTestingBuilderDeps(s serverutils.TestServerInterface) (*scbuild.Dependencies, func()) { diff --git a/pkg/sql/schemachanger/scbuild/testdata/alter_table b/pkg/sql/schemachanger/scbuild/testdata/alter_table index 495c9068d7b4..fbbe49e67b50 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/alter_table +++ b/pkg/sql/schemachanger/scbuild/testdata/alter_table @@ -5,300 +5,274 @@ CREATE TABLE defaultdb.foo (i INT PRIMARY KEY) build ALTER TABLE defaultdb.foo ADD COLUMN j INT ---- -- target: - direction: ADD - elementProto: - column: - column: - id: 2 - name: j - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 52 +- ADD Column:{DescID: 52, ColumnID: 2, ElementName: "j"} state: ABSENT -- target: - direction: ADD - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 2 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: new_primary_key - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 1 - storeColumnIds: - - 2 - storeColumnNames: - - j - tableId: 52 + details: + column: + id: 2 + name: j + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 52 +- ADD PrimaryIndex:{DescID: 52, ElementName: "new_primary_key", IndexID: 2} state: ABSENT -- target: - direction: DROP - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 1 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: primary - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 2 - tableId: 52 + details: + index: + foreignKey: {} + geoConfig: {} + id: 2 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: new_primary_key + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 1 + storeColumnIds: + - 2 + storeColumnNames: + - j + tableId: 52 +- DROP PrimaryIndex:{DescID: 52, ElementName: "primary", IndexID: 1} state: PUBLIC + details: + index: + foreignKey: {} + geoConfig: {} + id: 1 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: primary + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 2 + tableId: 52 build ALTER TABLE defaultdb.foo ADD COLUMN j INT DEFAULT 123 ---- -- target: - direction: ADD - elementProto: - column: - column: - defaultExpr: 123:::INT8 - id: 2 - name: j - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 52 +- ADD Column:{DescID: 52, ColumnID: 2, ElementName: "j"} state: ABSENT -- target: - direction: ADD - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 2 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: new_primary_key - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 1 - storeColumnIds: - - 2 - storeColumnNames: - - j - tableId: 52 + details: + column: + defaultExpr: 123:::INT8 + id: 2 + name: j + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 52 +- ADD PrimaryIndex:{DescID: 52, ElementName: "new_primary_key", IndexID: 2} state: ABSENT -- target: - direction: DROP - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 1 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: primary - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 2 - tableId: 52 + details: + index: + foreignKey: {} + geoConfig: {} + id: 2 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: new_primary_key + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 1 + storeColumnIds: + - 2 + storeColumnNames: + - j + tableId: 52 +- DROP PrimaryIndex:{DescID: 52, ElementName: "primary", IndexID: 1} state: PUBLIC + details: + index: + foreignKey: {} + geoConfig: {} + id: 1 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: primary + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 2 + tableId: 52 build ALTER TABLE defaultdb.foo ADD COLUMN j INT DEFAULT 123; ALTER TABLE defaultdb.foo ADD COLUMN k INT DEFAULT 456; ---- -- target: - direction: ADD - elementProto: - column: - column: - defaultExpr: 123:::INT8 - id: 2 - name: j - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 52 +- ADD Column:{DescID: 52, ColumnID: 2, ElementName: "j"} state: ABSENT -- target: - direction: ADD - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 2 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: new_primary_key - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 1 - storeColumnIds: - - 2 - - 3 - storeColumnNames: - - j - - k - tableId: 52 + details: + column: + defaultExpr: 123:::INT8 + id: 2 + name: j + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 52 +- ADD PrimaryIndex:{DescID: 52, ElementName: "new_primary_key", IndexID: 2} state: ABSENT -- target: - direction: DROP - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 1 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: primary - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 2 - tableId: 52 + details: + index: + foreignKey: {} + geoConfig: {} + id: 2 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: new_primary_key + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 1 + storeColumnIds: + - 2 + - 3 + storeColumnNames: + - j + - k + tableId: 52 +- DROP PrimaryIndex:{DescID: 52, ElementName: "primary", IndexID: 1} state: PUBLIC -- target: - direction: ADD - elementProto: - column: - column: - defaultExpr: 456:::INT8 - id: 3 - name: k - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 52 + details: + index: + foreignKey: {} + geoConfig: {} + id: 1 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: primary + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 2 + tableId: 52 +- ADD Column:{DescID: 52, ColumnID: 3, ElementName: "k"} state: ABSENT + details: + column: + defaultExpr: 456:::INT8 + id: 3 + name: k + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 52 build ALTER TABLE defaultdb.foo ADD COLUMN a INT AS (i+1) STORED ---- -- target: - direction: ADD - elementProto: - column: - column: - computeExpr: i + 1:::INT8 - id: 2 - name: a - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 52 +- ADD Column:{DescID: 52, ColumnID: 2, ElementName: "a"} state: ABSENT -- target: - direction: ADD - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 2 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: new_primary_key - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 1 - storeColumnIds: - - 2 - storeColumnNames: - - a - tableId: 52 + details: + column: + computeExpr: i + 1:::INT8 + id: 2 + name: a + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 52 +- ADD PrimaryIndex:{DescID: 52, ElementName: "new_primary_key", IndexID: 2} state: ABSENT -- target: - direction: DROP - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 1 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: primary - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 2 - tableId: 52 + details: + index: + foreignKey: {} + geoConfig: {} + id: 2 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: new_primary_key + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 1 + storeColumnIds: + - 2 + storeColumnNames: + - a + tableId: 52 +- DROP PrimaryIndex:{DescID: 52, ElementName: "primary", IndexID: 1} state: PUBLIC + details: + index: + foreignKey: {} + geoConfig: {} + id: 1 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: primary + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 2 + tableId: 52 create-table CREATE TABLE defaultdb.bar (j INT); @@ -308,137 +282,125 @@ build ALTER TABLE defaultdb.foo ADD COLUMN a INT; ALTER TABLE defaultdb.bar ADD COLUMN b INT; ---- -- target: - direction: ADD - elementProto: - column: - column: - id: 2 - name: a - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 52 +- ADD Column:{DescID: 52, ColumnID: 2, ElementName: "a"} state: ABSENT -- target: - direction: ADD - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 2 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: new_primary_key - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 1 - storeColumnIds: - - 2 - storeColumnNames: - - a - tableId: 52 + details: + column: + id: 2 + name: a + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 52 +- ADD PrimaryIndex:{DescID: 52, ElementName: "new_primary_key", IndexID: 2} state: ABSENT -- target: - direction: DROP - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 1 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 1 - keyColumnNames: - - i - name: primary - partitioning: {} - sharded: {} - unique: true - version: 3 - otherPrimaryIndexId: 2 - tableId: 52 + details: + index: + foreignKey: {} + geoConfig: {} + id: 2 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: new_primary_key + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 1 + storeColumnIds: + - 2 + storeColumnNames: + - a + tableId: 52 +- DROP PrimaryIndex:{DescID: 52, ElementName: "primary", IndexID: 1} state: PUBLIC -- target: - direction: ADD - elementProto: - column: - column: - id: 3 - name: b - nullable: true - type: - family: IntFamily - oid: 20 - width: 64 - familyName: primary - tableId: 53 + details: + index: + foreignKey: {} + geoConfig: {} + id: 1 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 1 + keyColumnNames: + - i + name: primary + partitioning: {} + sharded: {} + unique: true + version: 3 + otherPrimaryIndexId: 2 + tableId: 52 +- ADD Column:{DescID: 53, ColumnID: 3, ElementName: "b"} state: ABSENT -- target: - direction: ADD - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 2 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 2 - keyColumnNames: - - rowid - name: new_primary_key - partitioning: {} - sharded: {} - unique: true - otherPrimaryIndexId: 1 - storeColumnIds: - - 1 - - 3 - storeColumnNames: - - j - - b - tableId: 53 + details: + column: + id: 3 + name: b + nullable: true + type: + family: IntFamily + oid: 20 + width: 64 + familyName: primary + tableId: 53 +- ADD PrimaryIndex:{DescID: 53, ElementName: "new_primary_key", IndexID: 2} state: ABSENT -- target: - direction: DROP - elementProto: - primaryIndex: - index: - foreignKey: {} - geoConfig: {} - id: 1 - interleave: {} - keyColumnDirections: - - ASC - keyColumnIds: - - 2 - keyColumnNames: - - rowid - name: primary - partitioning: {} - sharded: {} - unique: true - otherPrimaryIndexId: 2 - storeColumnIds: - - 1 - storeColumnNames: - - j - tableId: 53 + details: + index: + foreignKey: {} + geoConfig: {} + id: 2 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 2 + keyColumnNames: + - rowid + name: new_primary_key + partitioning: {} + sharded: {} + unique: true + otherPrimaryIndexId: 1 + storeColumnIds: + - 1 + - 3 + storeColumnNames: + - j + - b + tableId: 53 +- DROP PrimaryIndex:{DescID: 53, ElementName: "primary", IndexID: 1} state: PUBLIC + details: + index: + foreignKey: {} + geoConfig: {} + id: 1 + interleave: {} + keyColumnDirections: + - ASC + keyColumnIds: + - 2 + keyColumnNames: + - rowid + name: primary + partitioning: {} + sharded: {} + unique: true + otherPrimaryIndexId: 2 + storeColumnIds: + - 1 + storeColumnNames: + - j + tableId: 53 diff --git a/pkg/sql/schemachanger/scbuild/testdata/drop_sequence b/pkg/sql/schemachanger/scbuild/testdata/drop_sequence index 42ee78ee85e1..7c2368ad655e 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/drop_sequence +++ b/pkg/sql/schemachanger/scbuild/testdata/drop_sequence @@ -5,12 +5,10 @@ CREATE SEQUENCE defaultdb.SQ1 build DROP SEQUENCE defaultdb.SQ1 CASCADE ---- -- target: - direction: DROP - elementProto: - sequence: - sequenceId: 52 +- DROP Sequence:{DescID: 52} state: PUBLIC + details: + sequenceId: 52 create-table CREATE TABLE defaultdb.blog_posts (id INT PRIMARY KEY, val int DEFAULT nextval('defaultdb.sq1'), title text) @@ -23,27 +21,21 @@ CREATE TABLE defaultdb.blog_posts2 (id INT PRIMARY KEY, val int DEFAULT nextval( build DROP SEQUENCE defaultdb.SQ1 CASCADE ---- -- target: - direction: DROP - elementProto: - defaultExpression: - columnId: 2 - tableId: 53 - usesSequenceIDs: - - 52 +- DROP DefaultExpression:{DescID: 53, ColumnID: 2} state: PUBLIC -- target: - direction: DROP - elementProto: - defaultExpression: - columnId: 2 - tableId: 54 - usesSequenceIDs: - - 52 + details: + columnId: 2 + tableId: 53 + usesSequenceIDs: + - 52 +- DROP DefaultExpression:{DescID: 54, ColumnID: 2} state: PUBLIC -- target: - direction: DROP - elementProto: - sequence: - sequenceId: 52 + details: + columnId: 2 + tableId: 54 + usesSequenceIDs: + - 52 +- DROP Sequence:{DescID: 52} state: PUBLIC + details: + sequenceId: 52 diff --git a/pkg/sql/schemachanger/scbuild/testdata/drop_table b/pkg/sql/schemachanger/scbuild/testdata/drop_table index 7064701d736f..20397c802672 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/drop_table +++ b/pkg/sql/schemachanger/scbuild/testdata/drop_table @@ -38,96 +38,76 @@ CREATE VIEW v1 as (select customer_id, carrier from defaultdb.shipments); build DROP TABLE defaultdb.shipments CASCADE; ---- -- target: - direction: DROP - elementProto: - view: - dependedOnBy: [] - dependsOn: - - 55 - tableId: 57 +- DROP View:{DescID: 57} state: PUBLIC -- target: - direction: DROP - elementProto: - outForeignKey: - name: fk_customers - originColumns: - - 4 - originId: 55 - referenceColumns: - - 1 - referenceId: 52 + details: + dependedOnBy: [] + dependsOn: + - 55 + tableId: 57 +- DROP OutboundForeignKey:{DescID: 55, DepID: 52, ElementName: "fk_customers"} state: PUBLIC -- target: - direction: DROP - elementProto: - inForeignKey: - name: fk_customers - originColumns: - - 1 - originId: 52 - referenceColumns: - - 4 - referenceId: 55 + details: + name: fk_customers + originColumns: + - 4 + originId: 55 + referenceColumns: + - 1 + referenceId: 52 +- DROP InboundForeignKey:{DescID: 52, DepID: 55, ElementName: "fk_customers"} state: PUBLIC -- target: - direction: DROP - elementProto: - outForeignKey: - name: fk_orders - originColumns: - - 4 - originId: 55 - referenceColumns: - - 2 - referenceId: 53 + details: + name: fk_customers + originColumns: + - 1 + originId: 52 + referenceColumns: + - 4 + referenceId: 55 +- DROP OutboundForeignKey:{DescID: 55, DepID: 53, ElementName: "fk_orders"} state: PUBLIC -- target: - direction: DROP - elementProto: - inForeignKey: - name: fk_orders - originColumns: - - 2 - originId: 53 - referenceColumns: - - 4 - referenceId: 55 + details: + name: fk_orders + originColumns: + - 4 + originId: 55 + referenceColumns: + - 2 + referenceId: 53 +- DROP InboundForeignKey:{DescID: 53, DepID: 55, ElementName: "fk_orders"} state: PUBLIC -- target: - direction: DROP - elementProto: - sequence: - sequenceId: 56 + details: + name: fk_orders + originColumns: + - 2 + originId: 53 + referenceColumns: + - 4 + referenceId: 55 +- DROP Sequence:{DescID: 56} state: PUBLIC -- target: - direction: DROP - elementProto: - sequenceOwner: - ownerTableId: 55 - sequenceId: 56 + details: + sequenceId: 56 +- DROP SequenceOwnedBy:{DescID: 56, DepID: 55} state: PUBLIC -- target: - direction: DROP - elementProto: - defaultExpression: - columnId: 5 - defaultExpr: nextval(54:::REGCLASS) - tableId: 55 - usesSequenceIDs: - - 54 + details: + ownerTableId: 55 + sequenceId: 56 +- DROP DefaultExpression:{DescID: 55, ColumnID: 5} state: PUBLIC -- target: - direction: DROP - elementProto: - relationDependedOnBy: - dependedOn: 55 - tableId: 54 + details: + columnId: 5 + defaultExpr: nextval(54:::REGCLASS) + tableId: 55 + usesSequenceIDs: + - 54 +- DROP RelationDependedOnBy:{DescID: 54, DepID: 55} state: PUBLIC -- target: - direction: DROP - elementProto: - table: - tableId: 55 + details: + dependedOn: 55 + tableId: 54 +- DROP Table:{DescID: 55} state: PUBLIC + details: + tableId: 55 diff --git a/pkg/sql/schemachanger/scbuild/testdata/drop_view b/pkg/sql/schemachanger/scbuild/testdata/drop_view index 9e9a4a40b918..d75e558ee771 100644 --- a/pkg/sql/schemachanger/scbuild/testdata/drop_view +++ b/pkg/sql/schemachanger/scbuild/testdata/drop_view @@ -9,15 +9,13 @@ CREATE VIEW defaultdb.v1 AS (SELECT name FROM defaultdb.t1) build DROP VIEW defaultdb.v1 ---- -- target: - direction: DROP - elementProto: - view: - dependedOnBy: [] - dependsOn: - - 52 - tableId: 53 +- DROP View:{DescID: 53} state: PUBLIC + details: + dependedOnBy: [] + dependsOn: + - 52 + tableId: 53 create-view CREATE VIEW defaultdb.v2 AS (SELECT name AS n1, name AS n2 FROM v1) @@ -42,68 +40,54 @@ CREATE VIEW v5 AS (SELECT 'a'::defaultdb.typ::string AS k, n2, n1 from defaultdb build DROP VIEW defaultdb.v1 CASCADE ---- -- target: - direction: DROP - elementProto: - view: - dependedOnBy: - - 54 - - 55 - dependsOn: - - 52 - tableId: 53 +- DROP View:{DescID: 53} state: PUBLIC -- target: - direction: DROP - elementProto: - view: - dependedOnBy: - - 55 - - 56 - dependsOn: - - 53 - tableId: 54 + details: + dependedOnBy: + - 54 + - 55 + dependsOn: + - 52 + tableId: 53 +- DROP View:{DescID: 54} state: PUBLIC -- target: - direction: DROP - elementProto: - view: - dependedOnBy: [] - dependsOn: - - 53 - - 54 - tableId: 55 + details: + dependedOnBy: + - 55 + - 56 + dependsOn: + - 53 + tableId: 54 +- DROP View:{DescID: 55} state: PUBLIC -- target: - direction: DROP - elementProto: - view: - dependedOnBy: - - 59 - dependsOn: - - 54 - tableId: 56 + details: + dependedOnBy: [] + dependsOn: + - 53 + - 54 + tableId: 55 +- DROP View:{DescID: 56} state: PUBLIC -- target: - direction: DROP - elementProto: - view: - dependedOnBy: [] - dependsOn: - - 56 - tableId: 59 + details: + dependedOnBy: + - 59 + dependsOn: + - 54 + tableId: 56 +- DROP View:{DescID: 59} state: PUBLIC -- target: - direction: DROP - elementProto: - typeRef: - descriptorId: 59 - typeId: 57 + details: + dependedOnBy: [] + dependsOn: + - 56 + tableId: 59 +- DROP TypeReference:{DescID: 57, DepID: 59} state: PUBLIC -- target: - direction: DROP - elementProto: - typeRef: - descriptorId: 59 - typeId: 58 + details: + descriptorId: 59 + typeId: 57 +- DROP TypeReference:{DescID: 58, DepID: 59} state: PUBLIC + details: + descriptorId: 59 + typeId: 58 diff --git a/pkg/sql/schemachanger/scplan/plan_test.go b/pkg/sql/schemachanger/scplan/plan_test.go index 8461beb2ec53..821ac94c664b 100644 --- a/pkg/sql/schemachanger/scplan/plan_test.go +++ b/pkg/sql/schemachanger/scplan/plan_test.go @@ -11,6 +11,7 @@ package scplan_test import ( + "bufio" "context" "fmt" "path/filepath" @@ -129,18 +130,20 @@ func TestPlanAlterTable(t *testing.T) { } // indentText indents text for formatting out marshaled data. -func indentText(input string, tab string) (final string) { - split := strings.Split(input, "\n") - for idx, line := range split { +func indentText(input string, tab string) string { + result := strings.Builder{} + scanner := bufio.NewScanner(strings.NewReader(input)) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + line := scanner.Text() if len(line) == 0 { continue } - final += tab + line - if idx != len(split)-1 { - final = final + "\n" - } + result.WriteString(tab) + result.WriteString(line) + result.WriteString("\n") } - return final + return result.String() } // marshalDeps marshals dependencies in scplan.Plan to a string. diff --git a/pkg/ui/cluster-ui/package.json b/pkg/ui/cluster-ui/package.json index 1372803cb4e6..aff9a5c0c0b7 100644 --- a/pkg/ui/cluster-ui/package.json +++ b/pkg/ui/cluster-ui/package.json @@ -1,6 +1,6 @@ { "name": "@cockroachlabs/cluster-ui", - "version": "21.2.0-prerelease-0", + "version": "21.2.0-prerelease-1", "description": "Cluster UI is a library of large features shared between CockroachDB and CockroachCloud", "repository": { "type": "git", diff --git a/pkg/ui/cluster-ui/src/store/reducers.ts b/pkg/ui/cluster-ui/src/store/reducers.ts index 097ab5c63282..61c0fb72113f 100644 --- a/pkg/ui/cluster-ui/src/store/reducers.ts +++ b/pkg/ui/cluster-ui/src/store/reducers.ts @@ -19,6 +19,7 @@ import { import { NodesState, reducer as nodes } from "./nodes"; import { LivenessState, reducer as liveness } from "./liveness"; import { SessionsState, reducer as sessions } from "./sessions"; +import { TransactionsState, reducer as transactions } from "./transactions"; import { TerminateQueryState, reducer as terminateQuery, @@ -33,6 +34,7 @@ export type AdminUiState = { nodes: NodesState; liveness: LivenessState; sessions: SessionsState; + transactions: TransactionsState; terminateQuery: TerminateQueryState; uiConfig: UIConfigState; }; @@ -48,6 +50,7 @@ export const reducers = combineReducers({ nodes, liveness, sessions, + transactions, terminateQuery, uiConfig, }); diff --git a/pkg/ui/cluster-ui/src/store/sagas.ts b/pkg/ui/cluster-ui/src/store/sagas.ts index 9057d5746e8b..031aea9bcb23 100644 --- a/pkg/ui/cluster-ui/src/store/sagas.ts +++ b/pkg/ui/cluster-ui/src/store/sagas.ts @@ -16,6 +16,7 @@ import { statementsSaga } from "./statements"; import { nodesSaga } from "./nodes"; import { livenessSaga } from "./liveness"; import { sessionsSaga } from "./sessions"; +import { transactionsSaga } from "./transactions"; import { terminateSaga } from "./terminateQuery"; import { notifificationsSaga } from "./notifications"; import { sqlStatsSaga } from "./sqlStats"; @@ -29,6 +30,7 @@ export function* sagas(cacheInvalidationPeriod?: number) { fork(livenessSaga, cacheInvalidationPeriod), fork(sessionsSaga), fork(terminateSaga), + fork(transactionsSaga), fork(notifificationsSaga), fork(sqlStatsSaga), ]); diff --git a/pkg/ui/cluster-ui/src/store/transactions/index.ts b/pkg/ui/cluster-ui/src/store/transactions/index.ts new file mode 100644 index 000000000000..14a252d8b2d5 --- /dev/null +++ b/pkg/ui/cluster-ui/src/store/transactions/index.ts @@ -0,0 +1,12 @@ +// 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. + +export * from "./transactions.reducer"; +export * from "./transactions.sagas"; diff --git a/pkg/ui/cluster-ui/src/store/transactions/transactions.reducer.ts b/pkg/ui/cluster-ui/src/store/transactions/transactions.reducer.ts new file mode 100644 index 000000000000..29e3a66c1c2b --- /dev/null +++ b/pkg/ui/cluster-ui/src/store/transactions/transactions.reducer.ts @@ -0,0 +1,51 @@ +// 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. + +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { cockroach } from "@cockroachlabs/crdb-protobuf-client"; +import { DOMAIN_NAME, noopReducer } from "../utils"; + +type StatementsResponse = cockroach.server.serverpb.StatementsResponse; + +export type TransactionsState = { + data: StatementsResponse; + lastError: Error; + valid: boolean; +}; + +const initialState: TransactionsState = { + data: null, + lastError: null, + valid: true, +}; + +const transactionsSlice = createSlice({ + name: `${DOMAIN_NAME}/transactions`, + initialState, + reducers: { + received: (state, action: PayloadAction) => { + state.data = action.payload; + state.valid = true; + state.lastError = null; + }, + failed: (state, action: PayloadAction) => { + state.valid = false; + state.lastError = action.payload; + }, + invalidated: state => { + state.valid = false; + }, + // Define actions that don't change state + refresh: noopReducer, + request: noopReducer, + }, +}); + +export const { reducer, actions } = transactionsSlice; diff --git a/pkg/ui/cluster-ui/src/store/transactions/transactions.sagas.spec.ts b/pkg/ui/cluster-ui/src/store/transactions/transactions.sagas.spec.ts new file mode 100644 index 000000000000..2ee891fb1bca --- /dev/null +++ b/pkg/ui/cluster-ui/src/store/transactions/transactions.sagas.spec.ts @@ -0,0 +1,87 @@ +// 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. + +import { expectSaga } from "redux-saga-test-plan"; +import { throwError } from "redux-saga-test-plan/providers"; +import * as matchers from "redux-saga-test-plan/matchers"; +import { cockroach } from "@cockroachlabs/crdb-protobuf-client"; + +import { getStatements } from "src/api/statementsApi"; + +import { + receivedTransactionsSaga, + refreshTransactionsSaga, + requestTransactionsSaga, +} from "./transactions.sagas"; +import { actions, reducer, TransactionsState } from "./transactions.reducer"; + +describe("TransactionsPage sagas", () => { + const statements = new cockroach.server.serverpb.StatementsResponse({ + statements: [], + last_reset: null, + }); + + describe("refreshTransactionsSaga", () => { + it("dispatches request transactions action", () => { + expectSaga(refreshTransactionsSaga) + .put(actions.request()) + .run(); + }); + }); + + describe("requestStatementsSaga", () => { + it("successfully requests statements list", () => { + expectSaga(requestTransactionsSaga) + .provide([[matchers.call.fn(getStatements), statements]]) + .put(actions.received(statements)) + .withReducer(reducer) + .hasFinalState({ + data: statements, + lastError: null, + valid: true, + }) + .run(); + }); + + it("returns error on failed request", () => { + const error = new Error("Failed request"); + expectSaga(requestTransactionsSaga) + .provide([[matchers.call.fn(getStatements), throwError(error)]]) + .put(actions.failed(error)) + .withReducer(reducer) + .hasFinalState({ + data: null, + lastError: error, + valid: false, + }) + .run(); + }); + }); + + describe("receivedStatementsSaga", () => { + it("sets valid status to false after specified period of time", () => { + const timeout = 500; + expectSaga(receivedTransactionsSaga, timeout) + .delay(timeout) + .put(actions.invalidated()) + .withReducer(reducer, { + data: statements, + lastError: null, + valid: true, + }) + .hasFinalState({ + data: statements, + lastError: null, + valid: false, + }) + .run(1000); + }); + }); +}); diff --git a/pkg/ui/cluster-ui/src/store/transactions/transactions.sagas.ts b/pkg/ui/cluster-ui/src/store/transactions/transactions.sagas.ts new file mode 100644 index 000000000000..b959f73158e0 --- /dev/null +++ b/pkg/ui/cluster-ui/src/store/transactions/transactions.sagas.ts @@ -0,0 +1,53 @@ +// 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. + +import { all, call, put, delay, takeLatest } from "redux-saga/effects"; +import { getStatements } from "src/api/statementsApi"; +import { actions } from "./transactions.reducer"; +import { rootActions } from "../reducers"; + +import { CACHE_INVALIDATION_PERIOD, throttleWithReset } from "src/store/utils"; + +export function* refreshTransactionsSaga() { + yield put(actions.request()); +} + +export function* requestTransactionsSaga() { + try { + const result = yield call(getStatements); + yield put(actions.received(result)); + } catch (e) { + yield put(actions.failed(e)); + } +} + +export function* receivedTransactionsSaga(delayMs: number) { + yield delay(delayMs); + yield put(actions.invalidated()); +} + +export function* transactionsSaga( + cacheInvalidationPeriod: number = CACHE_INVALIDATION_PERIOD, +) { + yield all([ + throttleWithReset( + cacheInvalidationPeriod, + actions.refresh, + [actions.invalidated, actions.failed, rootActions.resetState], + refreshTransactionsSaga, + ), + takeLatest(actions.request, requestTransactionsSaga), + takeLatest( + actions.received, + receivedTransactionsSaga, + cacheInvalidationPeriod, + ), + ]); +} diff --git a/pkg/ui/cluster-ui/src/transactionsPage/index.ts b/pkg/ui/cluster-ui/src/transactionsPage/index.ts index 667cc1857895..9d01439d0025 100644 --- a/pkg/ui/cluster-ui/src/transactionsPage/index.ts +++ b/pkg/ui/cluster-ui/src/transactionsPage/index.ts @@ -9,3 +9,4 @@ // licenses/APL.txt. export * from "./transactionsPage"; +export * from "./transactionsPageConnected"; diff --git a/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.selectors.ts b/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.selectors.ts new file mode 100644 index 000000000000..c58f4d1c478c --- /dev/null +++ b/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.selectors.ts @@ -0,0 +1,28 @@ +// 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. + +import { createSelector } from "reselect"; + +import { adminUISelector } from "../statementsPage/statementsPage.selectors"; + +export const selectTransactionsSlice = createSelector( + adminUISelector, + adminUiState => adminUiState.transactions, +); + +export const selectTransactionsData = createSelector( + selectTransactionsSlice, + transactionsState => transactionsState.data, +); + +export const selectTransactionsLastError = createSelector( + selectTransactionsSlice, + state => state.lastError, +); diff --git a/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.tsx b/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.tsx index 0ca135f52d62..bce9dd8605e5 100644 --- a/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.tsx +++ b/pkg/ui/cluster-ui/src/transactionsPage/transactionsPage.tsx @@ -60,17 +60,25 @@ interface TState { transactionStats: TransactionStats | null; } -interface TransactionsPageProps { +export interface TransactionsPageStateProps { data: IStatementsResponse; nodeRegions: { [nodeId: string]: string }; - refreshData: () => void; - resetSQLStats: () => void; error?: Error | null; pageSize?: number; } +export interface TransactionsPageDispatchProps { + refreshData: () => void; + resetSQLStats: () => void; +} + +export type TransactionsPageProps = TransactionsPageStateProps & + TransactionsPageDispatchProps & + RouteComponentProps; + export class TransactionsPage extends React.Component< - RouteComponentProps & TransactionsPageProps + TransactionsPageProps, + TState > { trxSearchParams = getSearchParams(this.props.history.location.search); filters = getFiltersFromQueryString(this.props.history.location.search); diff --git a/pkg/ui/cluster-ui/src/transactionsPage/transactionsPageConnected.tsx b/pkg/ui/cluster-ui/src/transactionsPage/transactionsPageConnected.tsx new file mode 100644 index 000000000000..41b61fe8e4d4 --- /dev/null +++ b/pkg/ui/cluster-ui/src/transactionsPage/transactionsPageConnected.tsx @@ -0,0 +1,45 @@ +// 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. + +import { connect } from "react-redux"; +import { RouteComponentProps, withRouter } from "react-router-dom"; +import { Dispatch } from "redux"; + +import { AppState } from "src/store"; +import { actions as transactionsActions } from "src/store/transactions"; +import { actions as resetSQLStatsActions } from "src/store/sqlStats"; +import { TransactionsPage } from "./transactionsPage"; +import { + TransactionsPageStateProps, + TransactionsPageDispatchProps, +} from "./transactionsPage"; +import { + selectTransactionsData, + selectTransactionsLastError, +} from "./transactionsPage.selectors"; +import { nodeRegionsByIDSelector } from "../store/nodes"; + +export const TransactionsPageConnected = withRouter( + connect< + TransactionsPageStateProps, + TransactionsPageDispatchProps, + RouteComponentProps + >( + (state: AppState) => ({ + data: selectTransactionsData(state), + nodeRegions: nodeRegionsByIDSelector(state), + error: selectTransactionsLastError(state), + }), + (dispatch: Dispatch) => ({ + refreshData: () => dispatch(transactionsActions.refresh()), + resetSQLStats: () => dispatch(resetSQLStatsActions.request()), + }), + )(TransactionsPage), +);